"""Framework for getting filetype-specific metadata.
Instantiate appropriate class with filename. Returned object acts like a
dictionary, with key-value pairs for each piece of metadata.
import fileinfo
info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3")
print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()])
Or use listDirectory function to get info on all files in a directory.
for info in fileinfo.listDirectory("/music/ap/", [".mp3"]):
...
Framework can be extended by adding classes for particular file types, e.g.
HTMLFileInfo, MPGFileInfo, DOCFileInfo. Each class is completely responsible for
parsing its files appropriately; see MP3FileInfo for example.
This program is part of "Dive Into Python", a free Python book for
experienced programmers. Visit http://diveintopython.org/ for the
latest version.
"""
__author__ = "Mark Pilgrim (mark@diveintopython.org)"
__version__ = "$Revision: 1.3 $"
__date__ = "$Date: 2004/05/05 21:57:19 $"
__copyright__ = "Copyright (c) 2001 Mark Pilgrim"
__license__ = "Python"
import os
import sys
from UserDict import UserDict #导入类
def stripnulls(data): #该函数用string作为参数,返回一个字符串(没有首尾空格和\00)
"strip whitespace and nulls"
return data.replace("\00", " ").strip()
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self) #实现父类的初始化
self["name"] = filename #数据属性
class MP3FileInfo(FileInfo):
"store ID3v1.0 MP3 tags"
tagDataMap = {"title" : ( 3, 33, stripnulls), #tagDataMap 类属性
"artist" : ( 33, 63, stripnulls),
"album" : ( 63, 93, stripnulls),
"year" : ( 93, 97, stripnulls),
"comment" : ( 97, 126, stripnulls),
"genre" : (127, 128, ord)}
def __parse(self, filename): #__parse私有方法,在类外面不能调用
"parse ID3v1.0 tags from MP3 file"
self.clear()
try:
fsock = open(filename, "rb", 0)
try:
fsock.seek(-128, 2) #定位标示符在相对于文件末尾的128字节处
tagdata = fsock.read(128)
finally:
fsock.close()
if tagdata[:3] == ‘TAG‘:
for tag, (start, end, parseFunc) in self.tagDataMap.items():#多变量赋值
self[tag] = parseFunc(tagdata[start:end]) #字典赋值
except IOError:
pass
def __setitem__(self, key, item): #设置MP3FileInfo的数据属性,可
#以通过字典【key】进行赋值。
if key == "name" and item:
self.__parse(item) #设置name关键字
FileInfo.__setitem__(self, key, item) #设置其他关键字
def listDirectory(directory, fileExtList):
"get list of file info objects for files of particular extensions"
fileList = [os.path.normcase(f) for f in os.listdir(directory)] #获取指定目
#录下面的文件列表( 所有文件)
# normcase(path)
# 标准化一个路径名的大小写。在Unix上,返回未改变的路径;在不区分大小写的文件系统上,
#它转换路径为小写字母。在Windows上 ,它也转换正斜杠为反斜杠。
fileList = [os.path.join(directory, f) for f in fileList \ #os.path.join(str1,str2),
#返回str1/str2用斜杠连接
if os.path.splitext(f)[1] in fileExtList] #splitext方法切分文件名和扩展名,
#[1]是扩展名(此时fileList是mp3的文件列表)
def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):
#文件名和fileinfo模块对象的引用
"get file info class from filename extension"
subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:] #返回 MP3FileInfo
return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
return [getFileInfoClass(f)(f) for f in fileList]
#getFileInfoClass(f)返回一个类,
#getFileInfoClass(f)(f)我们创建这个类 (不管它是什么) 的一个实例,
#传入文件名 (又是 f) 给 __init__ 方法
#返回的是结果实例的列表
if __name__ == "__main__":
for info in listDirectory("/music/_singles/", [".mp3"]):
#listDirectory 循环指定目录,返回实例列表,这个列表是所有歌曲元数据的集合
print "\n".join(["%s=%s" % (k, v) for k, v in info.items()])
print
#文件最后128字节 样例:
# ‘TAGKAIRO****THE BEST GOA ***DJ MARY-JANE***
# Rave Mix 2000http://mp3.com/DJMARYJANE \037‘
#运行脚本效果:
#[root@localhost ~]# python fileinfo.py
#album=My Happy Ending
#comment=
#name=/music/Avril - My Happy Ending.mp3
#title=我的快乐结局-www.ciwong.com
#artist=
#year=
#genre=12
#album=
#comment=
#name=/music/Angela Ammons - Always Getting Over You.mp3
#title=
#artist=
#year=
#genre=148