從
毛豆工作室下載下來的「開卷八分鐘」的mp3們,沒有 idv3 的 title,沒有lyrics,把它們放入iTunes再下載到 iPhone的話,這樣不方便。
毛豆工作室有xml 可以下載,先用 xml.dom.minidom 分析取出 fileName + title + description ,之後用
eyeD3 (python 模組) 將mp3 加tag。
總共有一千多個mp3,很難確保第一次就寫程式就OK,保險點是第一步驟的結果先暫存在 txt 檔,之後再讀 txt檔用 eyeD3。或是先對一兩個mp3試驗,之後再擴展之。再者,從xml中抽取出的description裡面有時夾雜不想要的廣告文字或html控制碼,先暫存輸出到txt檔,各別修改之。這樣子做至少比在程式一開始寫時就全盤考慮來得簡單、有效率的多了。現在的text editor的 find/replace都很強大,有regular expression功能,做起來不比用python程式遜色。
這個xml 結構是有兩百多個item,每個item描述每個mp3,每個mp3有 title、description、link等 nodes。
dom1 = xml.dom.minidom.parse('8minsreading_2010.xml')
items = dom1.getElementsByTagName('item')
for item in items:
title = item.getElementsByTagName('title')[0].\
firstChild.nodeValue
link = item.getElementsByTagName('link')[0].\
firstChild.nodeValue
fileName = link.split('/')[-1].split('.')[0]
description = item.getElementsByTagName('description')\
[0].firstChild.nodeValue
Unicode 簡体中文的問題:第一個問題是python在print到 stdio的時候,簡中會變成亂碼(文字化け),就算用了
sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
偶爾某些印出還是亂碼,所以不要太在意console output,還是先印出到暫存的 utf-8 text 檔。
第二個問題是 iTunes 用的IDv3 是
eyeD3.ID3_V2_3 ,是UCS-2 little endian,也就是前綴要加上
tag.encoding='\x01'
不保險的話,要對tag的每個frame.encoding都設好
for frame in tag.frames: frame.encoding='\x01'
為了要讓下載到iPhone的mp3,在聆聽時點一下螢幕會出現歌詞。這個mp3必須要加一個tag 叫「UNSYNCED LYRICS」。從foobar2000上看到tag名是 UNSYNCED LYRICS,但其實對eyeD3只要一個簡單的addLyrics即可:
tag.addLyrics(description)
以下python code全文,是邊做邊改邊試的,只是為了一次性工作而做的。你可以取出copy paste對你有幫助的code片段,比如 xml 的讀取,eyeD3 tag的寫入。
import os, sys, codecs, glob, re
import xml.dom.minidom
from xml.dom.minidom import parse
import eyeD3
sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
def read_xml():
xmlFileName = '8minsreading_2010.xml'
dom1 = parse(xmlFileName)
txtFileName = xmlFileName.split('.')[0] + '.txt'
fout = codecs.open(txtFileName, mode='w', \
encoding='utf-8')
items = dom1.getElementsByTagName('item')
for item in items:
title = item.getElementsByTagName('title')[0].\
firstChild.nodeValue
link = item.getElementsByTagName('link')[0].\
firstChild.nodeValue
fileName = link.split('/')[-1].split('.')[0]
description = item.getElementsByTagName(\
'description')[0].firstChild.nodeValue
description = description.split(' ')[0]
description = description.strip()
fout.write(fileName + '\t' + title + '\t' + \
description + '\n')
fout.close()
def sum_all():
pat = re.compile(r'\d\d\d\d\d\d\t')
table = {}
txtFiles = ['8minsreading_2011.txt',
'8minsreading_2010.txt',
'8minsreading_2009.txt',
'8minsreading_2008.txt',
'8minsreading_2007.txt']
for txtFile in txtFiles:
fin = codecs.open(txtFile, mode='r',
encoding='utf-8')
for line in fin:
line = line.strip()
if not line:
continue
if pat.match(line):
comps = line.split('\t')
fileTitle = comps[0]
title = comps[1]
description = ''.join(comps[2:])
table[fileTitle] = (fileTitle, title,
description)
else:
(fileTitle, title, description) = \
table[fileTitle]
description += line
table[fileTitle] = (fileTitle, title,
description)
fin.close()
fout = codecs.open('all.txt', mode='w',
encoding='utf-8')
keys = table.keys()
keys.sort()
for k in keys:
fileTitle, title, description = table[k]
fout.write( fileTitle+'\t'+ title +'\t'+
description +'\n' )
fout.close()
table = {}
fin = codecs.open('all.txt', mode='r', encoding='utf-8')
for line in fin:
comps = line.split('\t')
fileTitle, title = comps[0], comps[1]
description = ''.join(comps[2:])
table[fileTitle] = (title, description)
fin.close()
mp3Files = glob.glob('*.mp3')
for mp3File in mp3Files:
fileTitle = mp3File.split('.')[0]
title, description = table[fileTitle]
tag = eyeD3.Tag()
tag.link(mp3File)
tag.setVersion(eyeD3.ID3_V2_3)
tag.encoding='\x01'
tag.setTitle(fileTitle+title )
tag.setAlbum(u'開卷八分鐘')
tag.setArtist(u'梁文道')
tag.addLyrics(description)
for frame in tag.frames: frame.encoding='\x01'
tag.update()