2
# Pretend to be /usr/bin/id3v2 from id3lib, sort of.
3
# Copyright 2005 Joe Wreschnig
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.
9
# $Id: mid3v2 2962 2006-03-13 07:24:05Z piman $
15
from optparse import OptionParser, SUPPRESS_HELP
19
class ID3OptionParser(OptionParser):
21
mutagen_version = ".".join(map(str, mutagen.version))
22
my_version = ".".join(map(str, VERSION))
23
version = "mid3v2 %s\nUses Mutagen %s" % (my_version, mutagen_version)
24
return OptionParser.__init__(
25
self, version=version,
26
usage="%prog [OPTION] [FILE]...",
27
description="Mutagen-based replacement for id3lib's id3v2.")
29
def format_help(self, *args, **kwargs):
30
text = OptionParser.format_help(self, *args, **kwargs)
32
You can set the value for any ID3v2 frame by using '--' and then a frame ID.
34
mid3v2 --TIT3 "Monkey!" file.mp3
35
would set the "Subtitle/Description" frame to "Monkey!".
37
Any editing operation will cause the ID3 tag to be upgraded to ID3v2.4.
40
def list_frames(option, opt, value, parser):
41
items = mutagen.id3.Frames.items()
43
for name, frame in items:
44
print " --%s %s" % (name, frame.__doc__.split("\n")[0])
47
def list_frames_2_2(option, opt, value, parser):
48
items = mutagen.id3.Frames_2_2.items()
50
for name, frame in items:
51
print " --%s %s" % (name, frame.__doc__.split("\n")[0])
54
def list_genres(option, opt, value, parser):
55
for i, genre in enumerate(mutagen.id3.TCON.GENRES):
56
print "%3d: %s" % (i, genre)
59
def delete_tags(filenames, v1, v2):
60
for filename in filenames:
61
print "deleting ID3 tag info in %s" % filename
62
mutagen.id3.delete(filename, v1, v2)
64
def write_files(edits, filenames):
65
enc = locale.getpreferredencoding()
66
edits = [(frame[2:], value.decode(enc)) for (frame, value) in edits]
68
for filename in filenames:
69
print "Writing", filename
70
try: id3 = mutagen.id3.ID3(filename)
71
except mutagen.id3.ID3NoHeaderError:
72
print "No ID3 header found; creating a new tag"
73
id3 = mutagen.id3.ID3()
74
except Exception, err:
77
for frame, value in edits:
79
values = value.split(":")
81
value, desc, lang = values[0], "", "eng"
82
elif len(values) == 2:
83
desc, value, lang = values[0], values[1], "eng"
85
value = ":".join(values[1:-1])
86
desc, lang = values[0], values[-1]
87
frame = mutagen.id3.COMM(
88
encoding=3, text=value, lang=lang, desc=desc)
90
frame = mutagen.id3.Frames[frame](encoding=3, text=value)
91
id3.loaded_frame(frame)
94
def list_tags(filenames):
95
enc = locale.getpreferredencoding()
96
for filename in filenames:
97
print "IDv2 tag info for %s:" % filename
98
try: id3 = mutagen.id3.ID3(filename, translate=False)
99
except Exception, err: print str(err)
100
else: print id3.pprint().encode(enc, "replace")
102
def list_tags_raw(filenames):
103
enc = locale.getpreferredencoding()
104
for filename in filenames:
105
print "Raw IDv2 tag info for %s:" % filename
106
try: id3 = mutagen.id3.ID3(filename, translate=False)
107
except Exception, err: print str(err)
109
for frame in id3.values(): print repr(frame)
112
parser = ID3OptionParser()
114
"-f", "--list-frames", action="callback", callback=list_frames,
115
help="Display all possible frames for ID3v2.3 / ID3v2.4")
117
"--list-frames-v2.2", action="callback", callback=list_frames_2_2,
118
help="Display all possible frames for ID3v2.2")
120
"-L", "--list-genres", action="callback", callback=list_genres,
121
help="Lists all ID3v1 genres")
123
"-l", "--list", action="store_const", dest="action", const="list",
124
help="Lists the tag(s) on the file(s)")
126
"--list-raw", action="store_const", dest="action", const="list-raw",
127
help="Lists the tag(s) on the file(s) in Python format")
129
"-d", "--delete-v2", action="store_const", dest="action",
130
const="delete-v2", help="Deletes ID3v2 tags")
132
"-s", "--delete-v1", action="store_const", dest="action",
133
const="delete-v1", help="Deletes ID3v1 tags")
135
"-D", "--delete-all", action="store_const", dest="action",
136
const="delete-v1-v2", help="Deletes ID3v1 and ID3v2 tags")
138
"-C", "--convert", action="store_const", dest="action",
140
help="Convert tags to ID3v2.4 (any editing will do this)")
143
"-a", "--artist", metavar='"ARTIST"', action="callback",
144
help="Set the artist information", type="string",
145
callback=lambda *args: args[3].edits.append(("--TPE1", args[2])))
147
"-A", "--album", metavar='"ALBUM"', action="callback",
148
help="Set the album title information", type="string",
149
callback=lambda *args: args[3].edits.append(("--TALB", args[2])))
151
"-t", "--song", metavar='"SONG"', action="callback",
152
help="Set the song title information", type="string",
153
callback=lambda *args: args[3].edits.append(("--TIT2", args[2])))
155
"-c", "--comment", metavar='"DESCRIPTION":"COMMENT":"LANGUAGE"',
156
action="callback", help="Set the comment information", type="string",
157
callback=lambda *args: args[3].edits.append(("--COMM", args[2])))
159
"-g", "--genre", metavar='"GENRE"', action="callback",
160
help="Set the genre or genre number", type="string",
161
callback=lambda *args: args[3].edits.append(("--TCON", args[2])))
163
"-y", "--year", "--date", metavar='YYYY[-MM-DD]', action="callback",
164
help="Set the year/date", type="string",
165
callback=lambda *args: args[3].edits.append(("--TDRC", args[2])))
167
"-T", "--track", metavar='"num/num"', action="callback",
168
help="Set the track number/(optional) total tracks", type="string",
169
callback=lambda *args: args[3].edits.append(("--TRCK", args[2])))
171
for frame in mutagen.id3.Frames:
172
if issubclass(mutagen.id3.Frames[frame], mutagen.id3.TextFrame):
174
"--" + frame, action="callback", help=SUPPRESS_HELP,
175
type='string', metavar="value", # optparse blows up with this
176
callback=lambda *args: args[3].edits.append(args[1:3]))
179
(options, args) = parser.parse_args(argv[1:])
182
if parser.edits: write_files(parser.edits, args)
183
elif options.action in [None, 'list']: list_tags(args)
184
elif options.action == "list-raw": list_tags_raw(args)
185
elif options.action == "convert": write_files([], args)
186
elif options.action.startswith("delete"):
187
delete_tags(args, "v1" in options.action, "v2" in options.action)
188
else: parser.print_help()
189
else: parser.print_help()
191
if __name__ == "__main__":
192
try: import mutagen, mutagen.id3
194
# Run as ./mid3v2 out of tools/
195
sys.path.append(os.path.abspath("../"))
196
import mutagen, mutagen.id3