【Python】 获取MP3信息replica
replica
初衷是想要整理iphone中的音樂。IOS(我自己的手機還是IOS8.3,新版本的系統可能有變化了)自帶的音樂軟件中所有音樂文件都存放在/var/mobile/Media/iTunes_Control里面。不過很令人抓狂的是首先這個目錄被分隔成了從F00-Fxx的多個子目錄,我的手機上總共到F49,mp3文件都放在這些子目錄中。其次,mp3文件名全部都被點竄了,是看起來毫無規律的隨機四位大寫字母。每隔一段時間我都想從手機中把音樂備份出來然后放到電腦上,但是不知道文件名的話維護起來很麻煩。所以需要一個小程序來根據mp3的信息改變文件名。
程序的目標非常簡單,就是首先提取MP3文件中的信息,主要提取其歌名和演唱者兩個字段,然后把這個文件重命名成 “歌名 - 演唱者.mp3”這種格式。
■ 關于mp3信息的提取方法
mp3除了音頻信息的部分外,在文件的某些地方還會存放有關于這個音頻的一些基本信息比如作者,創作時間,專輯名,曲序號,專輯圖片等等。這些信息被統稱為ID3標簽。ID3標簽被分成兩代,第一代ID3v1只存儲一些很簡單的信息,占用文件末尾的128個字節(下面的直接讀文件的就是默認是ID3v1的情況)。而ID3v2位于文件的開頭,并且包含很全的信息比如加上一張專輯插圖。
網上搜到最多的使用ID3或者類似的一些模塊進行提取,也有很多人根據mp3的文件結構特征直接對文件進行讀取操作然后過濾出信息。因為我手上的資源很雜而且試了一下第二種方法發現很多文件提取信息都不全或者錯誤,所以還是用了第一種方法。下面這個是從網上摘來的,某種比較簡潔的第二種方法示例:這個函數讀取一個文件,然后從文件內容的特定位置解析出tag信息并返回一個字典。
def getID3(filename):fp = open(filename, 'r')fp.seek(-128, 2)fp.read(3) # TAG inizialetitle = fp.read(30)artist = fp.read(30)album = fp.read(30)anno = fp.read(4)comment = fp.read(28)fp.close()return {'title':title, 'artist':artist, 'album':album, 'anno':anno}和網上很多人用ID3或者eyed3之類的模塊不同,我用了個相對比較冷門的replica,他一共就只有三個模塊文件cli.py , tagger.py , cloner.py。我們用到的主要是tagger。基本用法如下:
from replica import taggertags = tagger.get_tags("PATH.mp3") #獲取一個mp3文件的ID3標簽信息 tagger.set_tags(tags,"ANOTHER.mp3")get_tags方法得到的是一個mutagen的MP3對象(replica是基于mutagen的,mutagen是更基本一些的音頻處理模塊)。這個對象所屬的類應該實現了__getattr__方法,所以你可以像一個字典一樣去訪問這個對象中的一些鍵值。而如果打印這個這個對象看到的就是一個字典:
for k,v in tags.items():if k == u"APIC:": #跳過U'APIC:'這個鍵是因為這個鍵的值是專輯圖片,如果用字符來表示的話太大了這里顯示不下continueprint k,vprint repr(k),repr(v) ###打印結果###
TDRC 2011
TIT2 ゆりゆららららゆるゆり大事件
ゆりゆららららゆるゆり 大事件
TRCK 1/4
TPE1 七森中☆ごらく部
TALB ゆりゆららららゆるゆり大事件
TSRC JPPC01101395
TCON Anime
TXXX:DISCID 28036204
###repr結果### 'TDRC' TDRC(encoding=<Encoding.LATIN1: 0>, text=[u'2011'])
'TIT2' TIT2(encoding=<Encoding.UTF16: 1>, text=[u'\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\u5927\u4e8b\u4ef6'])
u'USLT::eng' USLT(encoding=<Encoding.UTF16: 1>, lang='eng', desc=u'', text=u'\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6\r\u3066\u3093\u3066\u3053\u307e\u3044\u306e\u4eca\u65e5\u660e\u65e5\u5927\u7206\u767a\r\u305d\u3093\u3067\u8eab\u9577\u4f38\u3073\u306a\u3044\u3084\u3042\u3042\u3069\u3046\u3057\u3088\r\u7518\u3044\u3082\u306e\u98df\u3079\u3059\u304d\u30c6\u30fc\u30de\u30d0\u30fc\u30af\r\u307b\u3044\u3058\u3083\u96a0\u305b\u3088\u4e59\u5973\u3067\u3069\u3046\u3058\u3083\u308d\r\u90e8\u6d3b\u52d5\u672c\u756a\r\u3057\u3081\u3057\u3081\u7121\u9045\u523b\r\u672c\u696d\u5b78\u696d\u306a\u306b\u305d\u308c\r\u305d\u3093\u306a\u306e\u305c\u3093\u305c\u3093\u305c\u3093\u305c\u3093\u305c\u3093\u305c\u3093\u98df\u3079\u308c\u306a\u3044\r\u685c\u54b2\u304d \uff08\u685c\u54b2\u304d\uff09\r\u685c\u6563\u308a \uff08\u685c\u6563\u308a\uff09\r\u660e\u65e5\u3082\u3044\u3044\u65e5\u3068\u6b4c\u3046\u3088\r\u541b\u304c\u597d\u304d \uff08\u541b\u304c\u597d\u304d\uff09\r\u541b\u304c\u3044\u3044 \uff08\u541b\u304c\u3044\u3044\uff09\r\u660e\u65e5\u3082\u3044\u305f\u3044\u3068\u601d\u3046\u3088\r\u6700\u7d42\u624b\u6bb5\u3067\u5c40\u7720\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6(\u3060\u3044\u3058\u3051\u3093)\r\u3044\u308d\u306f\u306b\u307b\u3078\u3068\u3067\u304a\u307f\u304f\u3058Get you\uff01\u3042\u308a\u3089\u3073\u3085\u30fc\r\u306b\u3083\u3093\uff01\u306b\u3083\u3093\uff01\u8001\u306b\u3083\u3093\uff01\u82e5\u306b\u3083\u3093\uff01\u7537\u306b\u3083\uff01\u5973\u3093\uff01\r\u5fc5\u6b7b\u306b\u5bc6\u66f8\u3092\u767a\u5c04\u3067\u5fc5\u4e2d\u6388\u696d\u4e2d\r\u3067\u3082\u306d\u3042\u3089\u3089\u3089\u30c1\u30e7\u30fc\u30af\u304c\u30df\u30b5\u30a4\u30eb\r\u864e\u7a74\u306b\u5165\u3089\u306a\u3044\r\u96e8\u306a\u3089\u30cf\u30ec\u30eb\u30e4\r\u30ab\u30e9\u30aa\u30b1\u5272\u9ad8\u306a\u306b\u305d\u308c\r\u305d\u3093\u306a\u306e\u305c\u3063\u305f\u3044\u305c\u3063\u305f\u3044\u305c\u3063\u305f\u3044\u98df()\u3079\u308c\u306a\u3044\uff01\uff01\r\u685c\u54b2\u304d \uff08\u685c\u54b2\u304d\uff09\r\u685c\u6563\u308a \uff08\u685c\u6563\u308a\uff09\r\u660e\u65e5\u3082\u3044\u3044\u65e5\u3068\u6b4c\u3046\u3088\r\u541b\u304c\u597d\u304d \uff08\u541b\u304c\u597d\u304d\uff09\r\u541b\u304c\u3044\u3044 \uff08\u541b\u304c\u3044\u3044\uff09\r\u660e\u65e5\u3082\u3044\u305f\u3044\u3068\u601d\u3046\u3088\r\u8fd1\u6240\u306e\u30ef\u30f3\u30b3\u3068\u683c\u95d8\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6\r\u30c1\u30e3\u30a4\u30e0\u304c\u76ee\u899a\u307e\u3057\rKIRA\u2606KIRA\u2606TSUN-DELE\r\u751f\u30af\u30ea\u30fc\u30e0\u60d1\u661f \u306a\u306b\u305d\u308c\r\u305d\u308c\u306a\u3089\u307b\u3093\u3068\u306b\u307b\u3093\u3068\u306b\u307b\u3093\u3068\u306b\u3084\u3081\u308c\u306a\u3044\uff01\u3084\u3081\u308c\u306a\u3044\uff01\uff01\r\u685c\u54b2\u304d \uff08\u685c\u54b2\u304d\uff09\r\u685c\u6563\u308a \uff08\u685c\u6563\u308a\uff09\r\u660e\u65e5\u3082\u3044\u3044\u65e5\u3068\u6b4c\u3046\u3088\r\u541b\u304c\u597d\u304d \uff08\u541b\u304c\u597d\u304d\uff09\r\u541b\u304c\u3044\u3044 \uff08\u541b\u304c\u3044\u3044\uff09\r\u660e\u65e5\u3082\u3044\u305f\u3044\u3068\u601d\u3046\u3088\r\u6700\u7d42\u624b\u6bb5(\u3067\u5c40\u7720\u308a\r\u8fd1\u6240\u306e\u30ef\u30f3\u30b3\u3068\u683c\u95d8\r\u9769\u547d\u8d77\u3053\u3057\u3066\u5352\u696d\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\r\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a \u5927\u4e8b\u4ef6\r')
'TRCK' TRCK(encoding=<Encoding.LATIN1: 0>, text=[u'1/4'])
'TPE1' TPE1(encoding=<Encoding.UTF16: 1>, text=[u'\u4e03\u68ee\u4e2d\u2606\u3054\u3089\u304f\u90e8'])
'TALB' TALB(encoding=<Encoding.UTF16: 1>, text=[u'\u3086\u308a\u3086\u3089\u3089\u3089\u3089\u3086\u308b\u3086\u308a\u5927\u4e8b\u4ef6'])
'TSRC' TSRC(encoding=<Encoding.UTF16: 1>, text=[u'JPPC01101395'])
'TCON' TCON(encoding=<Encoding.UTF16: 1>, text=[u'Anime'])
u'TXXX:DISCID' TXXX(encoding=<Encoding.UTF16: 1>, desc=u'DISCID', text=[u'28036204'])
字典本身打印出來是這樣:
{u'APIC:':'關于圖片信息的內容','TSRC': TSRC(encoding=<Encoding.UTF16: 1>, text=[u'JPPC01101395']), 'TCON': TCON(encoding=<Encoding.UTF16: 1>, text=[u'Anime']), u'TXXX:DISCID': TXXX(encoding=<Encoding.UTF16: 1>, desc=u'DISCID', text=[u'28036204']),xxxx還有一些,意思一下。。}結合各個鍵的值大概就可以猜出來這個鍵是什么意思了。對于沒有設置某個標簽信息的文件而言,它的tags對象中就不會有相關的鍵。值其實是一些對象,比如標題標簽的鍵是TIT2,值就是一個TIT2對象,其有兩個關鍵的屬性,分別是TIT2.encoding和TIT2.text分別指出了顯示標題時用的編碼格式和標題文本組成的單個元素的列表。
這些對象的全圖鑒可以參見mutagen/id3/_frames.py的源碼。只是關于如何改變或創建這些對象,改變一個既有的MP3的標簽信息這一方面還有待研究(其實是mutagen的內容了)
其實就論replica這個模塊的用法的話就是以上了,然而我在之后的編寫過程中又遇到了各種各樣的坑,比如編碼問題,windows系統對于文件名的要求等等,所以打算繼續寫下去。
?
■ 關于文本信息的編碼
在tag信息的對象中,大多承載文字信息的對象都有encoding和text兩個屬性,且text屬性中是一個單unicode元素的列表。其實這兩個屬性聯合起來就表示了這個unicode應該用哪種編碼進行encode才能成為原本的信息。
首先來看encoding這屬性。這個屬性其實是維護了一個mutagen中的Encoding對象,這個對象可以在源碼中看到(位于mutagen/id3/_specs.py),把四種編碼分別用了一個數字表示,在知道了對應關系之后我們可以在自己的腳本里添加一個同樣的字典使得使用更加方便:
ENCODING = {0: "latin1", 1: "utf16", 2: "utf16be", 3: "utf8"}?
因為從相關對象中取到的屬性本質就是一個數,比如TIT2.Encoding其實就是1.
在四種編碼格式中,比較特殊的是latin1這種格式。latin1就是ISO-8859-1,之前在en/decode以及print探索那篇文章中也提到過,被latin1 decode出來的unicode只能被latin1 encode成str,并且encode得到的東西的編碼格式和最原先的是一樣的。
?
■ windows中文件名的規則
windows中的文件名不能含有字符 \/:*?"<>| 中的任意一個。如果含有這些錯誤的話在運用os.rename或者其他類似的重命名手段的時候會報錯WindowsError123。另一方面,如果同目錄下已經有相同名字文件存在時重命名會報錯WindowsError183。
一般而言,對windows上的文件進行命名的時候我們可以直接用unicode類型的字符串(如果愿意,也可以用gb這個系列的編碼格式的字符串進行命名,不過能用unicode的情況下應該盡量用通用性更加好的unicode)。但是就這個程序而言,我們碰到了很多由latin1編碼格式得到的unicode,很遺憾在處理如果用這些unicode直接去命名文件,會出現亂碼。目前我能想到的解決辦法是在進行命名之前進行一個判斷,如果這個文本信息的編碼格式是latin1的,那么在將其輸出成文件名的一部分之前先把它encode("latin1"),如果不是,那么可以直接把它作為文件名的一部分去rename。
■ 關于其他一些小改善
至此整個小程序基本功能已經實現了,接下來就是一些業務邏輯的改善了。比如tag信息不全時怎么辦,重命名失敗時怎么辦,增加處理進度提示,對于目錄中非mp3文件的處理等等。最后整個腳本如下:
?
#!/usr/bin/env python # coding=utf8 import os import sys import logging import re from replica import taggerreload(sys) sys.setdefaultencoding("gb18030")ENCODING = {0: "latin1", 1: "utf16", 2: "utf16be", 3: "utf8"}logging.basicConfig(filename="mp3.log", level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s ")def modify_textual(string):return re.sub(u'[\\\/:\*\?"<>\|]', " ", string)def process_one_song(songName):mp3 = tagger.get_tags(u"{songname}".format(songname=songName))try:artist = mp3.get("TPE1").text[0]enco_form = ENCODING.get(mp3.get("TPE1").encoding)if enco_form == "latin1":artist = artist.encode("latin1")except AttributeError as e:artist = u"未知藝術家"try:name = mp3.get("TIT2").text[0]enco_form = ENCODING.get(mp3.get("TIT2").encoding)if enco_form == "latin1":name = name.encode("latin1")except AttributeError as e:name = u"未知曲名"name = modify_textual(name)artist = modify_textual(artist)logging.info(u"{songname}".format(songname=songName) +u"{filename}.mp3".format(filename=os.path.dirname(songName) + os.sep.decode("utf8") + name + u"-" + artist))try:os.rename(u"{songname}".format(songname=songName), u"{filename}.mp3".format(filename=os.path.dirname(songName) + os.sep.decode("utf8") + name + u"-" + artist))except WindowsError as windowserror:logging.error("error processing {name} for windows error [{error}]".format(name=songName, error=str(windowserror)))def main():try:rootpath = sys.argv[1]except IndexError as e:rootpath = 'mp3'if not os.path.isdir(rootpath):print u"輸入目錄不存在"sys.exit(1)count = 0total = 0for root, dir, files in os.walk(rootpath):total += len(files)for root, dir, files in os.walk(rootpath):for file in files:filename = os.path.join(root.decode("gb18030"), file.decode("gb18030"))if os.path.splitext(file)[1] != '.mp3':os.remove(filename)continuecount += 1print u"正在處理{percent:.0f}%的歌".format(percent=float(count) / total * 100)process_one_song(filename)?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的【Python】 获取MP3信息replica的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python第一弹--------初步了
- 下一篇: 常见的数据加密方式