很久以来,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显示乱码的问题。

发表评论

电子邮件地址不会被公开。 必填项已用*标注