解决MP3播放时显示乱码的问题
很久以来,Mac电脑上用iTunes打开NAS上的音乐,一直都是显示乱码。
但是其实在NAS上或者在Windows上查看是正确的。
最后定位问题是MP3的ID3信息编码格式引起的。估计是那部分歌曲比较老,还是gbk或者gb2312编码。只需要把每个文件的ID3的文字编码转换就行。
网上的解决方法,竟然是删掉ID3信息……好无语。
然后开始搜”MP3 tag converter”,一开始搜到的是MP3 Tag Encoding Converter Mac版。
iTunes的下载地址:https://itunes.apple.com/cn/app/mp3-tag-encoding-converter/id420122242?mt=12
但是已经下架了。
无奈继续寻找。
之后,在sourceforge找到一个ID3 tag converter
地址是https://sourceforge.net/projects/tagconv/
下载回来之后,直接make,发现报错
make: taglib-config: Command not found g++ -O2 -g tagconv.cpp -o tagconv tagconv.cpp:9:20: error: taglib.h: No such file or directory tagconv.cpp:10:29: error: taglib/mpegfile.h: No such file or directory tagconv.cpp:11:28: error: taglib/tstring.h: No such file or directory tagconv.cpp:12:24: error: taglib/tag.h: No such file or directory tagconv.cpp:13:29: error: taglib/id3v1tag.h: No such file or directory tagconv.cpp:14:29: error: taglib/id3v2tag.h: No such file or directory tagconv.cpp:15:32: error: taglib/id3v2header.h: No such file or directory
只能再次放弃了……
然后找到一份python脚本,又开始折腾。
#!/usr/bin/env python # Reencode the ID3v1 and ID3v2 tag of a mp3 file. # Copyright 2010 CHEN, Xing (cxcxcxcx@gmail.com) # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # import os import sys import locale from optparse import OptionParser import mutagen.id3 VERSION = (0, 1) def isascii(string): return not string or min(string) < '\x127' def MakeID3v1(id3, enc): """Return an ID3v1.1 tag string from a dict of ID3v2.4 frames.""" """Copied from id3.py of mutagen. What I modified is the encoding of ID3v1, so that the tag can be parsed correctly by Windows Media Player, etc.""" v1 = {} for v2id, name in {"TIT2": "title", "TPE1": "artist", "TALB": "album"}.items(): if v2id in id3: text = id3[v2id].text[0].encode(enc, 'replace')[:30] else: text = "" v1[name] = text + ("\x00" * (30 - len(text))) if "COMM" in id3: cmnt = id3["COMM"].text[0].encode(enc, 'replace')[:28] else: cmnt = "" v1["comment"] = cmnt + ("\x00" * (29 - len(cmnt))) if "TRCK" in id3: try: v1["track"] = chr(+id3["TRCK"]) except ValueError: v1["track"] = "\x00" else: v1["track"] = "\x00" if "TCON" in id3: try: genre = id3["TCON"].genres[0] except IndexError: pass else: if genre in TCON.GENRES: v1["genre"] = chr(TCON.GENRES.index(genre)) if "genre" not in v1: v1["genre"] = "\xff" if "TDRC" in id3: v1["year"] = str(id3["TDRC"])[:4] elif "TYER" in id3: v1["year"] = str(id3["TYER"])[:4] else: v1["year"] = "\x00\x00\x00\x00" return ("TAG%(title)s%(artist)s%(album)s%(year)s%(comment)s" "%(track)s%(genre)s") % v1 def has_id3v1(filename): f = open(filename, 'rb+') try: f.seek(-128, 2) except IOError: pass else: return (f.read(3) == "TAG") def get_id3v1(filename): id3v1 = None f = open(filename, 'rb+') try: f.seek(-128, 2) except IOError: pass else: id3v1 = mutagen.id3.ParseID3v1(f.read(128)) return id3v1 def convTxt(encList, s): decF = s for i in encList: try: decF = s.encode('latin1').decode(i) tagEnc = i break except: pass #print tagEnc return decF def saveTagToFile(filename, fileID3, v1enc): """ Update the file.""" fileID3.save(v1=1) try: hasID3v1 = has_id3v1(filename) try: f = open(filename, 'rb+') except IOError, err: from errno import ENOENT if err.errno != ENOENT: raise f = open(filename, 'ab') # create, then reopen f = open(filename, 'rb+') v1 = 2 if hasID3v1: f.seek(-128, 2) if v1 > 0: f.write(MakeID3v1(fileID3, v1enc)) else: f.truncate() elif v1 == 2: f.seek(0, 2) f.write(MakeID3v1(fileID3, v1enc)) finally: f.close() def main(argv): from mutagen import File default_enclist = "utf8,gbk" mutagen_version = ".".join(map(str, mutagen.version)) my_version = ".".join(map(str, VERSION)) version = "mp3tagiconv %s\nUses Mutagen %s" % ( my_version, mutagen_version) parser = OptionParser(usage="%prog [OPTION] [FILE]...", version=version, description=("Mutagen-based mp3 tag encoding converter, which " "can automatically detect the encoding " "of the tags of a MP3 file, and can convert it so " "that the tags can be recognized correctly in most " "applications.")) parser.add_option( "-e", "--encoding-list", metavar="ENCODING_LIST", action="store", type="string", dest="enclist", help=("Specify original tag encoding, comma seperated if you want " "me to guess one by one(default: %s)" % default_enclist)) parser.add_option( "--v1-encoding", metavar="ID3V1_ENCODING", action="store", type="string", dest="v1enc", help=("Specify tag encoding for ID3v1, the default value(gbk) should **ALWAYS** be OK for Simplified Chinese tags.")) parser.add_option( "--do-update", action="store_true", dest="notest", help="Save updates WITHOUT confirming. It is NOT recommended.") #parser.add_option( #"--confirm-test", action="store_true", dest="notest", #help="Actually modify files. This is NOT default in order to protect your mp3 file from being damaged by wrong parameters.") (options, args) = parser.parse_args(argv[1:]) if not args: raise SystemExit(parser.print_help() or 1) enclist = options.enclist or default_enclist enclist = enclist.split(',') notest = options.notest v1enc = options.v1enc or "gbk" enc = locale.getpreferredencoding() for filename in args: print "--", filename try: # Prepare information from ID3v1 id3v1 = get_id3v1(filename) fileID3 = mutagen.id3.ID3(filename) if id3v1 is not None: # Merge ID3v1 and ID3v2 for i in id3v1.keys(): if i not in fileID3.keys(): fileID3.add(id3v1[i]) #print f.pprint() for tag in filter(lambda t: t.startswith("T"), fileID3): frame = fileID3[tag] if isinstance(frame, mutagen.id3.TimeStampTextFrame): # non-unicode fields continue try: text = frame.text except AttributeError: continue try: #print frame.encoding if frame.encoding == 0: text = map(convTxt, [enclist,]*len(frame.text), frame.text) except (UnicodeError, LookupError): continue else: frame.text = text #for i in text: #print i.encode('utf8') if not text or min(map(isascii, text)): frame.encoding = 3 else: frame.encoding = 1 print frame.pprint().encode(enc) doSave = False if notest: doSave = True else: #print fileID3.pprint().encode(enc) print "Do you want to save?(y/N)", p=raw_input() doSave = p=="y" or p=="Y" if doSave: print "*** Saving..." saveTagToFile(filename, fileID3, v1enc) else: print print "*** File is NOT updated." except AttributeError: print "- Unknown file type" except KeyboardInterrupt: raise except Exception, err: print str(err) print if __name__ == "__main__": try: import mutagen except ImportError: sys.path.append(os.path.abspath("../")) import mutagen main(sys.argv)
直接执行会报错,缺少mutagen。
Traceback (most recent call last): File "mp3.py", line 25, in <module> import mutagen.id3 ImportError: No module named mutagen.id3
一开始用Python3的pip安装mutagen
pip install mutagen
安装有些慢,还总断……
ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.
然后用国内的源才顺利安装完毕
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mutagen
结果,运行py文件之后,报错……
File "mp3.py", line 163 except IOError, err: ^ SyntaxError: invalid syntax
原来这是一个python2的脚本啊,不能用python3。
于是只能用pip2来安装mutagen。
pip2 install -i https://pypi.tuna.tsinghua.edu.cn/simple mutagen
2.7的版本太老了,ssh支持不太好。会报错……
SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/security.html#snimissingwarning. SNIMissingWarning InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/security.html#insecureplatformwarning. InsecurePlatformWarning InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/security.html#insecureplatformwarning. InsecurePlatformWarning
没办法只能手动下载安装了。
https://pypi.org/project/mutagen/
上的最新版本1.44.0已经不支持python2了
Traceback (most recent call last): File "<string>", line 20, in <module> File "/private/var/folders/nh/clxwl_h93v5bcdd7jqnqxgy80000gn/T/pip-build-f5y6uI/mutagen/setup.py", line 246, in <module> raise Exception("Python 2 no longer supported") Exception: Python 2 no longer supported
所以我选的是1.36.2的版本。如果是pip安装1.36.2的话。
pip2 install -i https://pypi.tuna.tsinghua.edu.cn/simple "mutagen==1.36.2"
可惜我的python版本太低了,只能手动下载之后解压和安装。
tar -zxvf mutagen-1.36.2.tar.gz cd mutagen-1.36.2 python setup.py build python setup.py install
安装完毕后,顺利的执行了
Usage: mp3.py [OPTION] [FILE]... Mutagen-based mp3 tag encoding converter, which can automatically detect the encoding of the tags of a MP3 file, and can convert it so that the tags can be recognized correctly in most applications. Options: --version show program's version number and exit -h, --help show this help message and exit -e ENCODING_LIST, --encoding-list=ENCODING_LIST Specify original tag encoding, comma seperated if you want me to guess one by one(default: utf8,gbk) --v1-encoding=ID3V1_ENCODING Specify tag encoding for ID3v1, the default value(gbk) should **ALWAYS** be OK for Simplified Chinese tags. --do-update Save updates WITHOUT confirming. It is NOT recommended.
因为是要换成utf8,左右需要这么写
python mp3.py --v1-encoding=utf8 "文件名.mp3" TIT2=歌名 TRCK=5 TPE1=歌手 TALB=专辑 TCON=OtherDo you want to save?(y/N)
如果不需要确认就是这么写
python mp3.py --do-update --v1-encoding=utf8 "文件名.mp3"
如果是需要把某个目录下的mp3文件全部修改成utf8是这么写
find 专辑 -name *.mp3 | awk '{print "python mp3.py --do-update --v1-encoding=utf8 \""$0"\"";$1=$1;}' | sh
最后完美的解决了Mac OS X下iTunes播放mp3显示乱码的问题。