Browse Source

add support for vorbis in ogg container

Fabian Peter Hammerle 4 years ago
parent
commit
7372ffe709

+ 3 - 2
symuid/__init__.py

@@ -57,8 +57,9 @@ class Track:
             return _tag_interface.ID3(mutagen_file)
         if isinstance(mutagen_file.tags, mutagen.mp4.MP4Tags):
             return _tag_interface.MP4(mutagen_file)
-        if isinstance(mutagen_file, mutagen.oggopus.OggOpus):
-            return _tag_interface.OggOpus(mutagen_file)
+        if isinstance(mutagen_file, (mutagen.oggopus.OggOpus,
+                                     mutagen.oggvorbis.OggVorbis)):
+            return _tag_interface.Ogg(mutagen_file)
         raise NotImplementedError((track_path, type(mutagen_file)))
 
     @property

+ 5 - 3
symuid/_tag_interface.py

@@ -213,16 +213,18 @@ class MP4(_MutagenTagInterface):
         return self._set_free_uuid(self._UUID_TAG_KEY, uuid)
 
 
-class OggOpus(_MutagenTagInterface):
+class Ogg(_MutagenTagInterface):
 
     # https://github.com/cmus/cmus/blob/9a0723f7a90dc7de0898be87963d5105a999aa6c/ip/opus.c#L229
+    # https://github.com/cmus/cmus/blob/9a0723f7a90dc7de0898be87963d5105a999aa6c/ip/vorbis.c#L319
     # https://github.com/cmus/cmus/blob/17bf542c6b120d9dcf6642b259d78badfc1143eb/comment.c#L224
     _COMMENT_TAG_KEY = 'comment'
     _UUID_TAG_KEY = 'symuid:uuid'
 
     def __init__(self, mutagen_file):
-        assert isinstance(mutagen_file.tags, mutagen.oggopus.OggOpusVComment), \
-            (mutagen_file, mutagen_file.tags)
+        assert isinstance(mutagen_file.tags, (mutagen.oggopus.OggOpusVComment,
+                                              mutagen.oggvorbis.OggVCommentDict)), \
+            (type(mutagen_file), type(mutagen_file.tags))
         super().__init__(mutagen_file)
 
     def _get_single_text(self, tag_label) -> typing.Optional[str]:

+ 10 - 0
tests/conftest.py

@@ -29,3 +29,13 @@ def empty_ogg_opus_path(tmpdir, tracks_dir_path):
         dst=path,
     )
     return path
+
+
+@pytest.fixture
+def empty_ogg_vorbis_path(tmpdir, tracks_dir_path):
+    path = tmpdir.join('empty.ogg').strpath
+    shutil.copyfile(
+        src=os.path.join(tracks_dir_path, 'ogg-vorbis-empty.ogg'),
+        dst=path,
+    )
+    return path

+ 119 - 0
tests/tag_interface/test_ogg_opus.py

