From 6f0261e5ff831dc9e15d9995c2e5ae52b7edd738 Mon Sep 17 00:00:00 2001 From: Rowan Lewis Date: Thu, 12 Sep 2013 07:17:32 +1000 Subject: [PATCH 1/2] 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 2/2] 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, },