#!/usr/bin/env python
# parseid3.py -- Use binary read to parse the ID3v1 header of an MP3 file.

def fetch_ID3tag(mp3):
    "Fetch the ID3 tag of filename mp3 as tuple, or None."
    try:
        f = open(mp3, 'rb')
        f.seek(-128, 2)
        buf = f.read(3+30+30+30+4+30+1) # read so many bytes
        f.close()
    except IOError:
        return None     # Can't fetch ID3 tag
    
    return parse_ID3tag(buf)

def parse_ID3tag(buf):
    "Parse an ID3 tag stored in buf and return a dictionary or None."
    if not buf.startswith('TAG'):
        return None     # Not an ID3 tag!
    
    id3 = {}
    id3['title']   = remove_padding(buf[3:33])    # 30 chars for title
    id3['artist']  = remove_padding(buf[33:63])   # 30 chars for artist
    id3['album']   = remove_padding(buf[63:93])   # 30 chars for album
    id3['year']    = remove_padding(buf[93:97])   #  4 chars for year
    
    raw_comment    = buf[97:127]                  # 30 chars for comment+track
    
    if ord(raw_comment[-2]) == 0 and ord(raw_comment[-1]) != 0:
        id3['track'] = ord(raw_comment[-1])
        id3['comment'] = remove_padding(raw_comment[:-2])
    else:
        id3['track'] = None
        id3['comment'] = remove_padding(raw_comment)
    
    id3['genre']   = ord(buf[127])                #  1 byte  for genre
    
    return id3

def remove_padding(inp):
    "Remove padding chars whitespace and NULL from string inp"
    out = inp.strip(' \x00')
    return out

if __name__ == '__main__':
    import sys, pprint
    if len(sys.argv) < 2:
        print "Usage:", sys.argv[0], "[file.mp3 ...]"
        sys.exit(0)
    
    for fname in sys.argv[1:]:
        print "ID3(%s) == " % fname
        pprint.pprint(fetch_ID3tag(fname))
