""" tagger.py [options] tracks*

tagger.py iterates over the tracks files supplied, locates the mp3
files they refer to, and sets their mp3 tags to reflect the artist,
album, track number, etc.  

    -v         verbose
    --dry-run  don't actually do anything
"""

# Sample Tracks File:
#
# ver 1.0
# artist Jimi Hendrix
# title Axis: Bold As Love
# copyright 1968
# label MCA
# 
# track 1 EXP
# track 2 Up From The Skies
# track 3 Spanish Castle Magic
# track 4 Wait Until Tomorrow
# track 5 Ain't No Telling
# track 6 Little Wing
# track 7 If 6 Was 9
# track 8 You Got Me Floatin'
# track 9 Castles Made Of Sand
# track 10 She's So Fine
# track 11 One Rainy Wish
# track 12 Little Miss Lover
# track 13 Bold As Love
# 

import sys, getopt, re, os, os.path
import ID3

class TrackFile:
    def __init__ (self, path):
        self.dir = os.path.dirname (path)
        
        # These will be filled in by parse().  They are included
        # here for documentation purposes.
        self.artist = ""
        self.album = ""
        self.year = ""
        self.label = ""
        self.tracks = [] # list of tuples (number, title, filename, disc)

        self.parse (open (path))
        pass

    def parse(self, file):
        lines = file.read().split ('\n')
        curdir = self.dir
        disc = None
        for line in lines:

            # skip empty lines
            if not line: continue

            # parse each line by splitting it into words.  Could have use a
            # regular expression or something but this is easier, though
            # we lose whitespace and must reconstruct it (whatever)
            words = line.split ()
            if words[0] == "ver":
                pass
            elif words[0] == "artist":
                self.artist = " ".join (words[1:])
            elif words[0] == "title":
                self.album = " ".join (words[1:])
            elif words[0] == "copyright":
                self.year = " ".join (words[1:])
            elif words[0] == "disc":
                disc = words[1]
            elif words[0] == "label":
                self.label = " ".join (words[1:])
            elif words[0] == "dir":
                curdir = os.path.join (self.dir, words[1])
            elif words[0] == "track":
                tracknum = int(words[1])
                trackname = " ".join (words[2:])
                self.tracks.append ( (tracknum,
                                      trackname,
                                      "%s/t%02d.mp3" % (curdir, tracknum),
                                      disc) )
            else:
                print >> sys.stderr, "Warning: unknown tracks tag %s!"%words[0]
                pass

            pass
        return

    def __iter__ (self):

        """ Returns a list of tuples (filename, dictionary).  The
        filename points to an mp3 file and the dictionary has id3 data
        about it.  See _construct_tags_for_track() for more info about
        the dictionary. """

        list = []
        for track in self.tracks:
            list.append (self._construct_tags_for_track (track))
            pass
        return iter (list)

    def _construct_tags_for_track (self, trackdata):

        """ Returns a dictionary where the keys are the mp3 tagging
        information and the value is the, well, value.  The argument
        should be the (tracknumber,trackname,filename) tuple from self.tracks.

        Most of the values are strings, but TRACKNUMBER is an int.

        Examples:
        
        { 'ARTIST':'Jimi Hendrix',
          'ALBUM':'Axis - Bold As Love',
          'YEAR':'1968',
          'TRACKNUMBER':1, # NOTE: not a string
          'TITLE':'EXP' } """

        result = {}

        # If a disc was specified, append it to the album name
        # (be wary that the name doens't grow over 30 chars!)
        if trackdata[3]:
            disc = " / %s" % trackdata[3]
            album = "%.*s%s" % (30 - len(disc), self.album, disc)
        else:
            album = self.album
            pass
        
        result ['ARTIST'] = self.artist
        result ['ALBUM'] = album
        result ['YEAR'] = self.year
        result ['TRACKNUMBER'] = trackdata[0]
        result ['TITLE'] = trackdata[1]

        return (trackdata[2], result)

    pass

def usage (msg):
    print >> sys.stderr, __doc__
    print >> sys.stderr, msg
    sys.exit (1)
    pass

def main (arglist):

    try:
        opts,args = getopt.getopt (arglist[1:], "hv", ["dry-run"])

        verbose = 0
        dryrun = 0
        
        for opt, optarg in opts:
            if opt == "-v": verbose = 1
            elif opt == "--dry-run": dryrun = 1
            elif opt == "-h": usage ("")
            pass
        pass
    except getopt.GetoptError, e:
        usage (str (e))
        return
    
    for trackpath in args:
        # open the tracks file and parse its fields
        trackobj = TrackFile (trackpath)

        for trackfilename,track in trackobj:
            # in verbose mode, output the information we gleaned about
            # the track
            if verbose:
                print >> sys.stderr, "%s:" % trackfilename
                for tag, val in track.items():
                    print >> sys.stderr, "  %s: %s" % (tag, val)
                    pass
                pass

            # Set the ID3 tags if appropriate
            if not dryrun:
                # load the mp3 file
                id3info = ID3.ID3 (trackfilename)
                
                # set the id3 fields
                for tag, val in track.items():
                    id3info[tag] = val
                    pass
                
                # and delete reference to the object to write the
                # changes back out
                del id3info
                pass

            pass

        pass
    return

if __name__ == "__main__":
    main (sys.argv)
    pass
