Browse Source

fix _tag_interface.MP4.set_free_int for ints > 1 byte

Fabian Peter Hammerle 1 month ago
parent
commit
cdce51467d
2 changed files with 32 additions and 3 deletions
  1. 8 3
      symuid/_tag_interface.py
  2. 24 0
      tests/tag_interface/test_mp4.py

+ 8 - 3
symuid/_tag_interface.py

@@ -210,14 +210,19 @@ class MP4(_MutagenTagInterface):
         self._mutagen_file.tags['----:' + tag_label] = [tag]
         return tag
 
+    @staticmethod
+    def _int_to_freeform_data(integer: int) -> bytes:
+        # "a signed big-endian integer with length one of { 1,2,3,4,8 } bytes"
+        # TODO exclude 5-7, 9-
+        byte_len = max((integer.bit_length() + 8) // 8, 1)
+        return integer.to_bytes(byte_len, byteorder='big', signed=True)
+
     def set_free_int(self, tag_label, data):
         assert isinstance(data, int)
         return self._set_free(
             tag_label=tag_label,
-            # "a signed big-endian integer with length one of { 1,2,3,4,8 } bytes"
             dataformat=mutagen.mp4.AtomDataType.INTEGER,
-            # TODO set byte length properly
-            data=data.to_bytes(1, byteorder='big', signed=True),
+            data=self._int_to_freeform_data(data),
         )
 
     def _set_free_uuid(self, tag_label, data):

+ 24 - 0
tests/tag_interface/test_mp4.py

@@ -57,3 +57,27 @@ def test_get_free_int_ratio(empty_mp4_path, nominator, denominator):
     mutagen_file.save()
     mp4_iface = MP4(mutagen.File(empty_mp4_path))
     assert mp4_iface.get_free_int_ratio('foo:bar') == (nominator, denominator)
+
+
+@pytest.mark.parametrize(('integer', 'expected_tag_data'), [
+    (0, b'\x00'),
+    (4, b'\x04'),
+    (-1, b'\xff'),
+    (-2, b'\xfe'),
+    (2**6, b'\x40'),
+    (2**7-1, b'\x7f'),
+    (2**7, b'\x00\x80'),
+    (2**8, b'\x01\x00'),
+    (2**24, b'\x01\x00\x00\x00'),
+    (2**31-1, b'\x7f\xff\xff\xff'),
+])
+def test_set_free_int(empty_mp4_path, integer, expected_tag_data):
+    mp4_iface = MP4(mutagen.File(empty_mp4_path))
+    mp4_iface.set_free_int('foo:bar', integer)
+    mp4_iface.save()
+    mutagen_file = mutagen.File(empty_mp4_path)
+    assert len(mutagen_file.tags) == 1
+    tag, = mutagen_file.get('----:foo:bar')
+    assert tag.dataformat == mutagen.mp4.AtomDataType.INTEGER
+    assert bytes(tag) == expected_tag_data
+    assert MP4._freeform_to_int(tag) == integer