最近 itunes 升级,于是重新在整理音乐文件,看到那些没有专辑封面的 mp3,于是骨子里的不折腾不舒服斯基又发作了,手贱准备再次开始写一个自动工具来搞,上次的折腾记录在这里,根据 ID3 自动补充专辑封面和歌词
上次的失败是因为那个库对于中文的编码支持不是很好,于是最后就出现一些乱码问题,等等,于是这次准备换 python 搞,搜了一下,看到这几篇文章,http://hackerszone.diandian.co…,http://www.gracecode.com/posts…,http://www.fuchaoqun.com/2010/…,http://pipitu.org/2010/05/14/m…,于是我知道手贱的不止我一个,呵呵
他们用的 py 库是http://eyed3.nicfit.net,这次不搞图形界面,于是一通查资料,重新理清了 mp3 tag 的文件格式,按照我的理解,简单来说,是这样的
首先有一个不太成熟的 id3v1,这个东西的做法是把 title,album,artist 这些信息放到文件末尾,格式简单,编码是 ascii,所以不支持中文,而且有一个很大的问题是定长,30 个 char,于是后来演变出另外一种, id3v2,而这个 id3v2 还分为 id3v2.3,这个是目前最多的,也是兼容性最好的,他把这些信息放到文件头部,而且不定长,而且字段不定,所以灵活性很高,可扩展性很强,字符的编码可以是 ascii,或者 utf-8,但是 windows 对于 utf-8 的支持不好,所以如果是用 utf-8 的话,在资源管理器里面是看不出来的,所以 qq 音乐,千千静听这些,默认都是 id3v2.3 的 iso-8859-1,说是 iso-8859-1,其实本质上是 gbk,而 itunes 对于 gbk 和 utf-8 的解码都是没有问题的,所以最皆大欢喜的做法就是用 id3v2.3 的 iso-8859-1(gbk) 来编码,然后嵌入图形文件当作封面,这个 id3v2 还有一个 id3v2.4,这个比较新,所以不怎么广泛,他的优势是标签信息可以在文件头,也可以在文件尾,在文件头的劣势显而易见,每次改写都要重写整个文件,磁盘读写量很大,除了 id3v2,还有一种格式叫做 apev2,这个也比较先进,可惜的是普及率也不高。
方针路线定了,接下来就是实干了,照着前面几篇博客的代码,敲敲打打的得到下面这个代码
#!/usr/bin/env python # -*- coding:utf-8 -*- # 2012.12.3 '''根据豆瓣自动补全专辑封面''' import os, sys, re, time, urllib, eyeD3 import logging logging.basicConfig(filename = 'log%s.txt' % time.time(), filemode = 'w', format = '[%(levelname)s] [%(asctime)s] %(message)s', level = logging.DEBUG) def fillCover(filename): logging.info('processing %s' % filename) t = eyeD3.Tag() try: t.link(filename) except: logging.error('can not open file') return False images = t.getImages() if len(images) > 0: logging.info('alreay has cover images') return True title = t.getTitle() album = t.getAlbum() artist = t.getArtist() logging.debug('title is %s' % title) logging.debug('album is %s' % album) logging.debug('artist is %s' % artist) if title and artist: keyword = ' '.join([title, artist]) keyword = keyword.encode('utf-8') else: logging.debug('no id3 tags') keyword = filename.split('\')[-1].split('.')[0] keyword = keyword.encode('gbk') logging.debug('keyword is %s' % keyword) doubanSearchAPI = 'http://api.douban.com/v2/music/search?q={0}' request = doubanSearchAPI.format(urllib.quote(keyword)) logging.debug('request is %s' % request) result = urllib.urlopen(request).read() logging.debug('result is %s' % result) # 豆瓣 API 限制是每分钟 10 次请求 # http://developers.douban.com/wiki/?title=api_v2 #logging.warn('sleep for 6 sec') #time.sleep(6) if not result: return False doubanCoverPattern = 's(d+).jpg' doubanCoverURL = 'http://img3.douban.com/lpic/s{0}.jpg' match = re.search(doubanCoverPattern, result, re.IGNORECASE) if match: coverFileURL = doubanCoverURL.format(match.groups()[0]) logging.debug('cover image url is %s' % coverFileURL) else: logging.debug('no cover image url matched') return False try: logging.debug('downloading cover image file') coverFileName = ' - '.join([artist, album])+'.jpg' f = file(coverFileName, 'wb') f.write(urllib.urlopen(coverFileURL).read()) f.close() logging.debug('download finished') except: logging.error('download error') try: logging.debug('adding image') t.addImage(3, coverFileName, u'') t.update() logging.info('successfully add image') return True except: logging.error('add image error') return False def main(): if len(sys.argv) < 2: print 'usage: %s /path/to/your/music/folder/' % __file__ return False for i in os.listdir(sys.argv[1]): fillCover(os.path.join(sys.argv[1], i)) if __name__ == '__main__': main()
这份代码在英文歌曲下,基本没有问题,可以比较顺利的跑,但是,中文,又是恶心的编码问题,由于现有的歌曲中,id3v2.3 的编码方式有些是 gbk ,有些是 utf-8,这个就比较麻烦了,不过千千静听有一个批量编辑文件属性的功能,可以在那里面,把所有文件的编码方式统一,可惜的是,这个 py 库到目前我发现他只支持 utf-8,对于 gbk 会乱码,于是我现在对于中文歌曲的处理方法是,全部拖进千千静听,转成 id3v2.3 的 utf-8,然后用这份代码去跑,跑完了,事实上图片已经钱嵌进去了,但是在 windows 的资源管理器中看不到,在 itunes 中可以,在 airplay 中也可以,但是如果这个时候再用千千静听把所有的编码都转换回 gbk,就会丢失了专辑封面,这个就很麻烦,目前就卡在这里
接下来的话,最理想的情况当然是研究清楚这个 py 库,然后让他可以处理 gbk 的标签,然后用千千静听把所有的标签转成 gbk,再用这份代码去下载封面,这样就你好我好大家好了
Pingback: Python eyeD3 库的乱码问题初步 | ZRJ