Hello community, here is the log from the commit of package python-mutagen for openSUSE:Factory checked in at 2019-01-08 12:16:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-mutagen (Old) and /work/SRC/openSUSE:Factory/.python-mutagen.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-mutagen" Tue Jan 8 12:16:38 2019 rev:32 rq:662312 version:1.42.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-mutagen/python-mutagen.changes 2018-12-27 00:27:17.359766297 +0100 +++ /work/SRC/openSUSE:Factory/.python-mutagen.new.28833/python-mutagen.changes 2019-01-08 12:16:39.593028066 +0100 @@ -1,0 +2,9 @@ +Fri Dec 28 03:28:33 UTC 2018 - s...@suspend.net + +- update to version 1.42.0: + * id3: always read id3v1 tags and include them when no id3v2 exists + * id3: add a pretty print implementation for SYLT + * vorbis: improved error messages when validating keys/values + * Fix pylint warnings when using various save() methods + +------------------------------------------------------------------- Old: ---- mutagen-1.41.0.tar.gz New: ---- mutagen-1.42.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-mutagen.spec ++++++ --- /var/tmp/diff_new_pack.JUy5aF/_old 2019-01-08 12:16:40.129027562 +0100 +++ /var/tmp/diff_new_pack.JUy5aF/_new 2019-01-08 12:16:40.133027558 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-mutagen # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-mutagen -Version: 1.41.0 +Version: 1.42.0 Release: 0 Summary: Python module to Handle Audio Metadata License: GPL-2.0-or-later ++++++ mutagen-1.41.0.tar.gz -> mutagen-1.42.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/NEWS new/mutagen-1.42.0/NEWS --- old/mutagen-1.41.0/NEWS 2018-07-16 21:32:50.000000000 +0200 +++ new/mutagen-1.42.0/NEWS 2018-12-26 14:59:06.000000000 +0100 @@ -1,3 +1,23 @@ +1.42.0 - 2018-12-26 +------------------- + +* ID3: Always read id3v1 tags and include them when no id3v2 equivalent + exists. Can be disabled with the new ``load_v1`` option, + see :meth:`id3.ID3.load` + :pr:`357` (:user:`Fredrik Strupe <frestr>`) +* ID3: Add a pretty print implementation for SYLT + :pr:`359` (:user:`Hamid Alaei Varnosfaderani <halaei>`) +* vorbis: Improved error messages when validating keys/values + :pr:`356` (:user:`Michael Booth <MJuddBooth>`) +* Fix pylint warnings when using the various ``save()`` methods :pr:`364` + + +1.41.1 - 2018-08-11 +------------------- + +* MP4: fix rtng, stik, shwm getting saved as 16bit ints instead of 8bit :bug:`349` + + 1.41.0 - 2018-07-15 ------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/PKG-INFO new/mutagen-1.42.0/PKG-INFO --- old/mutagen-1.41.0/PKG-INFO 2018-07-16 21:33:14.000000000 +0200 +++ new/mutagen-1.42.0/PKG-INFO 2018-12-26 14:59:37.000000000 +0100 @@ -1,11 +1,11 @@ Metadata-Version: 1.1 Name: mutagen -Version: 1.41.0 +Version: 1.42.0 Summary: read and write audio tags for many formats Home-page: https://github.com/quodlibet/mutagen Author: Michael Urman Author-email: quod-libet-developm...@groups.google.com -License: GNU GPL v2 +License: GPL-2.0-or-later Description: .. image:: https://cdn.rawgit.com/quodlibet/mutagen/master/docs/images/logo.svg :align: center :width: 400px @@ -29,8 +29,8 @@ .. image:: https://travis-ci.org/quodlibet/mutagen.svg?branch=master :target: https://travis-ci.org/quodlibet/mutagen - .. image:: https://ci.appveyor.com/api/projects/status/d22bslvjvt3r1hv1/branch/master?svg=true - :target: https://ci.appveyor.com/project/lazka/mutagen/branch/master + .. image:: https://dev.azure.com/quodlibet/mutagen/_apis/build/status/quodlibet.mutagen + :target: https://dev.azure.com/quodlibet/mutagen/_build/latest?definitionId=3 .. image:: https://codecov.io/gh/quodlibet/mutagen/branch/master/graph/badge.svg :target: https://codecov.io/gh/quodlibet/mutagen @@ -46,5 +46,5 @@ Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2) +Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Classifier: Topic :: Multimedia :: Sound/Audio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/README.rst new/mutagen-1.42.0/README.rst --- old/mutagen-1.41.0/README.rst 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/README.rst 2018-11-17 13:12:22.000000000 +0100 @@ -21,8 +21,8 @@ .. image:: https://travis-ci.org/quodlibet/mutagen.svg?branch=master :target: https://travis-ci.org/quodlibet/mutagen -.. image:: https://ci.appveyor.com/api/projects/status/d22bslvjvt3r1hv1/branch/master?svg=true - :target: https://ci.appveyor.com/project/lazka/mutagen/branch/master +.. image:: https://dev.azure.com/quodlibet/mutagen/_apis/build/status/quodlibet.mutagen + :target: https://dev.azure.com/quodlibet/mutagen/_build/latest?definitionId=3 .. image:: https://codecov.io/gh/quodlibet/mutagen/branch/master/graph/badge.svg :target: https://codecov.io/gh/quodlibet/mutagen diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/docs/Makefile new/mutagen-1.42.0/docs/Makefile --- old/mutagen-1.41.0/docs/Makefile 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/docs/Makefile 2018-11-08 18:50:38.000000000 +0100 @@ -1,5 +1,5 @@ all: - python -m sphinx -b html -n . _build + python3 -m sphinx -b html -n . _build clean: rm -rf _build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/docs/conf.py new/mutagen-1.42.0/docs/conf.py --- old/mutagen-1.41.0/docs/conf.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/docs/conf.py 2018-11-08 18:50:38.000000000 +0100 @@ -16,8 +16,8 @@ 'sphinx.ext.extlinks', ] intersphinx_mapping = { - 'python': ('https://docs.python.org/2.7', None), - 'python3': ('https://docs.python.org/3.5', None), + 'python': ('https://docs.python.org/2', None), + 'python3': ('https://docs.python.org/3', None), } source_suffix = '.rst' master_doc = 'index' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/docs/extra.css new/mutagen-1.42.0/docs/extra.css --- old/mutagen-1.41.0/docs/extra.css 2017-05-25 15:49:20.000000000 +0200 +++ new/mutagen-1.42.0/docs/extra.css 2018-12-26 14:47:44.000000000 +0100 @@ -30,3 +30,7 @@ .rst-footer-buttons { display: none; } + +.versionmodified { + color: #008000; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/__init__.py new/mutagen-1.42.0/mutagen/__init__.py --- old/mutagen-1.41.0/mutagen/__init__.py 2018-07-16 21:32:50.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/__init__.py 2018-12-26 14:59:06.000000000 +0100 @@ -23,7 +23,7 @@ from mutagen._file import FileType, StreamInfo, File from mutagen._tags import Tags, Metadata, PaddingInfo -version = (1, 41, 0) +version = (1, 42, 0) """Version tuple.""" version_string = ".".join(map(str, version)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/_file.py new/mutagen-1.42.0/mutagen/_file.py --- old/mutagen-1.41.0/mutagen/_file.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/_file.py 2018-11-17 11:56:49.000000000 +0100 @@ -97,7 +97,7 @@ return self.tags.keys() @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Remove tags from a file. @@ -120,7 +120,7 @@ return self.tags.delete(filething) @loadfile(writable=True) - def save(self, filething, **kwargs): + def save(self, filething=None, **kwargs): """save(filething=None, **kwargs) Save metadata tags. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/_tags.py new/mutagen-1.42.0/mutagen/_tags.py --- old/mutagen-1.41.0/mutagen/_tags.py 2017-05-25 15:49:20.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/_tags.py 2018-11-17 11:56:49.000000000 +0100 @@ -115,7 +115,7 @@ raise NotImplementedError @loadfile(writable=False) - def save(self, filething, **kwargs): + def save(self, filething=None, **kwargs): """save(filething=None, **kwargs) Save changes to a file. @@ -129,7 +129,7 @@ raise NotImplementedError @loadfile(writable=False) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Remove tags from a file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/_tools/mid3v2.py new/mutagen-1.42.0/mutagen/_tools/mid3v2.py --- old/mutagen-1.41.0/mutagen/_tools/mid3v2.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/_tools/mid3v2.py 2018-12-16 14:47:44.000000000 +0100 @@ -11,6 +11,7 @@ import sys import codecs import mimetypes +import warnings from optparse import SUPPRESS_HELP @@ -137,7 +138,11 @@ if PY2: bytes_ = bytes_.decode("string_escape") else: - bytes_ = codecs.escape_decode(bytes_)[0] + # With py3.7 this has started to warn for invalid escapes, but we + # don't control the input so ignore it. + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + bytes_ = codecs.escape_decode(bytes_)[0] arg = bytes2fsn(bytes_) text = fsn2text(arg, strict=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/_vorbis.py new/mutagen-1.42.0/mutagen/_vorbis.py --- old/mutagen-1.41.0/mutagen/_vorbis.py 2017-05-25 15:49:20.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/_vorbis.py 2018-11-08 18:50:38.000000000 +0100 @@ -157,18 +157,20 @@ for key, value in self: try: if not is_valid_key(key): - raise ValueError + raise ValueError("%r is not a valid key" % key) except TypeError: raise ValueError("%r is not a valid key" % key) if not isinstance(value, text_type): if PY3: - raise ValueError("%r needs to be str" % key) + err = "%r needs to be str for key %r" % (value, key) + raise ValueError(err) try: value.decode("utf-8") except Exception: - raise ValueError("%r is not a valid value" % value) + err = "%r is not a valid value for key %r" % (value, key) + raise ValueError(err) return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/aiff.py new/mutagen-1.42.0/mutagen/aiff.py --- old/mutagen-1.41.0/mutagen/aiff.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/aiff.py 2018-11-17 11:56:49.000000000 +0100 @@ -271,7 +271,7 @@ @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, v2_version=4, v23_sep='/', padding=None): + def save(self, filething=None, v2_version=4, v23_sep='/', padding=None): """Save ID3v2 data to the AIFF file""" fileobj = filething.fileobj @@ -299,7 +299,7 @@ chunk.write(data) @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """Completely removes the ID3 chunk from the AIFF file""" delete(filething) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/apev2.py new/mutagen-1.42.0/mutagen/apev2.py --- old/mutagen-1.41.0/mutagen/apev2.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/apev2.py 2018-11-17 11:56:49.000000000 +0100 @@ -419,7 +419,7 @@ @convert_error(IOError, error) @loadfile(writable=True, create=True) - def save(self, filething): + def save(self, filething=None): """Save changes to a file. If no filename is given, the one most recently loaded is used. @@ -481,7 +481,7 @@ @convert_error(IOError, error) @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """Remove tags from a file.""" fileobj = filething.fileobj diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/asf/__init__.py new/mutagen-1.42.0/mutagen/asf/__init__.py --- old/mutagen-1.41.0/mutagen/asf/__init__.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/asf/__init__.py 2018-11-17 11:56:49.000000000 +0100 @@ -252,7 +252,7 @@ @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, padding=None): + def save(self, filething=None, padding=None): """save(filething=None, padding=None) Save tag changes back to the loaded file. @@ -319,7 +319,7 @@ raise ASFError @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Args: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/dsf.py new/mutagen-1.42.0/mutagen/dsf.py --- old/mutagen-1.41.0/mutagen/dsf.py 2017-05-25 15:49:20.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/dsf.py 2018-11-17 11:56:49.000000000 +0100 @@ -199,7 +199,7 @@ @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, v2_version=4, v23_sep='/', padding=None): + def save(self, filething=None, v2_version=4, v23_sep='/', padding=None): """Save ID3v2 data to the DSF file""" fileobj = filething.fileobj @@ -328,7 +328,7 @@ self.info = DSFInfo(dsf_file.fmt_chunk) @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): self.tags = None delete(filething) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/easyid3.py new/mutagen-1.42.0/mutagen/easyid3.py --- old/mutagen-1.41.0/mutagen/easyid3.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/easyid3.py 2018-11-17 11:56:49.000000000 +0100 @@ -173,7 +173,8 @@ lambda s, v: setattr(s.__id3, 'load', v)) @loadfile(writable=True, create=True) - def save(self, filething, v1=1, v2_version=4, v23_sep='/', padding=None): + def save(self, filething=None, v1=1, v2_version=4, v23_sep='/', + padding=None): """save(filething=None, v1=1, v2_version=4, v23_sep='/', padding=None) Save changes to a file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/flac.py new/mutagen-1.42.0/mutagen/flac.py --- old/mutagen-1.41.0/mutagen/flac.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/flac.py 2018-11-17 11:56:49.000000000 +0100 @@ -394,7 +394,7 @@ isrc (`mutagen.text`): ISRC code, exactly 12 characters type (`int`): 0 for audio, 1 for digital data pre_emphasis (`bool`): true if the track is recorded with pre-emphasis - indexes (List[`mutagen.flac.CueSheetTrackIndex`]): + indexes (list[CueSheetTrackIndex]): list of CueSheetTrackIndex objects """ @@ -442,9 +442,9 @@ lead_in_samples (`int`): number of lead-in samples compact_disc (`bool`): true if the cuesheet corresponds to a compact disc - tracks (List[`mutagen.flac.CueSheetTrack`]): + tracks (list[CueSheetTrack]): list of CueSheetTrack objects - lead_out (`mutagen.flac.CueSheetTrack` or `None`): + lead_out (`CueSheetTrack` or `None`): lead-out as CueSheetTrack or None if lead-out was not found """ @@ -678,7 +678,7 @@ Attributes: cuesheet (`CueSheet`): if any or `None` seektable (`SeekTable`): if any or `None` - pictures (List[`Picture`]): list of embedded pictures + pictures (list[Picture]): list of embedded pictures info (`StreamInfo`) tags (`mutagen._vorbis.VCommentDict`) """ @@ -757,7 +757,7 @@ add_vorbiscomment = add_tags @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """Remove Vorbis comments from a file. If no filename is given, the one most recently loaded is used. @@ -823,16 +823,13 @@ @property def pictures(self): - """ - Returns: - List[`Picture`]: List of embedded pictures - """ + """list[Picture]: List of embedded pictures""" return [b for b in self.metadata_blocks if b.code == Picture.code] @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, deleteid3=False, padding=None): + def save(self, filething=None, deleteid3=False, padding=None): """Save metadata blocks to a file. Args: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/id3/_file.py new/mutagen-1.42.0/mutagen/id3/_file.py --- old/mutagen-1.41.0/mutagen/id3/_file.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/id3/_file.py 2018-12-26 14:46:04.000000000 +0100 @@ -53,8 +53,8 @@ filething (filething): or `None` Attributes: - version (Tuple[int]): ID3 tag version as a tuple - unknown_frames (List[bytes]): raw frame data of any unknown frames + version (tuple[int]): ID3 tag version as a tuple + unknown_frames (list[bytes]): raw frame data of any unknown frames found size (int): the total size of the ID3 tag, including the header """ @@ -112,10 +112,9 @@ @convert_error(IOError, error) @loadfile() - def load(self, filething, known_frames=None, translate=True, v2_version=4): - """load(filething, known_frames=None, translate=True, v2_version=4) - - Load tags from a filename. + def load(self, filething, known_frames=None, translate=True, v2_version=4, + load_v1=True): + """Load tags from a filename. Args: filename (filething): filename or file object to load tag data from @@ -126,6 +125,11 @@ call update_to_v23() / update_to_v24() manually. v2_version (int): if update_to_v23 or update_to_v24 get called (3 or 4) + load_v1 (bool): Load tags from ID3v1 header if present. If both + ID3v1 and ID3v2 headers are present, combine the tags from + the two, with ID3v2 having precedence. + + .. versionadded:: 1.42 Example of loading a custom frame:: @@ -149,13 +153,17 @@ try: self._header = ID3Header(fileobj) except (ID3NoHeaderError, ID3UnsupportedVersionError): - frames, offset = find_id3v1(fileobj) + if not load_v1: + raise + + frames, offset = find_id3v1(fileobj, v2_version, known_frames) if frames is None: raise self.version = ID3Header._V11 for v in frames.values(): - self.add(v) + if len(self.getall(v.HashKey)) == 0: + self.add(v) else: # XXX: attach to the header object so we have it in spec parsing.. if known_frames is not None: @@ -165,6 +173,14 @@ remaining_data = self._read(self._header, data) self._padding = len(remaining_data) + if load_v1: + v1v2_ver = 4 if self.version[1] == 4 else 3 + frames, offset = find_id3v1(fileobj, v1v2_ver, known_frames) + if frames: + for v in frames.values(): + if len(self.getall(v.HashKey)) == 0: + self.add(v) + if translate: if v2_version == 3: self.update_to_v23() @@ -204,13 +220,14 @@ @convert_error(IOError, error) @loadfile(writable=True, create=True) - def save(self, filething, v1=1, v2_version=4, v23_sep='/', padding=None): + def save(self, filething=None, v1=1, v2_version=4, v23_sep='/', + padding=None): """save(filething=None, v1=1, v2_version=4, v23_sep='/', padding=None) Save changes to a file. Args: - filename (fspath): + filething (filething): Filename to save the tag to. If no filename is given, the one most recently loaded is used. v1 (ID3v1SaveOptions): @@ -268,7 +285,7 @@ f.truncate() @loadfile(writable=True) - def delete(self, filething, delete_v1=True, delete_v2=True): + def delete(self, filething=None, delete_v1=True, delete_v2=True): """delete(filething=None, delete_v1=True, delete_v2=True) Remove tags from a file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/id3/_frames.py new/mutagen-1.42.0/mutagen/id3/_frames.py --- old/mutagen-1.41.0/mutagen/id3/_frames.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/id3/_frames.py 2018-11-08 18:50:38.000000000 +0100 @@ -1095,13 +1095,18 @@ def HashKey(self): return '%s:%s:%s' % (self.FrameID, self.desc, self.lang) + def _pprint(self): + return str(self) + def __eq__(self, other): return str(self) == other __hash__ = Frame.__hash__ def __str__(self): - return u"".join(text for (text, time) in self.text) + unit = 'fr' if self.format == 1 else 'ms' + return u"\n".join("[{0}{1}]: {2}".format(time, unit, text) + for (text, time) in self.text) def __bytes__(self): return text_type(self).encode("utf-8") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/id3/_id3v1.py new/mutagen-1.42.0/mutagen/id3/_id3v1.py --- old/mutagen-1.41.0/mutagen/id3/_id3v1.py 2017-05-25 15:49:20.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/id3/_id3v1.py 2018-11-08 18:50:38.000000000 +0100 @@ -13,20 +13,30 @@ from mutagen._util import chr_, text_type -from ._frames import TCON, TRCK, COMM, TDRC, TALB, TPE1, TIT2 +from ._frames import TCON, TRCK, COMM, TDRC, TYER, TALB, TPE1, TIT2 -def find_id3v1(fileobj): +def find_id3v1(fileobj, v2_version=4, known_frames=None): """Returns a tuple of (id3tag, offset_to_end) or (None, 0) offset mainly because we used to write too short tags in some cases and we need the offset to delete them. + + v2_version: Decides whether ID3v2.3 or ID3v2.4 tags + should be returned. Must be 3 or 4. + + known_frames (Dict[`mutagen.text`, `Frame`]): dict mapping frame + IDs to Frame objects """ + if v2_version not in (3, 4): + raise ValueError("Only 3 and 4 possible for v2_version") + # id3v1 is always at the end (after apev2) extra_read = b"APETAGEX".index(b"TAG") + old_pos = fileobj.tell() try: fileobj.seek(-128 - extra_read, 2) except IOError as e: @@ -38,6 +48,7 @@ raise data = fileobj.read(128 + extra_read) + fileobj.seek(old_pos, 0) try: idx = data.index(b"TAG") except ValueError: @@ -53,7 +64,7 @@ if idx == ape_idx + extra_read: return (None, 0) - tag = ParseID3v1(data[idx:]) + tag = ParseID3v1(data[idx:], v2_version, known_frames) if tag is None: return (None, 0) @@ -62,12 +73,21 @@ # ID3v1.1 support. -def ParseID3v1(data): - """Parse an ID3v1 tag, returning a list of ID3v2.4 frames. +def ParseID3v1(data, v2_version=4, known_frames=None): + """Parse an ID3v1 tag, returning a list of ID3v2 frames Returns a {frame_name: frame} dict or None. + + v2_version: Decides whether ID3v2.3 or ID3v2.4 tags + should be returned. Must be 3 or 4. + + known_frames (Dict[`mutagen.text`, `Frame`]): dict mapping frame + IDs to Frame objects """ + if v2_version not in (3, 4): + raise ValueError("Only 3 and 4 possible for v2_version") + try: data = data[data.index(b"TAG"):] except ValueError: @@ -97,23 +117,45 @@ title, artist, album, year, comment = map( fix, [title, artist, album, year, comment]) + frame_class = { + "TIT2": TIT2, + "TPE1": TPE1, + "TALB": TALB, + "TYER": TYER, + "TDRC": TDRC, + "COMM": COMM, + "TRCK": TRCK, + "TCON": TCON, + } + for key in frame_class: + if known_frames is not None: + if key in known_frames: + frame_class[key] = known_frames[key] + else: + frame_class[key] = None + frames = {} - if title: - frames["TIT2"] = TIT2(encoding=0, text=title) - if artist: - frames["TPE1"] = TPE1(encoding=0, text=[artist]) - if album: - frames["TALB"] = TALB(encoding=0, text=album) + if title and frame_class["TIT2"]: + frames["TIT2"] = frame_class["TIT2"](encoding=0, text=title) + if artist and frame_class["TPE1"]: + frames["TPE1"] = frame_class["TPE1"](encoding=0, text=[artist]) + if album and frame_class["TALB"]: + frames["TALB"] = frame_class["TALB"](encoding=0, text=album) if year: - frames["TDRC"] = TDRC(encoding=0, text=year) - if comment: - frames["COMM"] = COMM( - encoding=0, lang="eng", desc="ID3v1 Comment", text=comment) + if v2_version == 3 and frame_class["TYER"]: + frames["TYER"] = frame_class["TYER"](encoding=0, text=year) + elif frame_class["TDRC"]: + frames["TDRC"] = frame_class["TDRC"](encoding=0, text=year) + if comment and frame_class["COMM"]: + frames["COMM"] = frame_class["COMM"]( + encoding=0, lang="eng", desc="ID3v1 Comment", text=comment) + # Don't read a track number if it looks like the comment was # padded with spaces instead of nulls (thanks, WinAmp). - if track and ((track != 32) or (data[-3] == b'\x00'[0])): + if (track and frame_class["TRCK"] and + ((track != 32) or (data[-3] == b'\x00'[0]))): frames["TRCK"] = TRCK(encoding=0, text=str(track)) - if genre != 255: + if genre != 255 and frame_class["TCON"]: frames["TCON"] = TCON(encoding=0, text=str(genre)) return frames diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/id3/_tags.py new/mutagen-1.42.0/mutagen/id3/_tags.py --- old/mutagen-1.41.0/mutagen/id3/_tags.py 2017-05-25 15:49:20.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/id3/_tags.py 2018-11-23 12:13:22.000000000 +0100 @@ -243,7 +243,7 @@ Args: key (text): key for frames to delete - values (List[`Frame`]): frames to add + values (list[Frame]): frames to add """ self.delall(key) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/mp4/__init__.py new/mutagen-1.42.0/mutagen/mp4/__init__.py --- old/mutagen-1.41.0/mutagen/mp4/__init__.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/mp4/__init__.py 2018-12-24 12:30:05.000000000 +0100 @@ -392,7 +392,7 @@ @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, padding=None): + def save(self, filething=None, padding=None): values = [] items = sorted(self.items(), key=lambda kv: _item_sort_key(*kv)) @@ -716,7 +716,8 @@ # by itunes for compatibility. if cdata.int8_min <= v <= cdata.int8_max and min_bytes <= 1: data = cdata.to_int8(v) - if cdata.int16_min <= v <= cdata.int16_max and min_bytes <= 2: + elif cdata.int16_min <= v <= cdata.int16_max and \ + min_bytes <= 2: data = cdata.to_int16_be(v) elif cdata.int32_min <= v <= cdata.int32_max and \ min_bytes <= 4: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/mutagen/ogg.py new/mutagen-1.42.0/mutagen/ogg.py --- old/mutagen-1.41.0/mutagen/ogg.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/mutagen/ogg.py 2018-11-17 11:56:49.000000000 +0100 @@ -50,7 +50,7 @@ offset (`int` or `None`): offset this page was read from (default None) complete (`bool`): if the last packet on this page is complete (default True) - packets (List[`bytes`]): list of raw packet data (default []) + packets (list[bytes]): list of raw packet data (default []) Note that if 'complete' is false, the next page's 'continued' property must be true (so set both when constructing pages). @@ -535,7 +535,7 @@ raise self._Error("no appropriate stream found") @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Remove tags from a file. @@ -567,7 +567,7 @@ raise self._Error @loadfile(writable=True) - def save(self, filething, padding=None): + def save(self, filething=None, padding=None): """save(filething=None, padding=None) Save a tag to a file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/setup.py new/mutagen-1.42.0/setup.py --- old/mutagen-1.41.0/setup.py 2018-07-16 21:00:52.000000000 +0200 +++ new/mutagen-1.42.0/setup.py 2018-12-16 14:42:15.000000000 +0100 @@ -81,7 +81,7 @@ tracked_files = out.splitlines() for ignore in [".travis.yml", ".gitignore", ".codecov.yml", - ".appveyor.yml"]: + "azure-pipelines.yml"]: tracked_files.remove(ignore) diff = set(tracked_files) - set(included_files) @@ -258,7 +258,7 @@ description="read and write audio tags for many formats", author="Michael Urman", author_email="quod-libet-developm...@groups.google.com", - license="GNU GPL v2", + license="GPL-2.0-or-later", classifiers=[ 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', @@ -270,7 +270,8 @@ 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', - 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', + ('License :: OSI Approved :: ' + 'GNU General Public License v2 or later (GPLv2+)'), 'Topic :: Multimedia :: Sound/Audio', ], packages=[ Binary files old/mutagen-1.41.0/tests/data/id3v1v2-combined.mp3 and new/mutagen-1.42.0/tests/data/id3v1v2-combined.mp3 differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/tests/test__id3frames.py new/mutagen-1.42.0/tests/test__id3frames.py --- old/mutagen-1.41.0/tests/test__id3frames.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/tests/test__id3frames.py 2018-11-08 18:50:38.000000000 +0100 @@ -181,7 +181,8 @@ ], [ 'SYLT', (b'\x00eng\x02\x01some lyrics\x00foo\x00\x00\x00\x00\x01' - b'bar\x00\x00\x00\x00\x10'), "foobar", '', + b'bar\x00\x00\x00\x00\x10'), + "[1ms]: foo\n[16ms]: bar", '', dict(encoding=0, lang='eng', type=1, format=2, desc='some lyrics') ], ['POSS', b'\x01\x0f', 15, 15, dict(format=1, position=15)], @@ -330,7 +331,7 @@ [ 'SLT', (b'\x00eng\x02\x01some lyrics\x00foo\x00\x00\x00\x00\x01bar' b'\x00\x00\x00\x00\x10'), - "foobar", '', + "[1ms]: foo\n[16ms]: bar", '', dict(encoding=0, lang='eng', type=1, format=2, desc='some lyrics') ], ['TT1', b'\x00ab\x00', 'ab', '', dict(encoding=0)], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/tests/test_id3.py new/mutagen-1.42.0/tests/test_id3.py --- old/mutagen-1.41.0/tests/test_id3.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/tests/test_id3.py 2018-11-08 18:50:38.000000000 +0100 @@ -30,9 +30,11 @@ empty = os.path.join(DATA_DIR, 'emptyfile.mp3') silence = os.path.join(DATA_DIR, 'silence-44-s.mp3') + silence_v1 = os.path.join(DATA_DIR, 'silence-44-s-v1.mp3') unsynch = os.path.join(DATA_DIR, 'id3v23_unsynch.id3') v22 = os.path.join(DATA_DIR, "id3v22-test.mp3") bad_tyer = os.path.join(DATA_DIR, 'bad-TYER-frame.mp3') + v1v2_combined = os.path.join(DATA_DIR, "id3v1v2-combined.mp3") def test_PIC_in_23(self): filename = get_temp_empty(".mp3") @@ -112,6 +114,95 @@ self.failUnless(tags["TRCK"].text == ["3/11"]) self.failUnless(tags["TPE1"].text == ["Anais Mitchell"]) + def test_load_v1(self): + tags = ID3(self.silence_v1) + self.assertEquals(tags["TALB"], "Quod Libet Test Data") + + with self.assertRaises(ID3NoHeaderError): + tags = ID3(self.silence_v1, load_v1=False) + + def test_load_v1_v2(self): + tags = ID3(self.v1v2_combined) + # From ID3v2 + self.assertEquals(tags["TPE1"].text, ["Anais Mitchell"]) + # From ID3v1 + self.assertEquals(tags["TALB"].text, ["Hymns for the Exiled"]) + + tags = ID3(self.v1v2_combined, load_v1=False) + self.assertEquals(tags["TPE1"].text, ["Anais Mitchell"]) + with self.assertRaises(KeyError): + tags["TALB"] + + def test_load_v1_v2_no_translate(self): + tags = ID3(self.v1v2_combined, v2_version=4, translate=False) + assert tags.version == (2, 4, 0) + assert str(tags["TDRC"].text[0]) == "1337" + tags = ID3(self.v1v2_combined, v2_version=3, translate=False) + assert tags.version == (2, 4, 0) + assert str(tags["TDRC"].text[0]) == "1337" + + def test_load_v1_v2_tcon_translate(self): + tags = ID3() + tags.add(TCON(text=["12"])) + v1_data = MakeID3v1(tags) + + filename = get_temp_copy(self.empty) + try: + tags = ID3() + tags.save(filename=filename, v1=0) + with open(filename, "ab") as h: + h.write(v1_data) + tags = ID3(filename, load_v1=True) + assert tags["TCON"][0] == "Other" + tags = ID3(filename, load_v1=False) + assert "TCON" not in tags + finally: + os.unlink(filename) + + def test_load_v1_v2_precedence(self): + tags = ID3(self.v1v2_combined) + self.assertEquals(tags["TRCK"].text, ["3/11"]) # i.e. not 123 + + # ID3v2 has TYER=2004 (which isn't a valid v2.4 frame), + # ID3v1 has TDRC=1337. + self.assertEquals(str(tags["TDRC"].text[0]), "1337") + with self.assertRaises(KeyError): + tags["TYER"] + + tags = ID3(self.v1v2_combined, v2_version=3) + + # With v2_version=3, the ID3v2 tag should still have precedence + self.assertEquals(str(tags["TYER"].text[0]), "2004") + with self.assertRaises(KeyError): + tags["TDRC"] + + def test_load_v1_comment(self): + # Tags with different HashKeys but equal FrameIDs (like COMM) + # should be kept separate + tags = ID3(self.v1v2_combined) + comments = tags.getall("COMM") + # From ID3v2 + self.failUnless("Waterbug Records, www.anaismitchell.com" in comments) + # From ID3v1 + self.failUnless("v1 comment" in comments) + + def test_load_v1_known_frames_override(self): + class MyCOMM(COMM): + @property + def FrameID(self): + # We want to replace the existing COMM, so override + # the FrameID + return COMM.__name__ + + frames = dict(id3.Frames) + frames["COMM"] = MyCOMM + tags = ID3(self.v1v2_combined, known_frames=frames) + + comments = tags.getall("COMM") + self.failUnless(len(comments) > 0) + for comm in comments: + self.assertIsInstance(comm, MyCOMM) + def test_empty_file(self): self.assertRaises(ID3Error, ID3, filename=self.empty) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mutagen-1.41.0/tests/test_mp4.py new/mutagen-1.42.0/tests/test_mp4.py --- old/mutagen-1.41.0/tests/test_mp4.py 2018-07-08 23:33:34.000000000 +0200 +++ new/mutagen-1.42.0/tests/test_mp4.py 2018-08-11 14:06:18.000000000 +0200 @@ -272,6 +272,17 @@ b"\x00\x00\x00\x15\x00\x00\x00\x00\x00" ) + def test_render_integer_min_size(self): + render_int = MP4Tags()._MP4Tags__render_integer + + data = render_int('stik', [42], 1) + tags = self.wrap_ilst(data) + assert tags['stik'] == [42] + + assert len(render_int('stik', [42], 2)) == len(data) + 1 + assert len(render_int('stik', [42], 4)) == len(data) + 3 + assert len(render_int('stik', [42], 8)) == len(data) + 7 + def test_render_text(self): self.failUnlessEqual( MP4Tags()._MP4Tags__render_text(