from collections import MutableMapping import mutagen import mutagen.id3 from mutagen.easyid3 import EasyID3 def popms(id3): for k, v in id3.iteritems(): if k.startswith('POPM'): yield k, v def byte2rating(b): if b >= 224: return 5 if b >= 160: return 4 if b >= 96: return 3 if b >= 32: return 2 if b >= 1: return 1 return 0 def rating2byte(r): if r == 5: return 256 if r == 4: return 192 if r == 3: return 128 if r == 2: return 64 if r == 1: return 1 return 0 def rating_get(id3, key): try: rating = id3['TXXX:RATING'] except KeyError: try: _, popm = popms(id3).next() except StopIteration: return [] else: return [unicode(byte2rating(popm.rating))] else: return list(rating.text) def _canconv(r): try: if int(r) != str(int(r)): return False return int(r) >= 1 and int(r) <= 5 except (ValueError, TypeError): return False def rating_set(id3, key, val): rating_delete(id3, key) if _canconv(val): popm = mutagen.id3.POPM() popm.email = "Windows Media Player 9 Series" popm.count = 0 popm.rating = rating2byte(int(val)) id3.add(popm) else: if 'TXXX:RATING' in id3: del(id3['TXXX:RATING']) id3.add(mutagen.id3.TXXX(encoding=3, desc='RATING', text=unicode(val))) def rating_delete(id3, key): for k, v in popms(id3): del(id3[k]) if 'TXXX:RATING' in id3: del(id3['TXXX:RATING']) replaygain_tags = ('replaygain_album_gain', 'replaygain_album_peak', \ 'replaygain_track_gain', 'replaygain_track_peak') for tag in replaygain_tags: EasyID3.RegisterTXXXKey(tag, tag) extra_tags = ('sync', 'totaltracks', 'totaldiscs') for tag in extra_tags: EasyID3.RegisterTXXXKey(tag, tag.upper()) EasyID3.RegisterTextKey('albumartist', 'TPE2') EasyID3.RegisterKey('rating', rating_get, rating_set, rating_delete) class SyncFile(MutableMapping): """Dumb OOP crap that just adds more code and a sense of self-importance""" def __init__(self, path): self.md = mutagen.File(path, easy=True) self.path = path self.seen = False def __getitem__(self, key): d = self.md[key] try: return d[0] except IndexError: raise KeyError(key) def __setitem__(self, key, value): if type(value) != unicode: raise ValueError self.md[key] = [value] def __delitem__(self, key): del(self.md[key]) def __iter__(self): for k in self.md: try: self.__getitem__(k) except KeyError: pass else: yield k def __len__(self): return len([k for k in self.__iter__()])