-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathmp3tagiconv.py
executable file
·255 lines (212 loc) · 7.45 KB
/
mp3tagiconv.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/env python
# Reencode the ID3v1 and ID3v2 tag of a mp3 file.
# Copyright 2010 CHEN, Xing ([email protected])
#
# 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:
TCON = id3["TCON"]
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)