diff --git a/picard/formats/apev2.py b/picard/formats/apev2.py index 62a62528c..6a96d55a0 100644 --- a/picard/formats/apev2.py +++ b/picard/formats/apev2.py @@ -21,8 +21,9 @@ import mutagen.apev2 import mutagen.monkeysaudio import mutagen.musepack import mutagen.wavpack -import mutagenext.optimfrog +import mutagen.optimfrog from picard.file import File +from picard.metadata import Metadata from picard.util import encode_filename, sanitize_date class APEv2File(File): @@ -33,12 +34,20 @@ class APEv2File(File): "Album Artist": "albumartist", "MixArtist": "remixer", "Weblink": "website", + "DiscSubtitle": "discsubtitle", + "BPM": "bpm", + "ISRC": "isrc", + "CatalogNumber": "catalognumber", + "BarCode": "barcode", + "EncodedBy": "encodedby", "MUSICBRAINZ_ALBUMSTATUS": "releasestatus", "MUSICBRAINZ_ALBUMTYPE": "releasetype", } + __rtranslate = dict([(v, k) for k, v in __translate.iteritems()]) def _load(self): file = self._File(encode_filename(self.filename)) + metadata = Metadata() if file.tags: for origname, values in file.tags.items(): for value in values: @@ -62,7 +71,8 @@ class APEv2File(File): name = self.__translate[name] else: name = name.lower() - self.metadata.add(name, value) + metadata.add(name, value) + self.metadata.update(metadata) self._info(file) def save(self): @@ -91,14 +101,14 @@ class APEv2File(File): value = '%s/%s' % (value, self.metadata['totaldiscs']) elif name in ('totaltracks', 'totaldiscs'): continue - elif name == "albumartist": - name = "Album Artist" # "performer:Piano=Joe Barr" => "Performer=Joe Barr (Piano)" elif name.startswith('performer:') or name.startswith('comment:'): name, desc = name.split(':', 1) name = name.title() if desc: value += ' (%s)' % desc + elif name in self.__rtranslate: + name = self.__rtranslate[name] else: name = name.title() temp.setdefault(name, []).append(value) @@ -128,7 +138,7 @@ class OptimFROGFile(APEv2File): """OptimFROG file.""" EXTENSIONS = [".ofr", ".ofs"] NAME = "OptimFROG" - _File = mutagenext.optimfrog.OptimFROG + _File = mutagen.optimfrog.OptimFROG def _info(self, file): super(OptimFROGFile, self)._info(file) if self.filename.lower().endswith(".ofs"): diff --git a/picard/formats/asf.py b/picard/formats/asf.py index 734c35e5c..42aba9809 100644 --- a/picard/formats/asf.py +++ b/picard/formats/asf.py @@ -19,7 +19,7 @@ from picard.file import File from picard.util import encode_filename -from picard.formats.mutagenext.asf import ASF +from mutagen.asf import ASF class ASFFile(File): """ASF (WMA) metadata reader/writer""" diff --git a/picard/formats/mutagenext/asf.py b/picard/formats/mutagenext/asf.py deleted file mode 100644 index 06d0a8608..000000000 --- a/picard/formats/mutagenext/asf.py +++ /dev/null @@ -1,636 +0,0 @@ -# -*- coding: utf-8 -*- -# ASF reader/tagger -# -# Copyright 2006 Lukáš Lalinský -# Copyright 2005-2006 Joe Wreschnig -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# $Id$ - -"""Read and write metadata to Window Media Audio files. -""" - -__all__ = ["ASF", "Open"] - -import struct -from mutagen import FileType, Metadata -from mutagen._util import insert_bytes, delete_bytes, DictMixin - -class error(IOError): pass -class ASFError(error): pass -class ASFHeaderError(error): pass - - -class ASFInfo(object): - """ASF stream information.""" - - def __init__(self): - self.length = 0.0 - self.sample_rate = 0 - self.bitrate = 0 - self.channels = 0 - - def pprint(self): - s = "Windows Media Audio %d bps, %s Hz, %d channels, %.2f seconds" % ( - self.bitrate, self.sample_rate, self.channels, self.length) - return s - - -class ASFTags(list, DictMixin): - """Dictionary containing ASF attributes.""" - - def pprint(self): - return "\n".join(["%s=%s" % (k, v) for k, v in self]) - - def __getitem__(self, key): - """A list of values for the key. - - This is a copy, so comment['title'].append('a title') will not - work. - - """ - values = [value for (k, value) in self if k == key] - if not values: raise KeyError, key - else: return values - - def __delitem__(self, key): - """Delete all values associated with the key.""" - to_delete = filter(lambda x: x[0] == key, self) - if not to_delete: raise KeyError, key - else: map(self.remove, to_delete) - - def __contains__(self, key): - """Return true if the key has any values.""" - for k, value in self: - if k == key: return True - else: return False - - def __setitem__(self, key, values): - """Set a key's value or values. - - Setting a value overwrites all old ones. The value may be a - list of Unicode or UTF-8 strings, or a single Unicode or UTF-8 - string. - - """ - if not isinstance(values, list): - values = [values] - try: del(self[key]) - except KeyError: pass - for value in values: - if key in _standard_attribute_names: - value = unicode(value) - elif not isinstance(value, ASFBaseAttribute): - if isinstance(value, unicode): - value = ASFUnicodeAttribute(value) - elif isinstance(value, bool): - value = ASFBoolAttribute(value) - elif isinstance(value, int): - value = ASFDWordAttribute(value) - elif isinstance(value, long): - value = ASFQWordAttribute(value) - self.append((key, value)) - - def keys(self): - """Return all keys in the comment.""" - return self and set(zip(*self)[0]) - - def as_dict(self): - """Return a copy of the comment data in a real dict.""" - d = {} - for key, value in self: - d.setdefault(key, []).append(value) - return d - - -class ASFBaseAttribute(object): - """Generic attribute.""" - TYPE = None - - def __init__(self, value=None, data=None, language=None, - stream=None, **kwargs): - self.language = language - self.stream = stream - if data: - self.value = self.parse(data, **kwargs) - else: - self.value = value - - def __repr__(self): - name = "%s(%r" % (type(self).__name__, self.value) - if self.language: - name += ", language=%d" % self.language - if self.stream: - name += ", stream=%d" % self.stream - name += ")" - return name - - def render(self, name): - name = name.encode("utf-16-le") + "\x00\x00" - data = self._render() - return (struct.pack(" self.size: - insert_bytes(fileobj, size - self.size, self.size) - if size < self.size: - delete_bytes(fileobj, self.size - size, 0) - fileobj.seek(0) - fileobj.write(data) - finally: - fileobj.close() - - def __read_file(self, fileobj): - header = fileobj.read(30) - if len(header) != 30 or header[:16] != HeaderObject.GUID: - raise ASFHeaderError, "Not an ASF file." - - self.extended_content_description_obj = None - self.content_description_obj = None - self.header_extension_obj = None - self.metadata_obj = None - self.metadata_library_obj = None - - self.size, self.num_objects = struct.unpack(" -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# $Id$ - -"""OptimFROG audio streams with APEv2 tags. - -OptimFROG is a lossless audio compression program. Its main goal is to -reduce at maximum the size of audio files, while permitting bit -identical restoration for all input. It is similar with the ZIP -compression, but it is highly specialized to compress audio data. - -For more information, see http://www.losslessaudio.org/ -""" - -__all__ = ["OptimFROG", "Open", "delete"] - -import struct -from mutagen.apev2 import APEv2File, error - -class OptimFROGHeaderError(error): pass - -class OptimFROGInfo(object): - """OptimFROG stream information. - - Attributes: - channels - number of audio channels - length - file length in seconds, as a float - sample_rate - audio sampling rate in Hz - bitrate -- audio bitrate, in bits per second - """ - - def __init__(self, fileobj): - header = fileobj.read(76) - if (len(header) != 76 or not header.startswith("OFR ") or - struct.unpack("