def Tags(path, fileformat=None): """ Loader for file metadata tags. Tag reading and writing for various file formats is implemented by tag formatter classes in module soundforest.tags.formats, initialized automatically by this class. """ if not os.path.isfile(path): raise TagError('No such file: %s' % path) path = normalized(os.path.realpath(path)) if fileformat is None: fileformat = AudioFileFormat(path) if not isinstance(fileformat, AudioFileFormat): raise TagError('File format must be AudioFileFormat instance') fileformat = fileformat if fileformat.is_metadata: raise TagError('Attempting to load audio tags from metadata file') if fileformat.codec is None: raise TagError('Unsupported audio file: %s' % path) tag_parser = fileformat.get_tag_parser() if tag_parser is None: return None return tag_parser(fileformat.codec, path)
def import_albumart(self, albumart): """ Parent method to set albumart tag. Child class must implement actual embedding of the tag to file. """ if not isinstance(albumart, AlbumArt): raise TagError('Albumart must be AlbumArt instance') if not albumart.is_loaded: raise TagError('Albumart to import is not loaded with image.') self.albumart = albumart
def __getitem__(self, item): """ Return tags formatted to unicode, decimal.Decimal or other supported types. Does not include albumart images, which are accessed by self.__getattr__('albumart') """ if item == 'tracknumber': return [unicode('%d' % self.track_numbering.value)] if item == 'totaltracks': return [unicode('%d' % self.track_numbering.total)] if item == 'disknumber': return [unicode('%d' % self.disk_numbering.value)] if item == 'totaldisks': return [unicode('%d' % self.disk_numbering.total)] if item[:5] == 'APIC:': return self.albumart_obj fields = self.__tag2fields__(item) for field in fields: if not self.entry.has_key(field): continue tag = self.entry[field] if not isinstance(tag, list): tag = [tag] values = [] for value in tag: matched = False for field in ['text', 'url']: if hasattr(value, field): value = getattr(value, field) if isinstance(value, list): value = value[0] matched = True break if value is None: continue if not matched: raise TagError('Error parsing %s: %s' % (tag, dir(value))) if not isinstance(value, unicode): try: value = '%d' % int(str(value)) except ValueError, emsg: pass try: value = unicode(value, 'utf-8') except UnicodeDecodeError, emsg: raise TagError('Error decoding %s tag %s: %s' % (self.path, field, emsg)) values.append(value)
def get_tag(self, item): """ Return tag from file. Raises TagError if tag is not found. """ if not self.has_key(item): raise TagError('No such tag: %s' % item) value = self.__normalized_tag__(item) if value is None: raise TagError('No such string tag: %s' % item) return value
def __getitem__(self, item): """ Return tags formatted to unicode, decimal.Decimal or other supported types. Does not include albumart images, which are accessed via self.albumart attribute """ fields = self.__tag2fields__(item) for field in fields: if field not in self.entry: continue tag = self.entry[field] if not isinstance(tag, list): tag = [tag] values = [] for value in tag: if not isinstance(value, unicode): if isinstance(value, int): value = unicode('%d' % value) else: try: value = unicode(value, 'utf-8') except UnicodeDecodeError, emsg: raise TagError('Error decoding %s tag %s: %s' % (self.path, field, emsg)) values.append(value) return values
def save(self, path): """ Save current albumart to given pathname """ if self.albumart is None: raise TagError('Error saving albumart: albumart is not loaded') self.albumart.save(path)
def as_base64_tag(self): """ Return albumart image data as base64_tag tag """ if self.albumart is None: raise TagError('Albumart is not loaded') return base64_tag(base64.b64encode(self.albumart.dump()))
def update_tags(self, data): if not isinstance(data, dict): raise TagError('Updated tags must be a dictionary instance') for k, v in data.items(): self.set_tag(k, v) return self.modified
def __init__(self, track): if not isinstance(track, mp3): raise TagError('Track is not instance of mp3') TrackAlbumart.__init__(self, track) try: self.tag = filter(lambda k: k[:5] == 'APIC:', self.track.entry.keys())[0] except IndexError: self.tag = None return try: albumart = AlbumArt() albumart.import_data(self.track.entry[self.tag].data) except AlbumArtError, emsg: raise TagError('Error reading mp3 albumart tag: %s' % emsg)
def replace_tags(self, data): """Replace tags Set tags given in data dictionary, removing any existing tags. """ if not isinstance(data, dict): raise TagError('Updated tags must be a dictionary instance') self.clear_tags() return self.update_tags(data)
def encode_frame(tag, value): """ Return a mp3 frame object matching tag """ try: m = __import__('mutagen.id3', globals(), {}, tag) tagclass = getattr(m, tag) if tagclass is None: raise AttributeError except AttributeError, emsg: raise TagError('Error importing ID3 frame %s: %s' % (tag, emsg))
def __init__(self, codec, path): TagParser.__init__(self, codec, path, tag_map=MP3_STANDARD_TAGS) try: self.entry = MP3(self.path, ID3=ID3) except IOError: raise TagError('No ID3 header in %s' % self.path) except ID3NoHeaderError: raise TagError('No ID3 header in %s' % self.path) except RuntimeError: raise TagError('Runtime error loading %s' % self.path) try: self.entry.add_tags() except ID3Error: pass self.albumart_obj = MP3AlbumArt(self) self.track_numbering = MP3NumberingTag(self, 'TRCK') self.disk_numbering = MP3NumberingTag(self, 'TPOS')
def __setattr__(self, attr, value): if attr in ['value', 'total']: if isinstance(value, list): value = value[0] try: if value is not None: value = int(value) except ValueError: raise TagError('TrackNumberingTag values must be integers') except TypeError: raise TagError('TrackNumberingTag values must be integers') if attr == 'value': self.f_value = value if attr == 'total': self.f_total = value else: object.__setattr__(self, attr, value)
def __init__(self, track, tag): TrackNumberingTag.__init__(self, track, tag) if not self.track.entry.has_key(self.tag): return try: self.value, self.total = self.track.entry[self.tag].text[0].split( '/') except ValueError: self.value = self.track.entry[self.tag].text[0] self.total = self.value try: self.value = int(self.value) self.total = int(self.total) except ValueError: raise TagError('Unsupported tag value for %s: %s' % (self.tag, self.track.entry[self.tag].text[0]))
""" try: for attr in ('track_numbering', 'disk_numbering'): try: tag = getattr(self, attr) tag.save_tag() except ValueError, emsg: logger.debug('Error processing %s: %s' % (attr, emsg)) if not self.modified: return self.entry.save() except OSError, (ecode, emsg): raise TagError(emsg) except IOError, (ecode, emsg): raise TagError(emsg) class TrackAlbumart(object): """ Parent class for common albumart operations """ def __init__(self, track): self.track = track self.modified = False self.albumart = None def __repr__(self):
def remove_tag(self, item): if not self.has_key(item): raise TagError('No such tag: %s' % item) del self[tag]
def set_albumart(self, albumart): if not self.supports_albumart: raise TagError('Format does not support albumart') return self.albumart.import_albumart(albumart)
def __setitem__(self, item, value): if isinstance(item, AlbumArt): try: self.albumart_obj.import_albumart(value) except AlbumArtError, emsg: raise TagError('Error setting albumart: %s' % emsg)