@@ -0,0 +1,119 @@
+import os
+
+import mutagen
+import pytest
+from symuid._tag_interface import Ogg
+
+# pylint: disable=protected-access
+
+
+@pytest.mark.parametrize('track_name', ['ogg-opus-empty.opus'])
+def test_get_track_path(tracks_dir_path, track_name):
+    track_path = os.path.join(tracks_dir_path, track_name)
+    iface = Ogg(mutagen.File(track_path))
+    assert track_path == iface.track_path
+
+
+@pytest.mark.parametrize(('track_name', 'tag_label', 'expected_text'), [
+    ('ogg-opus-empty.opus', 'artist', None),
+    ('ogg-opus-typical.opus', 'artist', 'some artist'),
+    ('ogg-opus-typical.opus', 'comment', 'some comment'),
+    ('ogg-opus-typical.opus', 'com', None),
+    ('ogg-opus-typical.opus', 'symuid:uuid',
+     '613ea4ac-a4cf-4026-8e99-1904b2bb5cd0'),
+])
+def test__get_single_text(tracks_dir_path, track_name, tag_label, expected_text):
+    iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    assert expected_text == iface._get_single_text(tag_label)
+
+
+@pytest.mark.parametrize(('track_name', 'expected_comment'), [
+    ('ogg-opus-empty.opus', None),
+    ('ogg-opus-typical.opus', 'some comment'),
+])
+def test_get_comment(tracks_dir_path, track_name, expected_comment):
+    iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    assert expected_comment == iface.get_comment()
+
+
+def test_set_comment(empty_ogg_opus_path):
+    iface = Ogg(mutagen.File(empty_ogg_opus_path))
+    assert iface.get_comment() is None
+    iface.set_comment('latin')
+    assert iface.get_comment() == 'latin'
+    iface.set_comment('你好')
+    assert iface.get_comment() == '你好'
+    iface.save()
+    iface_reread = Ogg(mutagen.File(empty_ogg_opus_path))
+    assert iface_reread.get_comment() == '你好'
+    tags = mutagen.File(iface.track_path).tags
+    assert len(tags) == 1
+    assert tags.items()[0] == ('comment', ['你好'])
+
+
+@pytest.mark.parametrize(('track_name', 'expected_uuid'), [
+    ('ogg-opus-empty.opus', None),
+    ('ogg-opus-typical.opus', b'a>\xa4\xac\xa4\xcf@&\x8e\x99\x19\x04\xb2\xbb\\\xd0'),
+])
+def test_get_track_uuid(tracks_dir_path, track_name, expected_uuid):
+    iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    assert expected_uuid == iface.get_track_uuid()
+
+
+def test_set_track_uuid(empty_ogg_opus_path):
+    iface = Ogg(mutagen.File(empty_ogg_opus_path))
+    assert iface.get_track_uuid() is None
+    uuid_a = b'\x9e\xa7\xc4\xf0\xda\xecE\xb7\xab\x9a\xba\x9f\xc6\xaa\xc0S'
+    iface.set_track_uuid(uuid_a)
+    assert iface.get_track_uuid() == uuid_a
+    uuid_b = b'_\xa0\xd7\xc0\xf3\x15F\x14\xbe\xe4idM=\x80\xb3'
+    iface.set_track_uuid(uuid_b)
+    assert iface.get_track_uuid() == uuid_b
+    iface.save()
+    iface_reread = Ogg(mutagen.File(empty_ogg_opus_path))
+    assert iface_reread.get_track_uuid() == uuid_b
+    tags = mutagen.File(iface.track_path).tags
+    assert len(tags) == 1
+    assert tags.items()[0] == (
+        'symuid:uuid', ['5fa0d7c0-f315-4614-bee4-69644d3d80b3'])
+
+
+@pytest.mark.parametrize(('track_name', 'tag_label', 'expected_int'), [
+    ('ogg-opus-empty.opus', 'tracknumber', None),
+    ('ogg-opus-typical.opus', 'tracknumber', 21),
+    ('ogg-opus-typical.opus', 'tracknumberr', None),
+    ('ogg-opus-typical.opus', 'symuid:pcnt:player:library:1572098177', 43),
+])
+def test_get_free_int(tracks_dir_path, track_name, tag_label, expected_int):
+    iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    assert expected_int == iface.get_free_int(tag_label)
+
+
+def test_get_free_ints(tracks_dir_path):
+    iface = Ogg(mutagen.File(os.path.join(
+        tracks_dir_path, 'ogg-opus-typical.opus')))
+    assert not list(iface.get_free_ints('symuid:none'))
+    assert list(iface.get_free_ints('tracknumber')) == [('tracknumber', 21)]
+    assert not list(iface.get_free_ints('tracknum'))
+    pcnt_tags = {('symuid:pcnt:player:library:1572098158', 42),
+                 ('symuid:pcnt:player:library:1572098177', 43)}
+    assert set(iface.get_free_ints('symuid:pcnt:player:library')) == pcnt_tags
+    assert set(iface.get_free_ints('symuid:pcnt:player')) == pcnt_tags
+    assert set(iface.get_free_ints('symuid:pcnt')) == pcnt_tags
+    assert set(iface.get_free_ints('symuid:pcnt:player:library:1572098158')) \
+        == {('symuid:pcnt:player:library:1572098158', 42)}
+
+
+def test_set_free_int(empty_ogg_opus_path):
+    iface = Ogg(mutagen.File(empty_ogg_opus_path))
+    assert iface.get_free_int('tracknumber') is None
+    iface.set_free_int('tracknumber', 7)
+    assert iface.get_free_int('tracknumber') == 7
+    iface.set_free_int('tracknumber', 14)
+    assert iface.get_free_int('tracknumber') == 14
+    iface.save()
+    iface_reread = Ogg(mutagen.File(empty_ogg_opus_path))
+    assert iface_reread.get_free_int('tracknumber') == 14
+    tags = mutagen.File(iface.track_path).tags
+    assert len(tags) == 1
+    assert tags.items()[0] == ('tracknumber', ['14'])

