modified: makefile
[GalaxyCodeBases.git] / etc / mp3tagiconv
blob9e0e7502d4785fbc15bee23fca6123a8409f138f
1 #!/usr/bin/env python
2 # Reencode the ID3v1 and ID3v2 tag of a mp3 file.
3 # Copyright 2010 CHEN, Xing (cxcxcxcx@gmail.com)
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of version 2 of the GNU General Public License as
7 # published by the Free Software Foundation.
10 # https://code.google.com/p/mp3tagiconv/
11 # http://linux-wiki.cn/wiki/Mp3%E6%A0%87%E7%AD%BE%E4%B9%B1%E7%A0%81%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90%E4%B8%8E%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88
12 # http://blog.icehoney.me/posts/2013-10-06-Archlinux-ape-cue-splitting
14 import os
15 import sys
16 import locale
18 from optparse import OptionParser
19 import mutagen.id3
21 VERSION = (0, 1)
23 def isascii(string):
24 return not string or min(string) < '\x127'
26 def MakeID3v1(id3, enc):
27 """Return an ID3v1.1 tag string from a dict of ID3v2.4 frames."""
29 """Copied from id3.py of mutagen. What I modified is the encoding of ID3v1, so that the tag can be parsed
30 correctly by Windows Media Player, etc."""
32 v1 = {}
34 for v2id, name in {"TIT2": "title", "TPE1": "artist",
35 "TALB": "album"}.items():
36 if v2id in id3:
37 text = id3[v2id].text[0].encode(enc, 'replace')[:30]
38 else:
39 text = ""
40 v1[name] = text + ("\x00" * (30 - len(text)))
42 if "COMM" in id3:
43 cmnt = id3["COMM"].text[0].encode(enc, 'replace')[:28]
44 else: cmnt = ""
45 v1["comment"] = cmnt + ("\x00" * (29 - len(cmnt)))
47 if "TRCK" in id3:
48 try: v1["track"] = chr(+id3["TRCK"])
49 except ValueError: v1["track"] = "\x00"
50 else: v1["track"] = "\x00"
52 if "TCON" in id3:
53 try: genre = id3["TCON"].genres[0]
54 except IndexError: pass
55 else:
56 if genre in TCON.GENRES:
57 v1["genre"] = chr(TCON.GENRES.index(genre))
58 if "genre" not in v1: v1["genre"] = "\xff"
60 if "TDRC" in id3:
61 v1["year"] = str(id3["TDRC"])[:4]
62 elif "TYER" in id3:
63 v1["year"] = str(id3["TYER"])[:4]
64 else:
65 v1["year"] = "\x00\x00\x00\x00"
67 return ("TAG%(title)s%(artist)s%(album)s%(year)s%(comment)s"
68 "%(track)s%(genre)s") % v1
70 def has_id3v1(filename):
71 f = open(filename, 'rb+')
72 try: f.seek(-128, 2)
73 except IOError: pass
74 else: return (f.read(3) == "TAG")
76 def get_id3v1(filename):
77 id3v1 = None
78 f = open(filename, 'rb+')
79 try: f.seek(-128, 2)
80 except IOError: pass
81 else:
82 id3v1 = mutagen.id3.ParseID3v1(f.read(128))
83 return id3v1
85 def convTxt(encList, s):
86 decF = s
87 for i in encList:
88 try:
89 decF = s.encode('latin1').decode(i)
90 tagEnc = i
91 break
92 except:
93 pass
94 #print tagEnc
95 return decF
97 def saveTagToFile(filename, fileID3, v1enc):
98 """ Update the file."""
99 fileID3.save(v1=1)
101 try:
102 hasID3v1 = has_id3v1(filename)
103 try: f = open(filename, 'rb+')
104 except IOError, err:
105 from errno import ENOENT
106 if err.errno != ENOENT: raise
107 f = open(filename, 'ab') # create, then reopen
108 f = open(filename, 'rb+')
110 v1 = 2
111 if hasID3v1:
112 f.seek(-128, 2)
113 if v1 > 0: f.write(MakeID3v1(fileID3, v1enc))
114 else: f.truncate()
115 elif v1 == 2:
116 f.seek(0, 2)
117 f.write(MakeID3v1(fileID3, v1enc))
118 finally:
119 f.close()
121 def main(argv):
122 from mutagen import File
124 default_enclist = "utf8,gbk"
126 mutagen_version = ".".join(map(str, mutagen.version))
127 my_version = ".".join(map(str, VERSION))
128 version = "mp3tagiconv %s\nUses Mutagen %s" % (
129 my_version, mutagen_version)
131 parser = OptionParser(usage="%prog [OPTION] [FILE]...", version=version,
132 description=("Mutagen-based mp3 tag encoding converter, which "
133 "can automatically detect the encoding "
134 "of the tags of a MP3 file, and can convert it so "
135 "that the tags can be recognized correctly in most "
136 "applications."))
137 parser.add_option(
138 "-e", "--encoding-list", metavar="ENCODING_LIST", action="store",
139 type="string", dest="enclist",
140 help=("Specify original tag encoding, comma seperated if you want "
141 "me to guess one by one(default: %s)" % default_enclist))
142 parser.add_option(
143 "--v1-encoding", metavar="ID3V1_ENCODING", action="store",
144 type="string", dest="v1enc",
145 help=("Specify tag encoding for ID3v1, the default value(gbk) should **ALWAYS** be OK for Simplified Chinese tags."))
146 parser.add_option(
147 "--do-update", action="store_true", dest="notest",
148 help="Save updates WITHOUT confirming. It is NOT recommended.")
149 #parser.add_option(
150 #"--confirm-test", action="store_true", dest="notest",
151 #help="Actually modify files. This is NOT default in order to protect your mp3 file from being damaged by wrong parameters.")
153 (options, args) = parser.parse_args(argv[1:])
154 if not args:
155 raise SystemExit(parser.print_help() or 1)
157 enclist = options.enclist or default_enclist
158 enclist = enclist.split(',')
160 notest = options.notest
161 v1enc = options.v1enc or "gbk"
163 enc = locale.getpreferredencoding()
164 for filename in args:
165 print "--", filename
166 try:
167 # Prepare information from ID3v1
168 id3v1 = get_id3v1(filename)
170 fileID3 = mutagen.id3.ID3(filename)
171 if id3v1 is not None:
172 # Merge ID3v1 and ID3v2
173 for i in id3v1.keys():
174 if i not in fileID3.keys():
175 fileID3.add(id3v1[i])
177 #print f.pprint()
178 for tag in filter(lambda t: t.startswith("T"), fileID3):
179 frame = fileID3[tag]
180 if isinstance(frame, mutagen.id3.TimeStampTextFrame):
181 # non-unicode fields
182 continue
184 try:
185 text = frame.text
186 except AttributeError:
187 continue
189 try:
190 #print frame.encoding
191 if frame.encoding == 0:
192 text = map(convTxt, [enclist,]*len(frame.text), frame.text)
193 except (UnicodeError, LookupError):
194 continue
195 else:
196 frame.text = text
197 #for i in text:
198 #print i.encode('utf8')
199 if not text or min(map(isascii, text)):
200 frame.encoding = 3
201 else:
202 frame.encoding = 1
203 print frame.pprint().encode(enc)
205 doSave = False
206 if notest:
207 doSave = True
208 else:
209 #print fileID3.pprint().encode(enc)
210 print "Do you want to save?(y/N)",
211 p=raw_input()
212 doSave = p=="y" or p=="Y"
214 if doSave:
215 print "*** Saving..."
216 saveTagToFile(filename, fileID3, v1enc)
217 else:
218 print
219 print "*** File is NOT updated."
221 except AttributeError: print "- Unknown file type"
222 except KeyboardInterrupt: raise
223 except Exception, err: print str(err)
224 print
226 if __name__ == "__main__":
227 try: import mutagen
228 except ImportError:
229 sys.path.append(os.path.abspath("../"))
230 import mutagen
231 main(sys.argv)