from collections import MutableMapping import mutagenx import mutagenx.id3 from mutagenx.easyid3 import EasyID3 def popms(id3): for k, v in id3.items(): 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): if 'TXXX:RATING' in id3: rating = id3['TXXX:RATING'] return list(rating.text) else: try: _, popm = next(popms(id3)) except StopIteration: return [] else: return [str(byte2rating(popm.rating))] def _canconv(r): try: ir = int(r) if ir != str(ir): return False return ir >= 1 and ir <= 5 except (ValueError, TypeError): return False def rating_set(id3, key, val): rating_delete(id3, key) if _canconv(val): popm = mutagenx.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(mutagenx.id3.TXXX(encoding=3, desc='RATING', text=str(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): def __init__(self, path): self.md = mutagenx.File(path, easy=True) self.path = path self.seen = False def __getitem__(self, key): if self.md == None: print(self.path) d = self.md[key] try: return d[0] except IndexError: raise KeyError(key) def __setitem__(self, key, value): #if type(value) != str: #raise ValueError if type(value) is type(None): 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__()]) def save(self): return self.md.save()