From 6f0261e5ff831dc9e15d9995c2e5ae52b7edd738 Mon Sep 17 00:00:00 2001 From: Rowan Lewis Date: Thu, 12 Sep 2013 07:17:32 +1000 Subject: [PATCH 01/10] Added opus support, maybe. --- beets/mediafile.py | 11 ++++++++--- test/rsrc/full.opus | Bin 0 -> 7902 bytes test/test_mediafile_basic.py | 20 ++++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 test/rsrc/full.opus diff --git a/beets/mediafile.py b/beets/mediafile.py index 9b6234192..8b2fc046d 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -30,6 +30,7 @@ if no tag is present. If no value is available, the value will be false """ import mutagen import mutagen.mp3 +import mutagen.oggopus import mutagen.oggvorbis import mutagen.mp4 import mutagen.flac @@ -72,6 +73,7 @@ TYPES = { 'aac': 'AAC', 'alac': 'ALAC', 'ogg': 'OGG', + 'opus': 'Opus', 'flac': 'FLAC', 'ape': 'APE', 'wv': 'WavPack', @@ -283,7 +285,7 @@ class StorageStyle(object): - id3_lang: set the language field of the frame object. """ def __init__(self, key, list_elem=True, as_type=unicode, - packing=None, pack_pos=0, pack_type=int, + packing=None, pack_pos=0, pack_type=int, id3_desc=None, id3_frame_field='text', id3_lang=None, suffix=None, float_places=2): self.key = key @@ -741,7 +743,7 @@ class ImageField(object): return pictures[0].data or None else: return None - + elif obj.type == 'asf': if 'WM/Picture' in obj.mgfile: pictures = obj.mgfile['WM/Picture'] @@ -862,6 +864,7 @@ class MediaFile(object): mutagen.flac.error, mutagen.monkeysaudio.MonkeysAudioHeaderError, mutagen.mp4.error, + mutagen.oggopus.error, mutagen.oggvorbis.error, mutagen.ogg.error, mutagen.asf.error, @@ -904,6 +907,8 @@ class MediaFile(object): self.type = 'mp3' elif type(self.mgfile).__name__ == 'FLAC': self.type = 'flac' + elif type(self.mgfile).__name__ == 'OggOpus': + self.type = 'opus' elif type(self.mgfile).__name__ == 'OggVorbis': self.type = 'ogg' elif type(self.mgfile).__name__ == 'MonkeysAudio': @@ -1262,7 +1267,7 @@ class MediaFile(object): packing=packing.SC, pack_pos=0, pack_type=float)], mp4 = [StorageStyle('----:com.apple.iTunes:replaygain_track_gain', as_type=str, float_places=2, suffix=b' dB'), - StorageStyle('----:com.apple.iTunes:iTunNORM', + StorageStyle('----:com.apple.iTunes:iTunNORM', packing=packing.SC, pack_pos=0, pack_type=float)], etc = StorageStyle(u'REPLAYGAIN_TRACK_GAIN', float_places=2, suffix=u' dB'), diff --git a/test/rsrc/full.opus b/test/rsrc/full.opus new file mode 100644 index 0000000000000000000000000000000000000000..81cd652841bf2d54c37496bc3c33f3a2f5d31d70 GIT binary patch literal 7902 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 BkAeUI literal 0 HcmV?d00001 diff --git a/test/test_mediafile_basic.py b/test/test_mediafile_basic.py index 91f663556..4d7a941e0 100644 --- a/test/test_mediafile_basic.py +++ b/test/test_mediafile_basic.py @@ -173,6 +173,15 @@ READ_ONLY_CORRECT_DICTS = { 'channels': 1, }, + 'full.opus': { + 'length': 1.0, + 'bitrate': 64000, + 'format': 'Opus', + 'samplerate': 48000, + 'bitdepth': 0, + 'channels': 1, + }, + 'full.ape': { 'length': 1.0, 'bitrate': 112040, @@ -224,6 +233,7 @@ TEST_FILES = { 'mp3': ['full', 'partial', 'min'], 'flac': ['full', 'partial', 'min'], 'ogg': ['full'], + 'opus': ['full'], 'ape': ['full'], 'wv': ['full'], 'mpc': ['full'], @@ -264,6 +274,9 @@ class AllFilesMixin(object): def test_ogg(self): self._run('full', 'ogg') + def test_opus(self): + self._run('full', 'opus') + def test_ape(self): self._run('full', 'ape') @@ -293,7 +306,7 @@ class ReadingTest(unittest.TestCase, AllFilesMixin): self.assertAlmostEqual(got, correct, msg=message) else: self.assertEqual(got, correct, message) - + def _run(self, tagset, kind): correct_dict = CORRECT_DICTS[tagset] path = os.path.join(_common.RSRC, tagset + '.' + kind) @@ -389,7 +402,7 @@ class WritingTest(unittest.TestCase, AllFilesMixin): else: raise ValueError('unknown field type ' + \ str(type(correct_dict[field]))) - + # Make a copy of the file we'll work on. root, ext = os.path.splitext(path) tpath = root + '_test' + ext @@ -429,6 +442,9 @@ class ReadOnlyTest(unittest.TestCase): def test_ogg(self): self._run('full.ogg') + def test_opus(self): + self._run('full.opus') + def test_ape(self): self._run('full.ape') From be8325dfe550b296d859e7c9cb634ae8767d67df Mon Sep 17 00:00:00 2001 From: Rowan Lewis Date: Thu, 12 Sep 2013 08:00:28 +1000 Subject: [PATCH 02/10] Opus now passes basic tests. --- beets/mediafile.py | 3 +++ test/test_mediafile_basic.py | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/beets/mediafile.py b/beets/mediafile.py index 8b2fc046d..961564bac 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -775,6 +775,9 @@ class ImageField(object): else: return None + if pic.data == '': + return None + return pic.data def __set__(self, obj, val): diff --git a/test/test_mediafile_basic.py b/test/test_mediafile_basic.py index 4d7a941e0..3c1bf1e11 100644 --- a/test/test_mediafile_basic.py +++ b/test/test_mediafile_basic.py @@ -175,9 +175,8 @@ READ_ONLY_CORRECT_DICTS = { 'full.opus': { 'length': 1.0, - 'bitrate': 64000, + 'bitrate': 63216, 'format': 'Opus', - 'samplerate': 48000, 'bitdepth': 0, 'channels': 1, }, From 99b4e25ac22136a4399fc3d49800db1224a33e1e Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 11 Sep 2013 16:08:18 -0700 Subject: [PATCH 03/10] finish up changelog for beets 1.3.0 --- docs/changelog.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index d2df94cae..2f5a8e4a8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,8 +1,8 @@ Changelog ========= -1.3.0 (in development) ----------------------- +1.3.0 (September 11, 2013) +-------------------------- Albums and items now have **flexible attributes**. This means that, when you want to store information about your music in the beets database, you're no @@ -33,6 +33,10 @@ match *nothing* instead of *everything*. So if you type ``beet ls fieldThatDoesNotExist:foo``, beets will now return no results, whereas previous versions would spit out a warning and then list your entire library. +There's more detail than you could ever need `on the beets blog`_. + +.. _on the beets blog: http://beets.radbox.org/blog/flexattr.html + 1.2.2 (August 27, 2013) ----------------------- @@ -1341,7 +1345,7 @@ issue involves correct ordering of autotagged albums. * BPD now uses a pure-Python socket library and no longer requires eventlet/greenlet (the latter of which is a C extension). For the curious, the - socket library in question is called `Bluelet`_. + socket library in question is called `Bluelet`_. * Non-autotagged imports are now resumable (just like autotagged imports). From fde071eb11a18be5c75a90eed34df321441d525b Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 11 Sep 2013 16:10:04 -0700 Subject: [PATCH 04/10] Added tag v1.3.0 for changeset 87945a0e2175 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 7b112e598..c2cd27735 100644 --- a/.hgtags +++ b/.hgtags @@ -24,3 +24,4 @@ b3f7b5267a2f7b46b826d087421d7f4569211240 v1.2.0 ecff182221ec32a9f6549ad3ce8d2ab4c3e5568a v1.2.0 bd7259ac13b54caecb1403f625688eb3eeeba8d6 v1.2.1 c6af5962e25b915ce538af1c0b53a89ceb340b04 v1.2.2 +87945a0e217591a842307fa11e161d4912598c32 v1.3.0 From 245a6ccb5b8891933e9f161e12b912176e99df29 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 12 Sep 2013 09:46:01 -0700 Subject: [PATCH 05/10] version bump: 1.3.1 --- beets/__init__.py | 2 +- docs/changelog.rst | 6 ++++++ docs/conf.py | 2 +- setup.py | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/beets/__init__.py b/beets/__init__.py index 3e794c068..0edaa71b3 100644 --- a/beets/__init__.py +++ b/beets/__init__.py @@ -12,7 +12,7 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -__version__ = '1.3.0' +__version__ = '1.3.1' __author__ = 'Adrian Sampson ' import beets.library diff --git a/docs/changelog.rst b/docs/changelog.rst index 2f5a8e4a8..9e7038768 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,12 @@ Changelog ========= +1.3.1 (in development) +---------------------- + +TK + + 1.3.0 (September 11, 2013) -------------------------- diff --git a/docs/conf.py b/docs/conf.py index 3a646837d..dba10592b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ project = u'beets' copyright = u'2012, Adrian Sampson' version = '1.3' -release = '1.3.0' +release = '1.3.1' pygments_style = 'sphinx' diff --git a/setup.py b/setup.py index d6201eba1..8a6c45f36 100755 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: -# +# # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. @@ -42,7 +42,7 @@ if 'sdist' in sys.argv: shutil.copytree(os.path.join(docdir, '_build', 'man'), mandir) setup(name='beets', - version='1.3.0', + version='1.3.1', description='music tagger and library organizer', author='Adrian Sampson', author_email='adrian@radbox.org', From 8dc1e223e97f66e12467417eaece09c9ebffd0ba Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 12 Sep 2013 10:05:22 -0700 Subject: [PATCH 06/10] opus: sample rate is always 48kHz --- beets/mediafile.py | 3 +++ test/test_mediafile_basic.py | 1 + 2 files changed, 4 insertions(+) diff --git a/beets/mediafile.py b/beets/mediafile.py index 961564bac..1ac589536 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -1322,6 +1322,9 @@ class MediaFile(object): """The audio's sample rate (an int).""" if hasattr(self.mgfile.info, 'sample_rate'): return self.mgfile.info.sample_rate + elif self.type == 'opus': + # Opus is always 48kHz internally. + return 48000 return 0 @property diff --git a/test/test_mediafile_basic.py b/test/test_mediafile_basic.py index 3c1bf1e11..af54ee717 100644 --- a/test/test_mediafile_basic.py +++ b/test/test_mediafile_basic.py @@ -177,6 +177,7 @@ READ_ONLY_CORRECT_DICTS = { 'length': 1.0, 'bitrate': 63216, 'format': 'Opus', + 'samplerate': 48000, 'bitdepth': 0, 'channels': 1, }, From 6857267cc77a0e3d69a2e7917b050251cdf0f88c Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 12 Sep 2013 10:16:47 -0700 Subject: [PATCH 07/10] opus: remove image from test file The encoder that produced this file for some reason included an empty image as the cover art and was confusing the tests. I left the null check in place to deal with this situation in the future. I think returning None is better than returning the empty string (which is of course not a valid image). --- beets/mediafile.py | 8 ++++---- test/rsrc/full.opus | Bin 7902 -> 7248 bytes test/test_mediafile_basic.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/beets/mediafile.py b/beets/mediafile.py index 1ac589536..ec14f99e9 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -756,9 +756,9 @@ class ImageField(object): return None else: - # Here we're assuming everything but MP3, MPEG-4, and FLAC - # use the Xiph/Vorbis Comments standard. This may not be - # valid. http://wiki.xiph.org/VorbisComment#Cover_art + # Here we're assuming everything but MP3, MPEG-4, FLAC, and + # ASF/WMA use the Xiph/Vorbis Comments standard. This may + # not be valid. http://wiki.xiph.org/VorbisComment#Cover_art if 'metadata_block_picture' not in obj.mgfile: # Try legacy COVERART tags. @@ -775,7 +775,7 @@ class ImageField(object): else: return None - if pic.data == '': + if not pic.data: return None return pic.data diff --git a/test/rsrc/full.opus b/test/rsrc/full.opus index 81cd652841bf2d54c37496bc3c33f3a2f5d31d70..9a5534b2215ddf17f1ff9c67b97d5c1ffde630f6 100644 GIT binary patch delta 29 lcmca-d%Gf10B}?K=5y(uMDHq#;ic5dS?a(24B|@M;FHs$9N|ne`oLb08i(T m&>&Y^N8*7k&=j&jJz2V_r)kuF>V+jaCJAC_+Ps@-sw4n0gd)=b diff --git a/test/test_mediafile_basic.py b/test/test_mediafile_basic.py index af54ee717..3cbee09e7 100644 --- a/test/test_mediafile_basic.py +++ b/test/test_mediafile_basic.py @@ -175,7 +175,7 @@ READ_ONLY_CORRECT_DICTS = { 'full.opus': { 'length': 1.0, - 'bitrate': 63216, + 'bitrate': 57984, 'format': 'Opus', 'samplerate': 48000, 'bitdepth': 0, From a785f66511048da12bc6d182acbba03ae09bfab7 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 12 Sep 2013 10:21:05 -0700 Subject: [PATCH 08/10] opus: changelog/thanks/docs (closes #377, #381) --- docs/changelog.rst | 4 +++- docs/guides/tagger.rst | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9e7038768..f8b0927aa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,9 @@ Changelog 1.3.1 (in development) ---------------------- -TK +* Add `Opus`_ audio support. Thanks to Rowan Lewis. + +.. _Ogg Opus: http://www.opus-codec.org/ 1.3.0 (September 11, 2013) diff --git a/docs/guides/tagger.rst b/docs/guides/tagger.rst index 29bba2d89..489fe9807 100644 --- a/docs/guides/tagger.rst +++ b/docs/guides/tagger.rst @@ -61,8 +61,8 @@ all of these limitations. unidentified albums. * Currently, MP3, AAC, FLAC, ALAC, Ogg Vorbis, Monkey's Audio, WavPack, - Musepack, and Windows Media files are supported. (Do you use some other - format? `Let me know!`_) + Musepack, Windows Media, and Opus files are supported. (Do you use some + other format? `Let me know!`_) .. _Let me know!: mailto:adrian@radbox.org @@ -185,7 +185,7 @@ candidates), like so:: Candidates: 1. Panther - Yourself (66.8%) 2. Tav Falco's Panther Burns - Return of the Blue Panther (30.4%) - # selection (default 1), Skip, Use as-is, or Enter search, or aBort? + # selection (default 1), Skip, Use as-is, or Enter search, or aBort? Here, you have many of the same options as before, but you can also enter a number to choose one of the options that beets has found. Don't worry about From 19958f75ff43fa73f1bc7c630cc5e824ae2e4b6d Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 12 Sep 2013 10:45:02 -0700 Subject: [PATCH 09/10] embedart: fix error message when imagehdr fails --- beetsplug/embedart.py | 21 ++++++++++++++------- docs/changelog.rst | 7 +++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/beetsplug/embedart.py b/beetsplug/embedart.py index 2b342c989..d019ece77 100644 --- a/beetsplug/embedart.py +++ b/beetsplug/embedart.py @@ -34,8 +34,15 @@ def _embed(path, items, maxwidth=0): data = open(syspath(path), 'rb').read() kindstr = imghdr.what(None, data) - if kindstr not in ('jpeg', 'png'): - log.error('A file of type %s is not allowed as coverart.' % kindstr) + if kindstr is None: + log.error(u'Could not embed art of unkown type: {0}'.format( + displayable_path(path) + )) + return + elif kindstr not in ('jpeg', 'png'): + log.error(u'Image type {0} is not allowed as cover art: {1}'.format( + kindstr, displayable_path(path) + )) return # Add art to each file. @@ -110,8 +117,9 @@ def embed(lib, imagepath, query): log.error('No album matches query.') return - log.info('Embedding album art into %s - %s.' % \ - (album.albumartist, album.album)) + log.info(u'Embedding album art into {0.albumartist} - {0.album}.'.format( + album + )) _embed(imagepath, album.items(), config['embedart']['maxwidth'].get(int)) @@ -161,9 +169,8 @@ def extract(lib, outpath, query): return outpath += '.' + ext - log.info('Extracting album art from: %s - %s\n' - 'To: %s' % \ - (item.artist, item.title, outpath)) + log.info(u'Extracting album art from: {0.artist} - {0.title}\n' + u'To: {1}'.format(item, displayable_path(outpath))) with open(syspath(outpath), 'wb') as f: f.write(art) diff --git a/docs/changelog.rst b/docs/changelog.rst index f8b0927aa..0b1382f30 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,8 +4,15 @@ Changelog 1.3.1 (in development) ---------------------- +New stuff: + * Add `Opus`_ audio support. Thanks to Rowan Lewis. +And some fixes: + +* :doc:`/plugins/fetchart`: Better error message when the image file has an + unrecognized type. + .. _Ogg Opus: http://www.opus-codec.org/ From 4f1ce5a64c9d70be535c854565bd9db04e2bb10b Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 12 Sep 2013 11:11:04 -0700 Subject: [PATCH 10/10] docs: mention musicbrainz port config (closes #365) --- docs/changelog.rst | 2 +- docs/reference/config.rst | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0b1382f30..f035d1550 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,7 +13,7 @@ And some fixes: * :doc:`/plugins/fetchart`: Better error message when the image file has an unrecognized type. -.. _Ogg Opus: http://www.opus-codec.org/ +.. _Opus: http://www.opus-codec.org/ 1.3.0 (September 11, 2013) diff --git a/docs/reference/config.rst b/docs/reference/config.rst index 36844b668..8a80b929f 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -237,7 +237,7 @@ copy Either ``yes`` or ``no``, indicating whether to **copy** files into the library directory when using ``beet import``. Defaults to ``yes``. Can be overridden with the ``-c`` and ``-C`` command-line options. - + The option is ignored if ``move`` is enabled (i.e., beets can move or copy files but it doesn't make sense to do both). @@ -246,7 +246,7 @@ move Either ``yes`` or ``no``, indicating whether to **move** files into the library directory when using ``beet import``. -Defaults to ``no``. +Defaults to ``no``. The effect is similar to the ``copy`` option but you end up with only one copy of the imported file. ("Moving" works even across filesystems; if @@ -347,14 +347,15 @@ instead of the main server. Use the ``host`` and ``ratelimit`` options under a ``musicbrainz:`` header, like so:: musicbrainz: - host: localhost + host: localhost:5000 ratelimit: 100 -The ``host`` key, of course, controls the Web server that will be contacted by -beets (default: musicbrainz.org). The ``ratelimit`` option, an integer, -controls the number of Web service requests per second (default: 1). **Do not -change the rate limit setting** if you're using the main MusicBrainz -server---on this public server, you're `limited`_ to one request per second. +The ``host`` key, of course, controls the Web server hostname (and port, +optionally) that will be contacted by beets (default: musicbrainz.org). The +``ratelimit`` option, an integer, controls the number of Web service requests +per second (default: 1). **Do not change the rate limit setting** if you're +using the main MusicBrainz server---on this public server, you're `limited`_ +to one request per second. .. _limited: http://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting .. _MusicBrainz: http://musicbrainz.org/