def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() self.filetype = name self.__tags['__filetype'] = self.filetype self.__tags['__tag_read'] = u'VorbisComment' util.MockTag.__init__(self, filename)
def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() #Used as storage. #Each key as the is the field as used by puddletag, eg. 'artist' #Each value contains an mutagen.id3.Frame object #that have two methods, get_value and set_value. #get_value returns the value stored by the frame as #text/unicode list. #set_value should take text/unicode list in parse it into #it understands. #When saving the frame stored will be used. If it has a 'frames' #attributes, those frames will be used instead. util.MockTag.__init__(self, filename)
def __deepcopy__(self, memo=None): cls = Tag() tags = CaselessDict() frames = [] [ frames.append(frame) if not hasattr(frame, 'frames') else frames.extend(frame.frames) for key, frame in self.__tags.items() if not key.startswith('__') ] funcs = [] frames_copy = [] for frame in frames: funcs.append((getattr(frame, 'get_value', None), getattr(frame, 'set_value', None))) if hasattr(frame, 'get_value'): delattr(frame, 'get_value') if hasattr(frame, 'set_value'): delattr(frame, 'set_value') frames_copy.append(deepcopy(frame)) tags = handle( dict([(frame.HashKey, frame) for frame in frames_copy])) for frame, (get_value, set_value) in zip(frames, funcs): if get_value is not None: frame.get_value = get_value if set_value is not None: frame.set_value = set_value for key, value in self.__tags.items(): if key not in tags: tags[key] = deepcopy(value) cls.set_fundamentals(tags, self.mut_obj, deepcopy(self.images)) cls.filepath = self.filepath return cls
def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() #Used as storage. #Each key as the is the field as used by puddletag, eg. 'artist' #Each value contains an mutagen.id3.Frame object #that have two methods, get_value and set_value. #get_value returns the value stored by the frame as #text/unicode list. #set_value should take text/unicode list in parse it into #it understands. #When saving the frame stored will be used. If it has a 'frames' #attributes, those frames will be used instead. util.MockTag.__init__(self, filename)
def __init__(self, filename=None): self.__images = [] self.__errors = set() self.__tags = CaselessDict() util.MockTag.__init__(self, filename)
class Tag(util.MockTag): """Class for AAC tags.""" mapping = {} revmapping = {} IMAGETAGS = (util.MIMETYPE, util.DATA) def __init__(self, filename=None): self.__images = [] self.__errors = set() self.__tags = CaselessDict() util.MockTag.__init__(self, filename) def get_filepath(self): return util.MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(util.MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo=None): cls = Tag() cls.mapping = self.mapping cls.revmapping = self.revmapping cls.set_fundamentals(deepcopy(self.__tags), deepcopy(self.images), self.mut_obj, deepcopy(self.__freeform), deepcopy(self.__errors)) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del(self.__tags[key]) @getdeco def __getitem__(self, key): if key.startswith('__'): if key == '__image': return self.images elif key == '__total': return FUNCS['totaltracks'][0](self.__tags['totaltracks']) else: return self.__tags[key] try: return FUNCS[key][0](self.__tags[key]) except KeyError: return gettext(self.__tags[key]) @setdeco def __setitem__(self, key, value): if isinstance(key, (int, long)): self.__tags[key] = value return elif key.startswith('__'): if key == '__image': self.images = value if key in FILETAGS: setattr(self, fn_hash[key], value) elif key == '__total': self.__tags['totaltracks'] = FUNCS['totaltracks'][1](value) return elif isempty(value): if key in self: del(self[key]) return else: try: new_val = FUNCS[key][1](value) if value: self.__tags[key] = new_val except KeyError: #User defined tags. self.__freeform[key] = '----:com.apple.iTunes:%s' % key self.__tags[key] = settext(value) except ValueError: pass def delete(self): self.mut_obj.delete() for key in self.usertags: del(self.__tags[self.revmapping.get(key, key)]) self.images = [] def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) def _get_images(self): return self.__images images = property(_get_images, _set_images) def _info(self): info = self.mut_obj.info fileinfo = [('Path', self[PATH]), ('Size', str_filesize(int(self.size))), ('Filename', self[FILENAME]), ('Modified', self.modified)] mp4info = [('Bitrate', self.bitrate), ('Frequency', self.frequency), ('Channels', unicode(info.channels)), ('Length', self.length), ('Bits per sample', unicode(info.bits_per_sample))] return [('File', fileinfo), ('MP4 Info', mp4info)] info = property(_info) def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" tags, audio = self.load(filename, MP4) self.images = [] if audio is None: return revmap, mapping = self.revmapping, self.mapping self.revmapping, self.mapping = {}, {} self.__freeform = {} #Keys are tags as required by mutagen i.e. The '----' #frames. Values are the tag as represented by puddletag. if audio.tags: #Not empty keys = audio.keys() try: self.images = map(bin_to_pic, audio['covr']) keys.remove('covr') except KeyError: self.images = [] convert = lambda k, v: FUNCS[k][1](v) #I want 'trkn', to split into track and totaltracks, like Mp3tag. if 'trkn' in keys: tags['track'] = convert('track', [z[0] for z in audio['trkn']]) tags['totaltracks'] = convert('totaltracks', [z[1] for z in audio['trkn']]) keys.remove('trkn') #Same as above if 'disk' in keys: tags['disc'] = convert('disc', [z[0] for z in audio['disk']]) tags['totaldiscs'] = convert('totaldiscs', [z[1] for z in audio['disk']]) keys.remove('disk') for key in keys: if key in TAGS: tags[TAGS[key]] = convert(TAGS[key], audio[key]) else: field = key[key.find(':', key.find(':') +1) + 1:] try: field = field.decode('utf8') except UnicodeDecodeError: field = field.decode('latin1') self.__freeform[field] = key try: tags[field] = [unicode(v, 'utf8') if not isinstance(v, unicode) else v for v in audio[key]] except UnicodeDecodeError: self.__errors.add(field) for k,v in tags.items(): if not v: del(tags[k]) self.__tags.update(info_to_dict(audio.info)) self.__tags.update(tags) self.revmapping, self.mapping = revmap, mapping self.__tags['__tag_read'] = u'MP4' self.filetype = 'MP4' self.__tags['__filetype'] = self.filetype self.set_attrs(ATTRIBUTES, self.__tags) self.update_tag_list() self.mut_obj = audio @keys_deco def keys(self): return self.__tags.keys() def save(self): if self.mut_obj.tags is None: self.mut_obj.add_tags() if self.filepath != self.mut_obj.filename: self.mut_obj.filename = self.filepath audio = self.mut_obj newtag = {} tuples = (('track', ['trkn', 'totaltracks']), ('disc', ['disk', 'totaldiscs'])) tags = self.__tags for tag, values in tuples: if tag in tags: denom = tags[tag] if values[1] in tags: total = tags[values[1]] newtag[values[0]] = [(int(t), int(total)) for t, total in zip(denom, total)] else: newtag[values[0]] = [(int(z), 0) for z in denom] elif values[1] in tags: total = tags[values[1]] newtag[values[0]] = [(0, int(z)) for z in total] tags = usertags(self.__tags) tags = [(z, tags[z]) for z in tags if z not in ['track', 'totaltracks', 'disc', 'totaldiscs']] for tag, value in tags: try: newtag[REVTAGS[tag]] = value except KeyError: newtag[self.__freeform[tag].encode('utf8')] = encode(self.__tags[tag]) if self.images: newtag['covr'] = filter(None, map(pic_to_bin, self.images)) toremove = [z for z in audio.keys() if z not in newtag and z not in self.__errors] for key in toremove: del(audio[key]) audio.update(newtag) audio.save() def set_fundamentals(self, tags, images, mut_obj, freeform=None, errors=None): self.__freeform = {} if freeform is None else freeform self.__errors = {} if errors is None else errors self.__tags = tags self.mut_obj = mut_obj self.images = images def update_tag_list(self): l = tag_versions.tags_in_file(self.filepath) if l: self.__tags['__tag'] = u'MP4, ' + u', '.join(l) else: self.__tags['__tag'] = u'MP4'
class Tag(util.MockTag): IMAGETAGS = (util.MIMETYPE, util.DESCRIPTION, util.DATA, util.IMAGETYPE) mapping = {} revmapping = {} def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() self.filetype = name self.__tags['__filetype'] = self.filetype self.__tags['__tag_read'] = u'VorbisComment' util.MockTag.__init__(self, filename) def get_filepath(self): return util.MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(util.MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def _get_images(self): return self.__images def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) images = property(_get_images, _set_images) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo): cls = Tag() cls.mapping = self.mapping cls.revmapping = self.revmapping cls.set_fundamentals(deepcopy(self.__tags), self.mut_obj, self.images) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del(self.__tags[key]) @getdeco def __getitem__(self, key): if key == '__image': return self.images elif key == '__total': return get_total(self) return self.__tags[key] @setdeco def __setitem__(self, key, value): if key.startswith('__'): if key == '__image': self.images = value elif key == '__total': set_total(self, value) elif key in fn_hash: setattr(self, fn_hash[key], value) elif isempty(value): if key in self: del(self[key]) else: return else: if isinstance(value, (int, long)): self.__tags[key.lower()] = [unicode(value)] else: self.__tags[key.lower()] = unicode_list(value) def delete(self): self.mut_obj.delete() for key in self.usertags: del(self.__tags[self.revmapping.get(key, key)]) self.images = [] @keys_deco def keys(self): return self.__tags.keys() def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" self.__images = [] tags, audio = self.load(filename, base) if audio is None: return for key in audio: if key == COVER_KEY: self.images = map(base64_to_image, audio[key]) else: self.__tags[key.lower()] = audio.tags[key] if base == FLAC: self.images = filter(None, map(bin_to_image, audio.pictures)) elif not self.images: self.images = [] self.__tags.update(info_to_dict(audio.info)) self.__tags.update(tags) self.set_attrs(ATTRIBUTES) self.mut_obj = audio self._originaltags = self.__tags.keys() self.update_tag_list() return self def save(self): """Writes the tags in self.__tags to self.filename if no filename is specified.""" filepath = self.filepath if self.mut_obj.tags is None: self.mut_obj.add_tags() if filepath != self.mut_obj.filename: self.mut_obj.filename = filepath audio = self.mut_obj newtag = {} for tag, value in usertags(self.__tags).items(): newtag[tag] = value if self.__images: if base == FLAC: audio.clear_pictures() map(lambda p: audio.add_picture(image_to_bin(p)), self.__images) else: newtag[COVER_KEY] = filter(None, map(image_to_base64, self.__images)) else: if base == FLAC: audio.clear_pictures() toremove = [z for z in audio if z not in newtag] for z in toremove: del(audio[z]) audio.update(newtag) audio.save() def set_fundamentals(self, tags, mut_obj, images=None): self.__tags = tags self.mut_obj = mut_obj if images: self.images = images else: self.images = [] self._originaltags = tags.keys() def update_tag_list(self): l = tags_in_file(self.filepath) if l: self.__tags['__tag'] = u'VorbisComment, ' + u', '.join(l) else: self.__tags['__tag'] = u'VorbisComment'
class Tag(util.MockTag): IMAGETAGS = (util.MIMETYPE, util.DATA, util.IMAGETYPE, util.DESCRIPTION) mapping = {} revmapping = {} __rtranslate = { 'album': 'WM/AlbumTitle', 'album_subtitle': 'WM/SetSubTitle', 'albumartist': 'WM/AlbumArtist', 'albumartistsortorder': 'WM/AlbumArtistSortOrder', 'albumsortorder': 'WM/AlbumSortOrder', 'artist': 'Author', 'bpm': 'WM/BeatsPerMinute', 'comment:': 'Description', 'composer': 'WM/Composer', 'conductor': 'WM/Conductor', 'copyright': 'WM/Copyright', 'discnumber': 'WM/PartOfSet', 'encodedby': 'WM/EncodedBy', 'encodersettings': 'WM/EncodingSettings', 'encodingtime': 'WM/EncodingTime', 'engineer': 'WM/Producer', 'genre': 'WM/Genre', 'grouping': 'WM/ContentGroupDescription', 'initialkey': 'WM/InitialKey', 'isrc': 'WM/ISRC', 'label': 'WM/Publisher', 'language': 'WM/Language', 'lyricist': 'WM/Writer', 'lyrics': 'WM/Lyrics', 'mbrainz_album_id': 'MusicBrainz/Album Id', 'mbrainz_albumartist_id': 'MusicBrainz/Album Artist Id', 'mbrainz_artist_id': 'MusicBrainz/Artist Id', 'mbrainz_discid': 'MusicBrainz/Disc Id', 'mbrainz_track_id': 'MusicBrainz/Track Id', 'mbrainz_trmid': 'MusicBrainz/TRM Id', 'mood': 'WM/Mood', 'musicip_puid': 'MusicIP/PUID', 'originalalbum': 'WM/OriginalAlbumTitle', 'originalartist': 'WM/OriginalArtist', 'originalfilename': 'WM/OriginalFilename', 'originallyricist': 'WM/OriginalLyricist', 'originalyear': 'WM/OriginalReleaseYear', 'performersortorder': 'WM/ArtistSortOrder', 'releasecountry': 'MusicBrainz/Album Release Country', 'releasestatus': 'MusicBrainz/Album Status', 'releasetype': 'MusicBrainz/Album Type', 'remixer': 'WM/ModifiedBy', 'subtitle': 'WM/SubTitle', 'title': 'Title', 'titlesortorder': 'WM/TitleSortOrder', 'track': 'WM/TrackNumber', 'unsyncedlyrics': 'WM/Lyrics', 'wwwartist': 'WM/AuthorURL', 'wwwaudiosource': 'WM/AudioSourceURL', 'wwwcommercialinfo': 'WM/PromotionURL', 'wwwcopyright': 'CopyrightURL', 'year': 'WM/Year', } __translate = dict([(v, k) for k, v in __rtranslate.iteritems()]) def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() util.MockTag.__init__(self, filename) def get_filepath(self): return util.MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(util.MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def _getImages(self): return self.__images def _setImages(self, images): if images: self.__images = map(parse_image, images) else: self.__images = [] cover_info(images, self.__tags) images = property(_getImages, _setImages) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags @getdeco def __getitem__(self, key): if key == '__image': return self.images elif key == '__total': return get_total(self) return self.__tags[key] @setdeco def __setitem__(self,key,value): if key.startswith('__'): if key == '__image': self.images = value elif key in fn_hash: setattr(self, fn_hash[key], value) elif key == '__total' and 'track' in self: if set_total(self, value): return return elif isempty(value): del(self[key]) elif key in self.__rtranslate: self.__tags[key] = unicode_list(value) @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del(self.__tags[key]) def delete(self): self.mut_obj.clear() for key in self.usertags: del(self.__tags[self.revmapping.get(key, key)]) self.images = [] self.save() def _info(self): info = self.mut_obj.info fileinfo = [('Path', self[PATH]), ('Size', str_filesize(int(self.size))), ('Filename', self[FILENAME]), ('Modified', self.modified)] wmainfo = [('Bitrate', self.bitrate), ('Frequency', self.frequency), ('Channels', unicode(info.channels)), ('Length', self.length)] return [('File', fileinfo), ('ASF Info', wmainfo)] info = property(_info) @keys_deco def keys(self): return self.__tags.keys() def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" self.images = None tags, audio = self.load(filename, ASF) if audio is None: return for name, values in audio.tags.iteritems(): try: self.__tags[self.__translate[name]] = map(unicode, values) except KeyError: if isinstance(values[0], ASFUnicodeAttribute): if not '/' in name and name not in self.__tags: self.__tags[name] = map(unicode, values) if 'WM/Picture' in audio: self.images = map(bin_to_pic, audio['WM/Picture']) self.__tags.update(info_to_dict(audio.info)) self.__tags.update(tags) self.__tags['__filetype'] = u'ASF' self.filetype = u'ASF' self.__tags['__tag_read'] = u'ASF' self.set_attrs(ATTRIBUTES) self.mut_obj = audio self.update_tag_list() return self def save(self): """Writes the tags in self.__tags to self.filename if no filename is specified.""" filepath = self.filepath if self.mut_obj.tags is None: self.mut_obj.add_tags() if filepath != self.mut_obj.filename: self.mut_obj.filename = filepath audio = self.mut_obj newtag = {} for field, value in usertags(self.__tags).items(): try: newtag[self.__rtranslate[field]] = value except KeyError: newtag[field] = value pics = map(pic_to_bin, self.images) if pics: newtag['WM/Picture'] = pics toremove = [z for z in audio if z not in newtag] for z in toremove: del(audio[z]) audio.update(newtag) audio.save() def update_tag_list(self): l = tag_versions.tags_in_file(self.filepath) if l: self.__tags['__tag'] = u'ASF, ' + u', '.join(l) else: self.__tags['__tag'] = u'ASF'
def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() util.MockTag.__init__(self, filename)
class Tag(TagBase): IMAGETAGS = (util.MIMETYPE, util.DESCRIPTION, util.DATA, util.IMAGETYPE) mapping = {} revmapping = {} def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() #Used as storage. #Each key as the is the field as used by puddletag, eg. 'artist' #Each value contains an mutagen.id3.Frame object #that have two methods, get_value and set_value. #get_value returns the value stored by the frame as #text/unicode list. #set_value should take text/unicode list in parse it into #it understands. #When saving the frame stored will be used. If it has a 'frames' #attributes, those frames will be used instead. util.MockTag.__init__(self, filename) def get_filepath(self): return util.MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(util.MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def _get_images(self): return self.__images def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) images = property(_get_images, _set_images) def _info(self): info = self.mut_obj.info fileinfo = [('Path', self[PATH]), ('Size', str_filesize(int(self.size))), ('Filename', self[FILENAME]), ('Modified', self.modified)] try: version = self.mut_obj.tags.version version = ('ID3 Version', self.filetype) except AttributeError: version = ('ID3 Version', 'No tags in file.') fileinfo.append(version) if isinstance(self.mut_obj, DSFFileType): mpginfo = [('Type', 'DSF')] elif isinstance(self.mut_obj, AIFFFileType): mpginfo = [('Type', 'AIFF')] elif (self.mut_obj, ID3FileType): mpginfo = [('Version', u'MPEG %i Layer %i' % (info.version, info.layer))] else: mpginfo = [] try: mpginfo.append(('Bitrate', self.bitrate)) except AttributeError: pass try: mpginfo.append(('Frequency', self.frequency)) except AttributeError: pass try: mpginfo.append(('Mode', MODES[info.mode])) except AttributeError: pass try: mpginfo.append(('Length', self.length)) except Att: pass return [('File', fileinfo), (mpginfo[0][0], mpginfo)] info = property(_info) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo=None): cls = Tag() tags = CaselessDict() frames = [] [ frames.append(frame) if not hasattr(frame, 'frames') else frames.extend(frame.frames) for key, frame in self.__tags.items() if not key.startswith('__') ] funcs = [] frames_copy = [] for frame in frames: funcs.append((getattr(frame, 'get_value', None), getattr(frame, 'set_value', None))) if hasattr(frame, 'get_value'): delattr(frame, 'get_value') if hasattr(frame, 'set_value'): delattr(frame, 'set_value') frames_copy.append(deepcopy(frame)) tags = handle( dict([(frame.HashKey, frame) for frame in frames_copy])) for frame, (get_value, set_value) in zip(frames, funcs): if get_value is not None: frame.get_value = get_value if set_value is not None: frame.set_value = set_value for key, value in self.__tags.items(): if key not in tags: tags[key] = deepcopy(value) cls.set_fundamentals(tags, self.mut_obj, deepcopy(self.images)) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del (self.__tags[key]) @getdeco def __getitem__(self, key): if key.startswith('__'): if key == '__image': return self.images elif key == '__filetype': return self.filetype elif key == '__total': return get_total(self) else: return self.__tags[key] elif not isinstance(key, basestring): return self.__tags[key] else: return self.__tags[key].get_value() @setdeco def __setitem__(self, key, value): if not isinstance(key, basestring): self.__tags[key] = value return if key.startswith('__'): if key == '__image': self.images = value elif key in fn_hash: setattr(self, fn_hash[key], value) elif key == '__total': set_total(self, value) return value = unicode_list(value) if isempty(value): if key in self: del (self[key]) return if key in self.__tags: self.__tags[key].set_value(value) else: if key in write_frames: self.__tags.update(write_frames[key](value)) elif key == u'comment': frame = {'comment': create_comment('', value)['comment:']} self.__tags.update(frame) elif key.startswith('comment:'): self.__tags.update( create_comment(key[len('comment:'):], value)) elif key.startswith('www:'): self.__tags.update(create_userurl(key, value)) elif key.startswith('ufid:'): self.__tags.update(create_ufid(key, value)) elif key.startswith('rgain:'): self.__tags.update(create_rgain(key, value)) elif key.startswith('unsyncedlyrics:'): self.__tags.update(create_uslt(key, value)) else: self.__tags.update(create_usertext(key, value)) def delete(self): self.mut_obj.delete() for key in self.usertags: del (self.__tags[self.revmapping.get(key, key)]) self.images = [] @keys_deco def keys(self): return self.__tags.keys() def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" self.__images = [] tags, audio = self.load(filename, id3_filetype) if audio is None: return if audio.tags: #Not empty audio.tags.update_to_v24() self.__tags.update(handle(audio)) #Get the image data. apics = audio.tags.getall("APIC") if apics: self.images = map(bin_to_pic, apics) else: self.images = [] self.__tags.update(tags) self.__tags.update(info_to_dict(audio.info)) if self.ext.lower() == 'mp3': self.__tags['__filetype'] = u'MP3' else: self.__tags['__filetype'] = u'ID3' self.set_attrs(ATTRIBUTES, self.__tags) try: self.__tags[ '__tag_read'] = u'ID3v%s.%s' % audio.tags.version[:2] except AttributeError: self.__tags['__tag_read'] = u'' self.mut_obj = audio self._originaltags = audio.keys() self.update_tag_list() return self def save(self, v1=None, v2=None): if v1 is None: v1 = v1_option """Writes the tags to file.""" filename = self.filepath if self.mut_obj.tags is None: self.mut_obj.add_tags() if filename != self.mut_obj.filename: self.mut_obj.tags.filename = filename self.mut_obj.filename = filename audio = self.mut_obj util.MockTag.save(self) userkeys = usertags(self.__tags).keys() frames = [] [ frames.append(frame) if not hasattr(frame, 'frames') else frames.extend(frame.frames) for key, frame in self.__tags.items() if key in userkeys ] hashes = dict([(frame.HashKey, frame) for frame in frames]) toremove = [ z for z in self._originaltags if z in audio and not (z in hashes or z.startswith('APIC')) ] audio.update(hashes) old_apics = [z for z in audio if z.startswith(u'APIC')] if self.__images: newimages = [] for image in filter(None, map(pic_to_bin, self.__images)): i = 0 while image.HashKey in newimages: i += 1 #Pad with spaces so that each key is unique. image.desc += u' ' * i audio[image.HashKey] = image newimages.append(image.HashKey) [toremove.append(z) for z in old_apics if z not in newimages] else: toremove.extend(old_apics) for z in set(toremove): try: del (audio[z]) except KeyError: continue audio.tags.filename = self.filepath v1 = v1_option if v1 is None else v1 v2 = v2_option if v2 is None else v2 if AIFF is not None and id3_filetype is AIFFFileType: if v2 == 3: audio.tags.save(v2_version=3) #AIFF doesn't support id3v1 else: audio.tags.save() #AIFF doesn't support id3v1 elif DSF is not None and id3_filetype is DSFFileType: if v2 == 3: audio.tags.save(v2_version=3) #DSF doesn't support id3v1 else: audio.tags.save() #DSF doesn't support id3v1 else: if v2 == 4: audio.tags.update_to_v24() audio.tags.save(v1=v1, v2=4) else: c = ID3() c.filename = self.filepath c.update(audio) c.update_to_v23() c.save(v1=v1, v2=3) self.__tags['__tag_read'] = u'ID3v2.4' if v2 == 4 else u'ID3v2.3' self.update_tag_list() self._originaltags = audio.keys() def set_fundamentals(self, tags, mut_obj, images=None): self.__tags = tags self.mut_obj = mut_obj if images: self.images = images self._originaltags = tags.keys() self.set_attrs(ATTRIBUTES, tags) def to_encoding(self, encoding=UTF8): frames = [] saved = [] for frame in self.__tags.values(): if hasattr(frame, 'encoding'): frames.append((frame, frame.encoding)) frame.encoding = encoding try: self.save(v2=4) except: for frame, enc in frames: frame.encoding = enc raise def update_tag_list(self): tag = self.__tags['__tag_read'] l = tag_versions.tags_in_file(self.filepath) if l: if tag and tag in l: l.remove(tag) l.insert(0, tag) self.__tags['__tag'] = u', '.join(l) else: self.__tags['__tag'] = tag
class Tag(TagBase): IMAGETAGS = (util.MIMETYPE, util.DESCRIPTION, util.DATA, util.IMAGETYPE) mapping = {} revmapping = {} def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() #Used as storage. #Each key as the is the field as used by puddletag, eg. 'artist' #Each value contains an mutagen.id3.Frame object #that have two methods, get_value and set_value. #get_value returns the value stored by the frame as #text/unicode list. #set_value should take text/unicode list in parse it into #it understands. #When saving the frame stored will be used. If it has a 'frames' #attributes, those frames will be used instead. util.MockTag.__init__(self, filename) def get_filepath(self): return util.MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(util.MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def _get_images(self): return self.__images def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) images = property(_get_images, _set_images) def _info(self): info = self.mut_obj.info fileinfo = [('Path', self[PATH]), ('Size', str_filesize(int(self.size))), ('Filename', self[FILENAME]), ('Modified', self.modified)] try: version = self.mut_obj.tags.version version = ('ID3 Version', self.filetype) except AttributeError: version = ('ID3 Version', 'No tags in file.') fileinfo.append(version) mpginfo = [('Version', u'MPEG %i Layer %i' % (info.version, info.layer)), ('Bitrate', self.bitrate), ('Frequency', self.frequency), ('Mode', MODES[info.mode]), ('Length', self.length)] return [('File', fileinfo), (mpginfo[0][0], mpginfo)] info = property(_info) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo=None): cls = Tag() tags = CaselessDict() frames = [] [frames.append(frame) if not hasattr(frame, 'frames') else frames.extend(frame.frames) for key, frame in self.__tags.items() if not key.startswith('__')] funcs = [] frames_copy = [] for frame in frames: funcs.append((getattr(frame, 'get_value', None), getattr(frame, 'set_value', None))) if hasattr(frame, 'get_value'): delattr(frame, 'get_value') if hasattr(frame, 'set_value'): delattr(frame, 'set_value') frames_copy.append(deepcopy(frame)) tags = handle(dict([(frame.HashKey, frame) for frame in frames_copy])) for frame, (get_value, set_value) in zip(frames, funcs): if get_value is not None: frame.get_value = get_value if set_value is not None: frame.set_value = set_value for key, value in self.__tags.items(): if key not in tags: tags[key] = deepcopy(value) cls.set_fundamentals(tags, self.mut_obj, deepcopy(self.images)) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del(self.__tags[key]) @getdeco def __getitem__(self,key): if key.startswith('__'): if key == '__image': return self.images elif key == '__filetype': return self.filetype elif key == '__total': return get_total(self) else: return self.__tags[key] elif not isinstance(key, basestring): return self.__tags[key] else: return self.__tags[key].get_value() @setdeco def __setitem__(self, key, value): if not isinstance(key, basestring): self.__tags[key] = value return if key.startswith('__'): if key == '__image': self.images = value elif key in fn_hash: setattr(self, fn_hash[key], value) elif key == '__total': set_total(self, value) return value = unicode_list(value) if isempty(value): if key in self: del(self[key]) return if key in self.__tags: self.__tags[key].set_value(value) else: if key in write_frames: self.__tags.update(write_frames[key](value)) elif key == u'comment': frame = {'comment': create_comment('', value)['comment:']} self.__tags.update(frame) elif key.startswith('comment:'): self.__tags.update(create_comment(key[len('comment:'):], value)) elif key.startswith('www:'): self.__tags.update(create_userurl(key, value)) elif key.startswith('ufid:'): self.__tags.update(create_ufid(key, value)) elif key.startswith('rgain:'): self.__tags.update(create_rgain(key, value)) elif key.startswith('unsyncedlyrics:'): self.__tags.update(create_uslt(key, value)) else: self.__tags.update(create_usertext(key, value)) def delete(self): self.mut_obj.delete() for key in self.usertags: del(self.__tags[self.revmapping.get(key, key)]) self.images = [] @keys_deco def keys(self): return self.__tags.keys() def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" self.__images = [] tags, audio = self.load(filename, id3_filetype) if audio is None: return if audio.tags: #Not empty audio.tags.update_to_v24() self.__tags.update(handle(audio)) #Get the image data. apics = audio.tags.getall("APIC") if apics: self.images = map(bin_to_pic, apics) else: self.images = []; self.__tags.update(tags) self.__tags.update(info_to_dict(audio.info)) if self.ext.lower() == 'mp3': self.__tags['__filetype'] = u'MP3' else: self.__tags['__filetype'] = u'ID3' self.set_attrs(ATTRIBUTES, self.__tags) try: self.__tags['__tag_read'] = u'ID3v%s.%s' % audio.tags.version[:2] except AttributeError: self.__tags['__tag_read'] = u'' self.mut_obj = audio self._originaltags = audio.keys() self.update_tag_list() return self def save(self, v1=None, v2=None): if v1 is None: v1 = v1_option """Writes the tags to file.""" filename = self.filepath if self.mut_obj.tags is None: self.mut_obj.add_tags() if filename != self.mut_obj.filename: self.mut_obj.tags.filename = filename self.mut_obj.filename = filename audio = self.mut_obj util.MockTag.save(self) userkeys = usertags(self.__tags).keys() frames = [] [frames.append(frame) if not hasattr(frame, 'frames') else frames.extend(frame.frames) for key, frame in self.__tags.items() if key in userkeys] hashes = dict([(frame.HashKey, frame) for frame in frames]) toremove = [z for z in self._originaltags if z in audio and not (z in hashes or z.startswith('APIC'))] audio.update(hashes) old_apics = [z for z in audio if z.startswith(u'APIC')] if self.__images: newimages = [] for image in filter(None, map(pic_to_bin, self.__images)): i = 0 while image.HashKey in newimages: i += 1 #Pad with spaces so that each key is unique. image.desc += u' '*i audio[image.HashKey] = image newimages.append(image.HashKey) [toremove.append(z) for z in old_apics if z not in newimages] else: toremove.extend(old_apics) for z in set(toremove): try: del(audio[z]) except KeyError: continue audio.tags.filename = self.filepath v1 = v1_option if v1 is None else v1 v2 = v2_option if v2 is None else v2 if AIFF is not None and id3_filetype is AIFFFileType: if v2 == 3: audio.tags.save(v2_version=3) #AIFF doesn't support id3v1 else: audio.tags.save() #AIFF doesn't support id3v1 else: if v2 == 4: audio.tags.update_to_v24() audio.tags.save(v1=v1, v2=4) else: c = ID3() c.filename = self.filepath c.update(audio) c.update_to_v23() c.save(v1=v1, v2=3) self.__tags['__tag_read'] = u'ID3v2.4' if v2 == 4 else u'ID3v2.3' self.update_tag_list() self._originaltags = audio.keys() def set_fundamentals(self, tags, mut_obj, images=None): self.__tags = tags self.mut_obj = mut_obj if images: self.images = images self._originaltags = tags.keys() self.set_attrs(ATTRIBUTES, tags) def to_encoding(self, encoding = UTF8): frames = [] saved = [] for frame in self.__tags.values(): if hasattr(frame, 'encoding'): frames.append((frame, frame.encoding)) frame.encoding = encoding try: self.save(v2=4) except: for frame, enc in frames: frame.encoding = enc raise def update_tag_list(self): tag = self.__tags['__tag_read'] l = tag_versions.tags_in_file(self.filepath) if l: if tag and tag in l: l.remove(tag) l.insert(0, tag) self.__tags['__tag'] = u', '.join(l) else: self.__tags['__tag'] = tag
class APEv2Base(MockTag): """Tag class for APEv2 files. Tags are used as in ogg.py""" IMAGETAGS = (util.MIMETYPE, util.DESCRIPTION, util.DATA, util.IMAGETYPE) mapping = {} revmapping = {} apev2 = True def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() MockTag.__init__(self, filename) def get_filepath(self): return MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def _get_images(self): return self.__images def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) images = property(_get_images, _set_images) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo): cls = Tag() cls.mapping = self.mapping cls.revmapping = self.revmapping cls.set_fundamentals(deepcopy(self.__tags), self.mut_obj, self.images) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del (self.__tags[key]) @getdeco def __getitem__(self, key): if key == '__image': return self.images elif key == '__total': return get_total(self) return self.__tags[key] @setdeco def __setitem__(self, key, value): if key == '__image': self.images = value return if key.startswith('__'): if key == '__image': self.images = value elif key == '__total': set_total(self, value) elif key in fn_hash: setattr(self, fn_hash[key], value) return elif isempty(value): if key in self: del (self[key]) else: value = unicode_list(value) if isempty(value): return self.__tags[key] = value def delete(self): self.mut_obj.delete() for key in self.usertags: del (self.__tags[self.revmapping.get(key, key)]) self.images = [] def _info(self): info = self.mut_obj.info fileinfo = [(u'Path', self[PATH]), (u'Size', str_filesize(int(self.size))), (u'Filename', self[FILENAME]), (u'Modified', self.modified)] apeinfo = [('Length', self.length)] attr = [(u'Channels', 'channels'), (u'Version', 'version')] for k, v in attr: try: apeinfo.append([k, unicode(getattr(info, v))]) except AttributeError: continue return [('File', fileinfo), ("%s Info" % self.filetype, apeinfo)] info = property(_info) @keys_deco def keys(self): return self.__tags.keys() def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" no_header = DefaultHeaderError if header_error is None \ else header_error self.__images = [] try: tags, audio = self.load(filename, mutagen_file) except no_header: #Try loading just APEv2 tags, audio = self.load(filename, APEv2File) except APENoHeaderError: audio = mutagen_file() tags, audio = self.load(filename, None) audio.filename = tags['__filepath'] if audio is None: return images = [] for key in audio: try: if key.lower() in COVER_KEYS: img_type = COVER_KEYS.get(key.lower(), 3) images.append(bin_to_pic(audio[key].value, img_type)) else: self.__tags[key.lower()] = audio.tags[key][:] except TypeError: pass self.images = images self.__tags.update(info_to_dict(audio.info)) self.__tags.update(tags) self.__tags['__tag_read'] = u'APEv2' self.set_attrs(attrib_fields) self.filetype = filetype self.__tags['__filetype'] = filetype self.update_tag_list() self.mut_obj = audio return self def save(self): if self.mut_obj.tags is None: self.mut_obj.add_tags() if self.filepath != self.mut_obj.filename: self.mut_obj.filename = self.filepath audio = self.mut_obj newtag = {} if self.images: [newtag.update(pic_to_bin(z)) for z in self.images] for field, value in usertags(self.__tags).items(): try: if isinstance(field, unicode): field = field.encode('utf8') newtag[field] = value except AttributeError: pass toremove = [ z for z in audio if z not in newtag and audio[z].kind == 0 ] for z in toremove: del (audio[z]) audio.tags.update(newtag) audio.save() def set_fundamentals(self, tags, mut_obj, images=None): self.__tags = tags self.mut_obj = mut_obj if images: self.images = images self._originaltags = tags.keys() def update_tag_list(self): l = tag_versions.tags_in_file( self.filepath, [tag_versions.ID3_V1, tag_versions.ID3_V2]) if l: self.__tags['__tag'] = u'APEv2, ' + u', '.join(l) else: self.__tags['__tag'] = u'APEv2'
def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() MockTag.__init__(self, filename)
class APEv2Base(MockTag): """Tag class for APEv2 files. Tags are used as in ogg.py""" IMAGETAGS = (util.MIMETYPE, util.DESCRIPTION, util.DATA, util.IMAGETYPE) mapping = {} revmapping = {} apev2 = True def __init__(self, filename=None): self.__images = [] self.__tags = CaselessDict() MockTag.__init__(self, filename) def get_filepath(self): return MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def _get_images(self): return self.__images def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) images = property(_get_images, _set_images) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo): cls = Tag() cls.mapping = self.mapping cls.revmapping = self.revmapping cls.set_fundamentals(deepcopy(self.__tags), self.mut_obj, self.images) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del(self.__tags[key]) @getdeco def __getitem__(self, key): if key == '__image': return self.images elif key == '__total': return get_total(self) return self.__tags[key] @setdeco def __setitem__(self, key, value): if key == '__image': self.images = value return if key.startswith('__'): if key == '__image': self.images = value elif key == '__total': set_total(self, value) elif key in fn_hash: setattr(self, fn_hash[key], value) return elif isempty(value): if key in self: del(self[key]) else: value = unicode_list(value) if isempty(value): return self.__tags[key] = value def delete(self): self.mut_obj.delete() for key in self.usertags: del(self.__tags[self.revmapping.get(key, key)]) self.images = [] def _info(self): info = self.mut_obj.info fileinfo = [(u'Path', self[PATH]), (u'Size', str_filesize(int(self.size))), (u'Filename', self[FILENAME]), (u'Modified', self.modified)] apeinfo = [('Length', self.length)] attr = [ (u'Channels', 'channels'), (u'Version', 'version')] for k, v in attr: try: apeinfo.append([k, unicode(getattr(info, v))]) except AttributeError: continue return [('File', fileinfo), ("%s Info" % self.filetype, apeinfo)] info = property(_info) @keys_deco def keys(self): return self.__tags.keys() def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" no_header = DefaultHeaderError if header_error is None \ else header_error self.__images = [] try: tags, audio = self.load(filename, mutagen_file) except no_header: #Try loading just APEv2 tags, audio = self.load(filename, APEv2File) except APENoHeaderError: audio = mutagen_file() tags, audio = self.load(filename, None) audio.filename = tags['__filepath'] if audio is None: return images = [] for key in audio: try: if key.lower() in COVER_KEYS: img_type = COVER_KEYS.get(key.lower(), 3) images.append( bin_to_pic(audio[key].value, img_type)) else: self.__tags[key.lower()] = audio.tags[key][:] except TypeError: pass self.images = images self.__tags.update(info_to_dict(audio.info)) self.__tags.update(tags) self.__tags['__tag_read'] = u'APEv2' self.set_attrs(attrib_fields) self.filetype = filetype self.__tags['__filetype'] = filetype self.update_tag_list() self.mut_obj = audio return self def save(self): if self.mut_obj.tags is None: self.mut_obj.add_tags() if self.filepath != self.mut_obj.filename: self.mut_obj.filename = self.filepath audio = self.mut_obj newtag = {} if self.images: [newtag.update(pic_to_bin(z)) for z in self.images] for field, value in usertags(self.__tags).items(): try: if isinstance(field, unicode): field = field.encode('utf8') newtag[field] = value except AttributeError: pass toremove = [z for z in audio if z not in newtag and audio[z].kind == 0] for z in toremove: del(audio[z]) audio.tags.update(newtag) audio.save() def set_fundamentals(self, tags, mut_obj, images=None): self.__tags = tags self.mut_obj = mut_obj if images: self.images = images self._originaltags = tags.keys() def update_tag_list(self): l = tag_versions.tags_in_file(self.filepath, [tag_versions.ID3_V1, tag_versions.ID3_V2]) if l: self.__tags['__tag'] = u'APEv2, ' + u', '.join(l) else: self.__tags['__tag'] = u'APEv2'
class Tag(util.MockTag): """Class for AAC tags.""" mapping = {} revmapping = {} IMAGETAGS = (util.MIMETYPE, util.DATA) def __init__(self, filename=None): self.__images = [] self.__errors = set() self.__tags = CaselessDict() util.MockTag.__init__(self, filename) def get_filepath(self): return util.MockTag.get_filepath(self) def set_filepath(self, val): self.__tags.update(util.MockTag.set_filepath(self, val)) filepath = property(get_filepath, set_filepath) def __contains__(self, key): if key == '__image': return bool(self.images) elif key == '__total': try: return bool(get_total(self)) except (KeyError, ValueError): return False if self.revmapping: key = self.revmapping.get(key, key) return key in self.__tags def __deepcopy__(self, memo=None): cls = Tag() cls.mapping = self.mapping cls.revmapping = self.revmapping cls.set_fundamentals(deepcopy(self.__tags), deepcopy(self.images), self.mut_obj, deepcopy(self.__freeform), deepcopy(self.__errors)) cls.filepath = self.filepath return cls @del_deco def __delitem__(self, key): if key == '__image': self.images = [] elif key.startswith('__'): return else: del (self.__tags[key]) @getdeco def __getitem__(self, key): if key.startswith('__'): if key == '__image': return self.images elif key == '__total': return FUNCS['totaltracks'][0](self.__tags['totaltracks']) else: return self.__tags[key] try: return FUNCS[key][0](self.__tags[key]) except KeyError: return gettext(self.__tags[key]) @setdeco def __setitem__(self, key, value): if isinstance(key, (int, long)): self.__tags[key] = value return elif key.startswith('__'): if key == '__image': self.images = value if key in FILETAGS: setattr(self, fn_hash[key], value) elif key == '__total': self.__tags['totaltracks'] = FUNCS['totaltracks'][1](value) return elif isempty(value): if key in self: del (self[key]) return else: try: new_val = FUNCS[key][1](value) if value: self.__tags[key] = new_val except KeyError: #User defined tags. self.__freeform[key] = '----:com.apple.iTunes:%s' % key self.__tags[key] = settext(value) except ValueError: pass def delete(self): self.mut_obj.delete() for key in self.usertags: del (self.__tags[self.revmapping.get(key, key)]) self.images = [] def _set_images(self, images): if images: self.__images = map(lambda i: parse_image(i, self.IMAGETAGS), images) else: self.__images = [] cover_info(images, self.__tags) def _get_images(self): return self.__images images = property(_get_images, _set_images) def _info(self): info = self.mut_obj.info fileinfo = [('Path', self[PATH]), ('Size', str_filesize(int(self.size))), ('Filename', self[FILENAME]), ('Modified', self.modified)] mp4info = [('Bitrate', self.bitrate), ('Frequency', self.frequency), ('Channels', unicode(info.channels)), ('Length', self.length), ('Bits per sample', unicode(info.bits_per_sample))] return [('File', fileinfo), ('MP4 Info', mp4info)] info = property(_info) def link(self, filename): """Links the audio, filename returns self if successful, None otherwise.""" tags, audio = self.load(filename, MP4) self.images = [] if audio is None: return revmap, mapping = self.revmapping, self.mapping self.revmapping, self.mapping = {}, {} self.__freeform = { } #Keys are tags as required by mutagen i.e. The '----' #frames. Values are the tag as represented by puddletag. if audio.tags: #Not empty keys = audio.keys() try: self.images = map(bin_to_pic, audio['covr']) keys.remove('covr') except KeyError: self.images = [] convert = lambda k, v: FUNCS[k][1](v) #I want 'trkn', to split into track and totaltracks, like Mp3tag. if 'trkn' in keys: tags['track'] = convert('track', [z[0] for z in audio['trkn']]) tags['totaltracks'] = convert('totaltracks', [z[1] for z in audio['trkn']]) keys.remove('trkn') #Same as above if 'disk' in keys: tags['disc'] = convert('disc', [z[0] for z in audio['disk']]) tags['totaldiscs'] = convert('totaldiscs', [z[1] for z in audio['disk']]) keys.remove('disk') for key in keys: if key in TAGS: tags[TAGS[key]] = convert(TAGS[key], audio[key]) else: field = key[key.find(':', key.find(':') + 1) + 1:] try: field = field.decode('utf8') except UnicodeDecodeError: field = field.decode('latin1') self.__freeform[field] = key try: field_value = [] for v in audio[key]: if isinstance(v, str): field_value.append(unicode(v, 'utf8')) elif isinstance(v, unicode): field_value.append(v) else: field_value.append(unicode(v)) except UnicodeDecodeError: self.__errors.add(field) for k, v in tags.items(): if not v: del (tags[k]) self.__tags.update(info_to_dict(audio.info)) self.__tags.update(tags) self.revmapping, self.mapping = revmap, mapping self.__tags['__tag_read'] = u'MP4' self.filetype = 'MP4' self.__tags['__filetype'] = self.filetype self.set_attrs(ATTRIBUTES, self.__tags) self.update_tag_list() self.mut_obj = audio @keys_deco def keys(self): return self.__tags.keys() def save(self): if self.mut_obj.tags is None: self.mut_obj.add_tags() if self.filepath != self.mut_obj.filename: self.mut_obj.filename = self.filepath audio = self.mut_obj newtag = {} tuples = (('track', ['trkn', 'totaltracks']), ('disc', ['disk', 'totaldiscs'])) tags = self.__tags for tag, values in tuples: if tag in tags: denom = tags[tag] if values[1] in tags: total = tags[values[1]] newtag[values[0]] = [(int(t), int(total)) for t, total in zip(denom, total)] else: newtag[values[0]] = [(int(z), 0) for z in denom] elif values[1] in tags: total = tags[values[1]] newtag[values[0]] = [(0, int(z)) for z in total] tags = usertags(self.__tags) tags = [(z, tags[z]) for z in tags if z not in ['track', 'totaltracks', 'disc', 'totaldiscs']] for tag, value in tags: try: newtag[REVTAGS[tag]] = value except KeyError: newtag[self.__freeform[tag].encode('utf8')] = encode( self.__tags[tag]) if self.images: newtag['covr'] = filter(None, map(pic_to_bin, self.images)) toremove = [ z for z in audio.keys() if z not in newtag and z not in self.__errors ] for key in toremove: del (audio[key]) audio.update(newtag) audio.save() def set_fundamentals(self, tags, images, mut_obj, freeform=None, errors=None): self.__freeform = {} if freeform is None else freeform self.__errors = {} if errors is None else errors self.__tags = tags self.mut_obj = mut_obj self.images = images def update_tag_list(self): l = tag_versions.tags_in_file(self.filepath) if l: self.__tags['__tag'] = u'MP4, ' + u', '.join(l) else: self.__tags['__tag'] = u'MP4'