diff --git a/beets/mediafile.py b/beets/mediafile.py index 9b6234192..961564bac 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'] @@ -773,6 +775,9 @@ class ImageField(object): else: return None + if pic.data == '': + return None + return pic.data def __set__(self, obj, val): @@ -862,6 +867,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 +910,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 +1270,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 000000000..81cd65284 Binary files /dev/null and b/test/rsrc/full.opus differ diff --git a/test/test_mediafile_basic.py b/test/test_mediafile_basic.py index 91f663556..3c1bf1e11 100644 --- a/test/test_mediafile_basic.py +++ b/test/test_mediafile_basic.py @@ -173,6 +173,14 @@ READ_ONLY_CORRECT_DICTS = { 'channels': 1, }, + 'full.opus': { + 'length': 1.0, + 'bitrate': 63216, + 'format': 'Opus', + 'bitdepth': 0, + 'channels': 1, + }, + 'full.ape': { 'length': 1.0, 'bitrate': 112040, @@ -224,6 +232,7 @@ TEST_FILES = { 'mp3': ['full', 'partial', 'min'], 'flac': ['full', 'partial', 'min'], 'ogg': ['full'], + 'opus': ['full'], 'ape': ['full'], 'wv': ['full'], 'mpc': ['full'], @@ -264,6 +273,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 +305,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 +401,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 +441,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')