+ 54 - 55
tests/tag_interface/test_ogg_vorbis.py

@@ -2,119 +2,118 @@ import os
 
 import mutagen
 import pytest
-
-from symuid._tag_interface import OggOpus
+from symuid._tag_interface import Ogg
 
 # pylint: disable=protected-access
 
 
-@pytest.mark.parametrize('track_name', ['ogg-opus-empty.opus'])
+@pytest.mark.parametrize('track_name', ['ogg-vorbis-empty.ogg'])
 def test_get_track_path(tracks_dir_path, track_name):
     track_path = os.path.join(tracks_dir_path, track_name)
-    iface = OggOpus(mutagen.File(track_path))
-    assert track_path == iface.track_path
+    ogg_iface = Ogg(mutagen.File(track_path))
+    assert track_path == ogg_iface.track_path
 
 
 @pytest.mark.parametrize(('track_name', 'tag_label', 'expected_text'), [
-    ('ogg-opus-empty.opus', 'artist', None),
-    ('ogg-opus-typical.opus', 'artist', 'some artist'),
-    ('ogg-opus-typical.opus', 'comment', 'some comment'),
-    ('ogg-opus-typical.opus', 'com', None),
-    ('ogg-opus-typical.opus', 'symuid:uuid',
-     '613ea4ac-a4cf-4026-8e99-1904b2bb5cd0'),
+    ('ogg-vorbis-empty.ogg', 'artist', None),
+    ('ogg-vorbis-typical.ogg', 'artist', 'libvorbis encoder'),
+    ('ogg-vorbis-typical.ogg', 'comment', 'some further information'),
+    ('ogg-vorbis-typical.ogg', 'com', None),
+    ('ogg-vorbis-typical.ogg', 'symuid:uuid',
+     '4cfe3a2a-6354-40f5-bd9b-5ccf1c6f48ba'),
 ])
 def test__get_single_text(tracks_dir_path, track_name, tag_label, expected_text):
-    iface = OggOpus(mutagen.File(os.path.join(tracks_dir_path, track_name)))
-    assert expected_text == iface._get_single_text(tag_label)
+    ogg_iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    assert expected_text == ogg_iface._get_single_text(tag_label)
 
 
 @pytest.mark.parametrize(('track_name', 'expected_comment'), [
-    ('ogg-opus-empty.opus', None),
-    ('ogg-opus-typical.opus', 'some comment'),
+    ('ogg-vorbis-empty.ogg', None),
+    ('ogg-vorbis-typical.ogg', 'some further information'),
 ])
 def test_get_comment(tracks_dir_path, track_name, expected_comment):
