home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / bin / mid3v2 < prev    next >
Encoding:
Text File  |  2010-06-11  |  9.7 KB  |  262 lines

  1. #!/usr/bin/python
  2. # Pretend to be /usr/bin/id3v2 from id3lib, sort of.
  3. # Copyright 2005 Joe Wreschnig
  4. #
  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.
  8.  
  9. import os
  10. import sys
  11. import locale
  12.  
  13. from optparse import OptionParser, SUPPRESS_HELP
  14.  
  15. try:
  16.     import mutagen, mutagen.id3
  17. except ImportError:
  18.     # Run as ./mid3v2 out of tools/
  19.     sys.path.append(os.path.abspath("../"))
  20.     import mutagen, mutagen.id3
  21.  
  22. VERSION = (1, 3)
  23.  
  24. global verbose
  25. verbose = True
  26.  
  27. class ID3OptionParser(OptionParser):
  28.     def __init__(self):
  29.         mutagen_version = ".".join(map(str, mutagen.version))
  30.         my_version = ".".join(map(str, VERSION))
  31.         version = "mid3v2 %s\nUses Mutagen %s" % (my_version, mutagen_version)
  32.         self.edits = []
  33.         OptionParser.__init__(
  34.             self, version=version,
  35.             usage="%prog [OPTION] [FILE]...",
  36.             description="Mutagen-based replacement for id3lib's id3v2.")
  37.  
  38.     def format_help(self, *args, **kwargs):
  39.         text = OptionParser.format_help(self, *args, **kwargs)
  40.         return text + """\
  41. You can set the value for any ID3v2 frame by using '--' and then a frame ID.
  42. For example:
  43.         mid3v2 --TIT3 "Monkey!" file.mp3
  44. would set the "Subtitle/Description" frame to "Monkey!".
  45.  
  46. Any editing operation will cause the ID3 tag to be upgraded to ID3v2.4.
  47. """
  48.  
  49. def list_frames(option, opt, value, parser):
  50.     items = mutagen.id3.Frames.items()
  51.     items.sort()
  52.     for name, frame in items:
  53.         print "    --%s    %s" % (name, frame.__doc__.split("\n")[0])
  54.     raise SystemExit
  55.  
  56. def list_frames_2_2(option, opt, value, parser):
  57.     items = mutagen.id3.Frames_2_2.items()
  58.     items.sort()
  59.     for name, frame in items:
  60.         print "    --%s    %s" % (name, frame.__doc__.split("\n")[0])
  61.     raise SystemExit
  62.  
  63. def list_genres(option, opt, value, parser):
  64.     for i, genre in enumerate(mutagen.id3.TCON.GENRES):
  65.         print "%3d: %s" % (i, genre)
  66.     raise SystemExit
  67.  
  68. def delete_tags(filenames, v1, v2):
  69.     for filename in filenames:
  70.         if verbose:
  71.             print "deleting ID3 tag info in %s" % filename
  72.         mutagen.id3.delete(filename, v1, v2)
  73.  
  74. def delete_frames(deletes, filenames):
  75.     frames = deletes.split(",")
  76.     for filename in filenames:
  77.         if verbose:
  78.             print "deleting %s from %s" % (deletes, filename)
  79.         try:
  80.             id3 = mutagen.id3.ID3(filename)
  81.         except mutagen.id3.ID3NoHeaderError:
  82.             if verbose:
  83.                 print "No ID3 header found; skipping."
  84.         except StandardError, err:
  85.             print str(err)
  86.         else:
  87.             map(id3.delall, frames)
  88.             id3.save()
  89.  
  90. def write_files(edits, filenames):
  91.     enc = locale.getpreferredencoding()
  92.     edits = [(frame[2:], value.decode(enc)) for (frame, value) in edits]
  93.  
  94.     # preprocess:
  95.     #   for all [frame,value] pairs in the edits list
  96.     #      gather values for identical frames into a list
  97.     tmp = {}
  98.     for frame, value in edits:
  99.         if tmp.has_key(frame):
  100.             tmp[frame].append(value)
  101.         else:
  102.             tmp[frame] = [value]
  103.     # edits is now a dictionary of frame -> [list of values]
  104.     edits = tmp
  105.  
  106.     for filename in filenames:
  107.         if verbose:
  108.             print "Writing", filename
  109.         try:
  110.             id3 = mutagen.id3.ID3(filename)
  111.         except mutagen.id3.ID3NoHeaderError:
  112.             if verbose:
  113.                 print "No ID3 header found; creating a new tag"
  114.             id3 = mutagen.id3.ID3()
  115.         except StandardError, err:
  116.             print str(err)
  117.             continue
  118.         for (frame, vlist) in edits.items():
  119.             if frame == "COMM":
  120.                 for value in vlist:
  121.                     values = value.split(":")
  122.                     if len(values) == 1:
  123.                         value, desc, lang = values[0], "", "eng"
  124.                     elif len(values) == 2:
  125.                         desc, value, lang = values[0], values[1], "eng"
  126.                     else:
  127.                         value = ":".join(values[1:-1])
  128.                         desc, lang = values[0], values[-1]
  129.                     frame = mutagen.id3.COMM(
  130.                         encoding=3, text=value, lang=lang, desc=desc)
  131.             elif issubclass(mutagen.id3.Frames[frame], mutagen.id3.UrlFrame):
  132.                 frame = mutagen.id3.Frames[frame](encoding=3, url=vlist)
  133.             else:
  134.                 frame = mutagen.id3.Frames[frame](encoding=3, text=vlist)
  135.             id3.add(frame)
  136.         id3.save(filename)
  137.  
  138. def list_tags(filenames):
  139.     enc = locale.getpreferredencoding()
  140.     for filename in filenames:
  141.         print "IDv2 tag info for %s:" % filename
  142.         try:
  143.             id3 = mutagen.id3.ID3(filename, translate=False)
  144.         except StandardError, err:
  145.             print str(err)
  146.         else:
  147.             print id3.pprint().encode(enc, "replace")
  148.  
  149. def list_tags_raw(filenames):
  150.     for filename in filenames:
  151.         print "Raw IDv2 tag info for %s:" % filename
  152.         try:
  153.             id3 = mutagen.id3.ID3(filename, translate=False)
  154.         except StandardError, err:
  155.             print str(err)
  156.         else:
  157.             for frame in id3.values():
  158.                 print repr(frame)
  159.  
  160. def main(argv):
  161.     parser = ID3OptionParser()
  162.     parser.add_option(
  163.         "-v", "--verbose", action="store_true", dest="verbose", default=False,
  164.         help="be verbose")
  165.     parser.add_option(
  166.         "-q", "--quiet", action="store_false", dest="verbose",
  167.         help="be quiet (the default)")
  168.     parser.add_option(
  169.         "-f", "--list-frames", action="callback", callback=list_frames,
  170.         help="Display all possible frames for ID3v2.3 / ID3v2.4")
  171.     parser.add_option(
  172.         "--list-frames-v2.2", action="callback", callback=list_frames_2_2,
  173.         help="Display all possible frames for ID3v2.2")
  174.     parser.add_option(
  175.         "-L", "--list-genres", action="callback", callback=list_genres,
  176.         help="Lists all ID3v1 genres")
  177.     parser.add_option(
  178.         "-l", "--list", action="store_const", dest="action", const="list",
  179.         help="Lists the tag(s) on the file(s)")
  180.     parser.add_option(
  181.         "--list-raw", action="store_const", dest="action", const="list-raw",
  182.         help="Lists the tag(s) on the file(s) in Python format")
  183.     parser.add_option(
  184.         "-d", "--delete-v2", action="store_const", dest="action",
  185.         const="delete-v2", help="Deletes ID3v2 tags")
  186.     parser.add_option(
  187.         "-s", "--delete-v1", action="store_const", dest="action",
  188.         const="delete-v1", help="Deletes ID3v1 tags")
  189.     parser.add_option(
  190.         "-D", "--delete-all", action="store_const", dest="action",
  191.         const="delete-v1-v2", help="Deletes ID3v1 and ID3v2 tags")
  192.     parser.add_option(
  193.         '--delete-frames', metavar='FID1,FID2,...', action='store',
  194.         dest='deletes', default='', help="Delete the given frames")
  195.     parser.add_option(
  196.         "-C", "--convert", action="store_const", dest="action",
  197.         const="convert",
  198.         help="Convert tags to ID3v2.4 (any editing will do this)")
  199.  
  200.     parser.add_option(
  201.         "-a", "--artist", metavar='"ARTIST"', action="callback",
  202.         help="Set the artist information", type="string",
  203.         callback=lambda *args: args[3].edits.append(("--TPE1", args[2])))
  204.     parser.add_option(
  205.         "-A", "--album", metavar='"ALBUM"', action="callback",
  206.         help="Set the album title information", type="string",
  207.         callback=lambda *args: args[3].edits.append(("--TALB", args[2])))
  208.     parser.add_option(
  209.         "-t", "--song", metavar='"SONG"', action="callback",
  210.         help="Set the song title information", type="string",
  211.         callback=lambda *args: args[3].edits.append(("--TIT2", args[2])))
  212.     parser.add_option(
  213.         "-c", "--comment", metavar='"DESCRIPTION":"COMMENT":"LANGUAGE"',
  214.         action="callback", help="Set the comment information", type="string",
  215.         callback=lambda *args: args[3].edits.append(("--COMM", args[2])))
  216.     parser.add_option(
  217.         "-g", "--genre", metavar='"GENRE"', action="callback",
  218.         help="Set the genre or genre number", type="string",
  219.         callback=lambda *args: args[3].edits.append(("--TCON", args[2])))
  220.     parser.add_option(
  221.         "-y", "--year", "--date", metavar='YYYY[-MM-DD]', action="callback",
  222.         help="Set the year/date", type="string",
  223.         callback=lambda *args: args[3].edits.append(("--TDRC", args[2])))
  224.     parser.add_option(
  225.         "-T", "--track", metavar='"num/num"', action="callback",
  226.         help="Set the track number/(optional) total tracks", type="string",
  227.         callback=lambda *args: args[3].edits.append(("--TRCK", args[2])))
  228.  
  229.     for frame in mutagen.id3.Frames:
  230.         if issubclass(mutagen.id3.Frames[frame], mutagen.id3.TextFrame) \
  231.             or issubclass(mutagen.id3.Frames[frame], mutagen.id3.UrlFrame):
  232.             parser.add_option(
  233.                 "--" + frame, action="callback", help=SUPPRESS_HELP,
  234.                 type='string', metavar="value", # optparse blows up with this
  235.                 callback=lambda *args: args[3].edits.append(args[1:3]))
  236.  
  237.     (options, args) = parser.parse_args(argv[1:])
  238.     global verbose
  239.     verbose = options.verbose
  240.  
  241.     if args:
  242.         if parser.edits or options.deletes:
  243.             if options.deletes:
  244.                 delete_frames(options.deletes, args)
  245.             if parser.edits:
  246.                 write_files(parser.edits, args)
  247.         elif options.action in [None, 'list']:
  248.             list_tags(args)
  249.         elif options.action == "list-raw":
  250.             list_tags_raw(args)
  251.         elif options.action == "convert":
  252.             write_files([], args)
  253.         elif options.action.startswith("delete"):
  254.             delete_tags(args, "v1" in options.action, "v2" in options.action)
  255.         else:
  256.             parser.print_help()
  257.     else:
  258.         parser.print_help()
  259.  
  260. if __name__ == "__main__":
  261.     main(sys.argv)
  262.