From b87f718aede176f178a8deee2c653f8fd010d99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 24 Feb 2007 08:53:37 +0100 Subject: [PATCH] Removed old code from mutagenext (now in mutagen). Added test for APEv2 files. --- picard/formats/apev2.py | 20 +- picard/formats/asf.py | 2 +- picard/formats/mutagenext/asf.py | 636 ------------------------- picard/formats/mutagenext/optimfrog.py | 63 --- test/data/test.wv | Bin 0 -> 2478 bytes test/test_formats.py | 49 ++ 6 files changed, 65 insertions(+), 705 deletions(-) delete mode 100644 picard/formats/mutagenext/asf.py delete mode 100644 picard/formats/mutagenext/optimfrog.py create mode 100644 test/data/test.wv 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("z)BcMP}Q$qNR*>Yxn;1{pa&M?`yAC z9qJ1MfC>YkVS)eem!|MPf~A|x_e4>}`C4syCIGutyK>84s=?5o4GEBe<}CowcgO$+ zC@`1Ql%PKwHMDzo-Yy-0D7)p6yszgN|HZOl_-9N8KOOv$LM^6DKxbe8pMe0_PyXf1 z^#%;Y{_OF8K@tZ7I5Mn#auxjq&U>eERh7$sG*Y}`2}bL^lKL+w1N-i{n@_M>jno$F zg#5m7mT%S3de=NxW5j81mdlsq_22BI29QY5OeEUj>M=@wgG6<@$>2Gsni602Lp*jf z&Ye+}k+C1sNz#MQIH(Ktqp|C~7${?biP1T%7Tm6g_mlXp1Rs9++8#3}kT}X_B^P_U zmq6(?L3O~BfG1Jb7(y7OIDmA+ZhMsD5O^O}S;IH|;KkjDY}+Xv;Yb_T^e3Gy8H_V; zm%yHwc2c`q{+{}nO>cXx3cNMZzGQF8B2Ru29*2-gwivY88v9RM5-*p4Q56}2mVvb_ zhTq}&0I(H)0;X3om}$oO?%=7iFKd^{{h$cEQb%s{KUNmrGiyeg`VD$2DavU@iq;ar z{P?x`bGWb}OeQrhmyp^?++OOh#VcX8Ach*#uQwd46>an1|LV2FYhZ1>oDeAX+VGGM zM7J1j>By3(4wWn-liGZF;Hj6RMSlAQVV@Zx$44+0B`4MPRbN^=DC7|rISRXz|6ZDl{(b)6{=PfxO1 zq(Nv9>`Lj%S*-6JRg|N1EKeheK@k$ib%Du)ecbpEU*~Gbggz!F)4ndL&*3bXW@IMU z3TaDm*xQC=HLXbqb#2MLI-1WtYS2e-k~wZHSdu(6(pMRw2(C~n0C5y9V{3Gxgm^E0 zQYtOmh}3!|<8FByM6@|Bnrdg+yhN4UpQb^5z8fST2#_4^`qr)U6(?j%m?oVWAvGB==dQAkRrk{ztG@Le5UKg2Z3gINfjrx%sX2JeK zELJy;uBY9EBuC&Jn9j~->~2mDwcqkP$p83>jyU(kK6PZ!uNrqwIK6)jSu_-}Aa@Gf z{1n=rJ@J=<0*3e|i8?+ZF{=LjSxyD{WBTWjsZabc-#;UMeK7BCmPqnC2Ev-<2F2e? z;<IoH^m^zF7W>4&72b$5Gzhk0o`Z3sWE7m4it9!i0Fc0G{qk!Apl zhTD3iDk81?lZZhlYePCKp=I{ymW*=qV3RzEsNpf41MJb^N5CSC7#t?re*K^wz}O-HLrN2^t@9d(e4cQ=o=Z^cd&2Q;DAhR_1C@_LhzrT6)?%@-J^=+=7*}mlAz`Kv;(Wp4 zx%^dQzY-*JCaoo=Ym4f9rjGCcVz z7rg5^OJTOyjFXj7d9-oNZhqiM7!vLHfO$M*;bl6aahKMn&ogyNo-dDCZNsxbx;%~% z*bj91kBiCqQj9bKNlF#5<9_-MWC-i7-Z2$WjT(K>=uF@ohu+F9)B^-Qqof`X=4*uW zqCjUOE0X@yhaA^QbxGE8_L|n_gNk9KakF1%Y4u}w%JhNi5Xia>kC_CovcpRW9Txk% zS=Toco>St!ioW}95UIkFvVTQ3zc$-MpxKLA(tV6cL(*T+nu!c=P4vVMr8thX>f>(+M3Z1;V;oYeF;FYA?u zU82N9Yo6jR<@{Nccd2k61s10|u>wX%z;1heqpBmTJlHed6M z4X%k=Q2V0Er|b)PTNXlN8jd7T0r`Ftp^OC=8fUW zI**b!dF=(Y+vEIGPfE1OTK=4J6Ev_O^K>$1i17H%xTWxTj$tabGAqw>!k`mvp zX3+d^v^YJqLl4X5l#)N%^`yviai&8yXT`iXb!Ted!x{v`7oe2AW;U@&Xc z<5z&Eah%A6$4>(^M7Ppk85sBc|r_)J<_MTY83&3US2 zYuH|$fy$ZIU577}dfA-2bJzsk_a#c0D8wbURdN9opIi)M6cGm)%te2vPdmZ-L6rU> z4mMv&&R0e0+GOL(XL}kf4>t1!-HRfUG@m0Ku|tlCBZKNeIZG^B%a!c?afggK=XKH) z^Rmem9MO|``~Cnq*>pAi@Co~~G`?RK7E{&v7{Z5rewBKR6v4P9klyxgOLtzCM62)Y zFc|UFu~BLHkR0At~y$!0Gwm~)83SPS(1@7aNe=MluT+*QSyUWXPqK# wyH6qPB)#n4HSEQ~H0KJRDpi#Utyxl;u*jG&8}