-    iface = OggOpus(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
     assert expected_comment == iface.get_comment()
 
 
-def test_set_comment(empty_ogg_opus_path):
-    iface = OggOpus(mutagen.File(empty_ogg_opus_path))
+def test_set_comment(empty_ogg_vorbis_path):
+    iface = Ogg(mutagen.File(empty_ogg_vorbis_path))
     assert iface.get_comment() is None
-    iface.set_comment('latin')
-    assert iface.get_comment() == 'latin'
-    iface.set_comment('你好')
-    assert iface.get_comment() == '你好'
+    iface.set_comment('latin chars')
+    assert iface.get_comment() == 'latin chars'
+    iface.set_comment('你好!')
+    assert iface.get_comment() == '你好!'
     iface.save()
-    iface_reread = OggOpus(mutagen.File(empty_ogg_opus_path))
-    assert iface_reread.get_comment() == '你好'
+    iface_reread = Ogg(mutagen.File(empty_ogg_vorbis_path))
+    assert iface_reread.get_comment() == '你好!'
     tags = mutagen.File(iface.track_path).tags
     assert len(tags) == 1
-    assert tags.items()[0] == ('comment', ['你好'])
+    assert tags.items()[0] == ('comment', ['你好!'])
 
 
 @pytest.mark.parametrize(('track_name', 'expected_uuid'), [
-    ('ogg-opus-empty.opus', None),
-    ('ogg-opus-typical.opus', b'a>\xa4\xac\xa4\xcf@&\x8e\x99\x19\x04\xb2\xbb\\\xd0'),
+    ('ogg-vorbis-empty.ogg', None),
+    ('ogg-vorbis-typical.ogg', b'L\xfe:*cT@\xf5\xbd\x9b\\\xcf\x1coH\xba'),
 ])
 def test_get_track_uuid(tracks_dir_path, track_name, expected_uuid):
-    iface = OggOpus(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
     assert expected_uuid == iface.get_track_uuid()
 
 
-def test_set_track_uuid(empty_ogg_opus_path):
-    iface = OggOpus(mutagen.File(empty_ogg_opus_path))
+def test_set_track_uuid(empty_ogg_vorbis_path):
+    iface = Ogg(mutagen.File(empty_ogg_vorbis_path))
     assert iface.get_track_uuid() is None
-    uuid_a = b'\x9e\xa7\xc4\xf0\xda\xecE\xb7\xab\x9a\xba\x9f\xc6\xaa\xc0S'
+    uuid_a = b's\x84@\xd8\xfe\xe8K\x80\xa7J\x13\x89\\%\xech'
     iface.set_track_uuid(uuid_a)
     assert iface.get_track_uuid() == uuid_a
-    uuid_b = b'_\xa0\xd7\xc0\xf3\x15F\x14\xbe\xe4idM=\x80\xb3'
+    uuid_b = b'\xfaQ\x9a\x0eo\xa3Cp\xa1\x98c\x01\x93\xfb\xf7\xd0'
     iface.set_track_uuid(uuid_b)
     assert iface.get_track_uuid() == uuid_b
     iface.save()
-    iface_reread = OggOpus(mutagen.File(empty_ogg_opus_path))
+    iface_reread = Ogg(mutagen.File(empty_ogg_vorbis_path))
     assert iface_reread.get_track_uuid() == uuid_b
     tags = mutagen.File(iface.track_path).tags
     assert len(tags) == 1
     assert tags.items()[0] == (
-        'symuid:uuid', ['5fa0d7c0-f315-4614-bee4-69644d3d80b3'])
+        'symuid:uuid', ['fa519a0e-6fa3-4370-a198-630193fbf7d0'])
 
 
 @pytest.mark.parametrize(('track_name', 'tag_label', 'expected_int'), [
-    ('ogg-opus-empty.opus', 'tracknumber', None),
-    ('ogg-opus-typical.opus', 'tracknumber', 21),
-    ('ogg-opus-typical.opus', 'tracknumberr', None),
-    ('ogg-opus-typical.opus', 'symuid:pcnt:player:library:1572098177', 43),
+    ('ogg-vorbis-empty.ogg', 'tracknumber', None),
+    ('ogg-vorbis-typical.ogg', 'tracknumber', 3),
+    ('ogg-vorbis-typical.ogg', 'tracknumberr', None),
+    ('ogg-vorbis-typical.ogg', 'symuid:pcnt:player:library:1572098177', 22),
 ])
 def test_get_free_int(tracks_dir_path, track_name, tag_label, expected_int):
-    iface = OggOpus(mutagen.File(os.path.join(tracks_dir_path, track_name)))
-    assert expected_int == iface.get_free_int(tag_label)
+    ogg_iface = Ogg(mutagen.File(os.path.join(tracks_dir_path, track_name)))
+    assert expected_int == ogg_iface.get_free_int(tag_label)
 
 
 def test_get_free_ints(tracks_dir_path):
-    iface = OggOpus(mutagen.File(os.path.join(
-        tracks_dir_path, 'ogg-opus-typical.opus')))
+    iface = Ogg(mutagen.File(os.path.join(
+        tracks_dir_path, 'ogg-vorbis-typical.ogg')))
     assert not list(iface.get_free_ints('symuid:none'))
-    assert list(iface.get_free_ints('tracknumber')) == [('tracknumber', 21)]
+    assert list(iface.get_free_ints('tracknumber')) == [('tracknumber', 3)]
     assert not list(iface.get_free_ints('tracknum'))
-    pcnt_tags = {('symuid:pcnt:player:library:1572098158', 42),
-                 ('symuid:pcnt:player:library:1572098177', 43)}
+    pcnt_tags = {('symuid:pcnt:player:library:1572098158', 21),
+                 ('symuid:pcnt:player:library:1572098177', 22)}
     assert set(iface.get_free_ints('symuid:pcnt:player:library')) == pcnt_tags
     assert set(iface.get_free_ints('symuid:pcnt:player')) == pcnt_tags
     assert set(iface.get_free_ints('symuid:pcnt')) == pcnt_tags
     assert set(iface.get_free_ints('symuid:pcnt:player:library:1572098158')) \
-        == {('symuid:pcnt:player:library:1572098158', 42)}
+        == {('symuid:pcnt:player:library:1572098158', 21)}
 
 
-def test_set_free_int(empty_ogg_opus_path):
-    iface = OggOpus(mutagen.File(empty_ogg_opus_path))
+def test_set_free_int(empty_ogg_vorbis_path):
+    iface = Ogg(mutagen.File(empty_ogg_vorbis_path))
     assert iface.get_free_int('tracknumber') is None
-    iface.set_free_int('tracknumber', 7)
-    assert iface.get_free_int('tracknumber') == 7
-    iface.set_free_int('tracknumber', 14)
-    assert iface.get_free_int('tracknumber') == 14
+    iface.set_free_int('tracknumber', 9)
+    assert iface.get_free_int('tracknumber') == 9
+    iface.set_free_int('tracknumber', 5)
+    assert iface.get_free_int('tracknumber') == 5
     iface.save()
-    iface_reread = OggOpus(mutagen.File(empty_ogg_opus_path))
-    assert iface_reread.get_free_int('tracknumber') == 14
+    iface_reread = Ogg(mutagen.File(empty_ogg_vorbis_path))
+    assert iface_reread.get_free_int('tracknumber') == 5
     tags = mutagen.File(iface.track_path).tags
     assert len(tags) == 1
-    assert tags.items()[0] == ('tracknumber', ['14'])
+    assert tags.items()[0] == ('tracknumber', ['5'])

+ 5 - 1
tests/test_track.py

@@ -27,6 +27,7 @@ def empty_id3_track(empty_id3_path):
     'id3v2.4-typical.mp3',
     'id3v2.4-empty.mp3',
     'ogg-opus-typical.opus',
+    'ogg-vorbis-typical.ogg',
 ])
 def test_init(tracks_dir_path, track_name):
     symuid.Track(os.path.join(tracks_dir_path, track_name))
@@ -36,6 +37,7 @@ def test_init(tracks_dir_path, track_name):
     ('id3v2.4-typical.mp3', 'some comment'),
     ('id3v2.4-empty.mp3', None),
     ('ogg-opus-typical.opus', 'some comment'),
+    ('ogg-vorbis-typical.ogg', 'some further information'),
 ])
 def test_get_comment(tracks_dir_path, track_name, expected_comment):
     track = symuid.Track(os.path.join(tracks_dir_path, track_name))
@@ -195,4 +197,6 @@ def test_walk(tracks_dir_path):
         tracks_dir_path,
         path_ignore_regex=re.compile(r'typical'))
     track_names = set(os.path.basename(t.path) for t in tracks)
-    assert track_names == {'id3v2.4-empty.mp3', 'ogg-opus-empty.opus'}
+    assert track_names == {'id3v2.4-empty.mp3',
+                           'ogg-opus-empty.opus',
+                           'ogg-vorbis-empty.ogg'}

BIN
tests/tracks/ogg-vorbis-empty.ogg


BIN
tests/tracks/ogg-vorbis-typical.ogg