Browse Source

mp4 interface: refactor; added get_free_int_ratio()

Fabian Peter Hammerle 1 month ago
parent
commit
a33fa4193d
2 changed files with 74 additions and 18 deletions
  1. 35 18
      symuid/_tag_interface.py
  2. 39 0
      tests/tag_interface/test_mp4.py

+ 35 - 18
symuid/_tag_interface.py

@@ -155,15 +155,6 @@ class MP4(_MutagenTagInterface):
             mutagen_file.tags
         super().__init__(mutagen_file)
 
-    def _get_single(self, tag_label):
-        tag = self._mutagen_file.tags.get(tag_label, None)
-        if tag is None:
-            # {}.get('a') == None
-            return None
-        if len(tag) == 1:
-            return tag[0]
-        raise ValueError(tag)
-
     @staticmethod
     def _freeform_to_int(freeform):
         # "a signed big-endian integer with length one of { 1,2,3,4,8 } bytes"
@@ -181,18 +172,36 @@ class MP4(_MutagenTagInterface):
                 value = MP4._freeform_to_int(values[0])
                 yield (re.sub(r'^----:', '', label), value)
 
-    def _get_free(self, tag_label):
+    def _get_free(self, tag_label) -> typing.List[mutagen.mp4.MP4FreeForm]:
         # freeform keys start with '----'
         # http://mutagen.readthedocs.io/en/latest/api/mp4.html
-        return self._get_single('----:' + tag_label)
+        tag = self._mutagen_file.tags.get('----:' + tag_label, None)
+        if not tag:
+            raise KeyError(tag_label)
+        return tag
+
+    def _get_free_ints(self, tag_label) -> typing.Tuple[int]:
+        return tuple(self._freeform_to_int(i) for i in self._get_free(tag_label))
 
     def get_free_int(self, tag_label):
-        tag = self._get_free(tag_label)
-        return None if tag is None else MP4._freeform_to_int(tag)
+        try:
+            ints = self._get_free_ints(tag_label)
+        except KeyError:
+            return None
+        if len(ints) != 1:
+            raise ValueError((tag_label, ints))
+        return ints[0]
 
-    def _get_free_uuid(self, tag_label):
-        tag = self._get_free(tag_label)
-        assert tag is None or tag.dataformat == mutagen.mp4.AtomDataType.UUID, tag.dataformat
+    def get_free_int_ratio(self, tag_label) -> typing.Tuple[int, int]:
+        ints = self._get_free_ints(tag_label)
+        if len(ints) != 2:
+            raise ValueError((tag_label, ints))
+        return ints
+
+    def _get_free_uuid(self, tag_label) -> mutagen.mp4.MP4FreeForm:
+        tag, = self._get_free(tag_label)
+        if tag.dataformat != mutagen.mp4.AtomDataType.UUID:
+            raise ValueError(tag)
         return tag
 
     def _set_free(self, tag_label, dataformat, data):
@@ -220,13 +229,21 @@ class MP4(_MutagenTagInterface):
         )
 
     def get_comment(self):
-        return self._get_single(self._COMMENT_TAG_KEY)
+        tag = self._mutagen_file.tags.get(self._COMMENT_TAG_KEY, None)
+        if tag is None:
+            return None
+        if len(tag) == 1:
+            return tag[0]
+        raise ValueError(tag)
 
     def set_comment(self, comment: str) -> None:
         self._mutagen_file[self._COMMENT_TAG_KEY] = [comment]
 
     def get_track_uuid(self):
-        return self._get_free_uuid(self._UUID_TAG_KEY)
+        try:
+            return self._get_free_uuid(self._UUID_TAG_KEY)
+        except KeyError:
+            return None
 
     def set_track_uuid(self, uuid):
         return self._set_free_uuid(self._UUID_TAG_KEY, uuid)

+ 39 - 0
tests/tag_interface/test_mp4.py

@@ -1,4 +1,5 @@
 import mutagen
+import pytest
 
 from symuid._tag_interface import MP4
 
@@ -18,3 +19,41 @@ def test_set_comment(empty_mp4_path):
     tags = mutagen.File(iface.track_path).tags
     assert len(tags) == 1
     assert tags.items()[0] == ('©cmt', ['mp4 你好'])
+
+
+def test__get_free_uuid(empty_mp4_path):
+    uuid = b'h\x97\x8c_1?B\t\x9d\xa3$\xdf\xd0Y\xa1\xc2'
+    mutagen_file = mutagen.File(empty_mp4_path)
+    mutagen_file['----:foo:bar'] = mutagen.mp4.MP4FreeForm(
+        dataformat=mutagen.mp4.AtomDataType.UUID, data=uuid)
+    mutagen_file.save()
+    mp4_iface = MP4(mutagen.File(empty_mp4_path))
+    assert mp4_iface._get_free_uuid('foo:bar') == uuid
+
+
+def test_get_track_uuid(empty_mp4_path):
+    uuid = b'h\x97\x8c_1?B\t\x9d\xa3$\xdf\xd0Y\xa1\xc2'
+    mutagen_file = mutagen.File(empty_mp4_path)
+    mutagen_file['----:symuid:uuid'] = mutagen.mp4.MP4FreeForm(
+        dataformat=mutagen.mp4.AtomDataType.UUID, data=uuid)
+    mutagen_file.save()
+    mp4_iface = MP4(mutagen.File(empty_mp4_path))
+    assert mp4_iface.get_track_uuid() == uuid
+
+
+@pytest.mark.parametrize(('nominator', 'denominator'), [
+    (21, 42),
+    (-21, 42),
+    (21, -42),
+    (-21, -42),
+    (0, 42),
+])
+def test_get_free_int_ratio(empty_mp4_path, nominator, denominator):
+    mutagen_file = mutagen.File(empty_mp4_path)
+    mutagen_file.tags['----:foo:bar'] = [
+        mutagen.mp4.MP4FreeForm(dataformat=mutagen.mp4.AtomDataType.INTEGER,
+                                data=i.to_bytes(4, byteorder='big', signed=True))
+        for i in (nominator, denominator)]
+    mutagen_file.save()
+    mp4_iface = MP4(mutagen.File(empty_mp4_path))
+    assert mp4_iface.get_free_int_ratio('foo:bar') == (nominator, denominator)