''' The Triple-M (aka Mix Master Marv) python abstraction library for PyTagger License GPL http://www.gnu.org/licenses/gpl.txt author: triplem@syntechnix.net This is a simple little library to simplify access to the ID3v2 objects in the PyTagger library from http://www.liquidx.net/pytagger/. Objects are simplified as dictionaries with single string values (and one that is a float). This may damage some tags that have more than one value entry, so don't use it if that's a problem for you. To get a list of the dictionary keys, go here: http://www.id3.org/develop.html ...and look in any of the "informal standards" of ID3v2 ID3v1 tags in Pytagger are really simple, and this makes them more complicated, but by viewing v1 tags through the same lens as v2 tag, calling both is simpler because they appear the same. Included functions intended for use: readid3(filename) Read both ID3 v1 and v2 tags and return a dictionary. Give preference to v2 entries when values exist there readid3v1(filename) Read the ID3v1 tags and return a dictionary readid3v2(filename) Read the ID3v2 tags and return a dictionary writeid3(filename, tags, version): Write both ID3 v1 and v2 tags. "version" is specific to the v2 tag. writeid3v1(filename, tags) Write ID3v1 tag writeid3v2(filename, tags, version) Write ID3v2 tag You should already have PyTagger compiled and installed. This library does not provide it for you. ''' import string, tagger # Read any ID3 tag and return it in a dictionary... reading all versions. # Give preference to ID3v2 tags. (i.e. If both a v1 and v2 tag values exist, # return the value in the v2 tag.) def readid3(filename): tags1 = readid3v1(filename) tags2 = readid3v2(filename) for index in tags1: try: if tags2[index] == '': tags2[index] = tags1[index] except: tags2[index] = tags1[index] return tags2 # Write ID3 v1 and v2 tags from the provided dictionary # Wipe out any existing ID3 tags def writeid3(filename, tags, version = 2.3): writeid3v1(filename, tags) writeid3v2(filename, tags, version) # Read any ID3v1 tag and return it in a dictionary # Abstract the v1 tag into v2 form (i.e. v2 tag name values) def readid3v1(filename): tagdict = {} tag = tagger.ID3v1(filename) if tag.songname != '': tagdict['TIT2'] = tag.songname if tag.artist != '': tagdict['TPE1'] = tag.artist if tag.album != '': tagdict['TALB'] = tag.album if tag.year != '': tagdict['TYER'] = tag.year if tag.comment != '': tagdict['COMM'] = tag.comment if tag.genre != '': tagdict['TCON'] = str(tag.genre) if tag.track != '': tagdict['TRCK'] = str(tag.track) del tag tagdict['version'] = 1.1 return tagdict # Write an ID3v1 tag from the provide dictionary # Wipe out any existing ID3v1 tag def writeid3v1(filename, tags): tag = tagger.ID3v1(filename) try: tag.songname = tags['TIT2'][:30] except: tag.songname = '' try: tag.artist = tags['TPE1'][:30] except: tag.artist = '' try: tag.album = tags['TALB'][:30] except: tag.album = '' try: tag.year = tags['TCOP'][:4] except: tag.year = '' try: tag.comment = tags['COMM'][:28] except: tag.comment = '' # Significant error in pytagger (at least on my system) # Until fixed, don't write Genre or Track # File "/site-packages/tagger/id3v1.py", line 159, in __setattr__ # if name == 'track' and type(value) != types.IntValue: # NameError: global name 'types' is not defined # Kill the genre tag because it's obsolete #tag.genre = "None" #try: # tag.track = int(tags['TRCK']) #except: # tag.track = int(0) tag.commit() del tag # Read all the ID tag elements into a dictionary # Delete out entries that have no data in them # Don't return lists, only return the first item as a string def readid3v2(filename): tempdict = {} tag = tagger.ID3v2(filename) tempdict['version'] = tag.version for frame in tag.frames: tempdict[frame.fid] = frame.strings # Weird thing... finding a null at the end of the strings if tempdict[frame.fid][-1:] == '\x00': tempdict[frame.fid] = tempdict[frame.fid][:-1] del tag complete = False while complete == False: for index in tempdict: try: if len(tempdict[index]) == 0: del tempdict[index] #print "deleting entry from zero len" break except: pass if iswhat(tempdict[index]) == 'list': try: #print "trimming list out of dictionary " + index tempdict[index] = str(tempdict[index][0]) break except: del tempdict[index] #print "error trimming list " + index break else: complete = True return tempdict # Write an ID3v2 tag from the provided dictionary # Wipe out any existing ID3v2 tag def writeid3v2(filename, tags, version = 2.3): try: version = tags['version'] except: pass del tags['version'] tag = tagger.ID3v2(filename, version) # Remove the existing tag for frame in tag.frames: tag.frames.remove(frame) # Add the new tag for index in tags: newframe = tag.new_frame(index) newframe.set_text(tags[index]) tag.frames.append(newframe) del newframe tag.commit() del tag return # Internal function to distinguish data types import inspect def iswhat(object): for (dtype, val) in inspect.getmembers(object): if dtype[:9] != "__class__": continue val = str(val) garbage, val, garbage2 = string.split(val, "'", 2) return val return 'unknown'