Browse Source

symuid-import-itunes: move code setting tags to new class symuid.Track

Fabian Peter Hammerle 5 years ago
parent
commit
b1047f537a
3 changed files with 88 additions and 52 deletions
  1. 9 52
      symuid-import-itunes
  2. 78 0
      symuid/__init__.py
  3. 1 0
      symuid/library/itunes.py

+ 9 - 52
symuid-import-itunes

@@ -2,75 +2,32 @@
 # -*- coding: utf-8 -*-
 
 import datetime as dt
-import mutagen
-import mutagen.id3
-import mutagen.mp4
 import os
 import re
+import symuid
 import symuid.library.itunes
 import sys
 
 
-def generate_play_count_tag_label(player, library_id, reg_dt):
-    return 'symuid:pcnt:{}:{}:{}'.format(player, library_id, int(reg_dt.timestamp()))
-
-
-def set_play_count_tag(track_path, player, library_id, reg_dt, play_count):
-    assert isinstance(reg_dt, dt.datetime), reg_dt
-    tag_label = generate_play_count_tag_label(
-        player=player,
-        library_id=library_id,
-        reg_dt=reg_dt,
-    )
-    track = mutagen.File(filename=track_path)
-    if isinstance(track.tags, mutagen.id3.ID3):
-        tag_label_id3 = 'TXXX:' + tag_label
-        if not tag_label_id3 in track.tags:
-            # mutagen.id3._specs.EncodedTextSpec.write encodes
-            # 'desc' and 'text'
-            tag = mutagen.id3.TXXX(
-                encoding=mutagen.id3.Encoding.LATIN1,
-                desc=tag_label,
-                text=[str(play_count)],
-            )
-            track.tags.add(tag)
-            track.save()
-            print('{!r}: set ID3 tag {!r}'.format(track_path, tag))
-    elif isinstance(track.tags, mutagen.mp4.MP4Tags):
-        tag_label_mp4 = '----:' + tag_label
-        if not tag_label_mp4 in track.tags:
-            track.tags[tag_label_mp4] = tag = mutagen.mp4.MP4FreeForm(
-                # "a signed big-endian integer with length one of { 1,2,3,4,8 } bytes"
-                # TODO set byte length properly
-                # setting signed=True just to be explicit here
-                #  (irrelevant for positive integers)
-                data=play_count.to_bytes(1, byteorder='big', signed=True),
-                dataformat=mutagen.mp4.AtomDataType.INTEGER,
-            )
-            track.save()
-            print('{!r}: set MP4 tag {!r}'.format(track_path, tag))
-    else:
-        raise Exception(track_path)
-
-
 def symuid_import_itunes(xml_library_path, path_regex_sub):
     lib = symuid.library.itunes.XmlLibrary(xml_library_path)
-    for track in lib.tracks:
+    for itunes_track in lib.tracks:
         # TODO create tag if last_play_dt is None
-        if track.last_play_dt and track.local:
-            track_path = track.local_path
+        if itunes_track.last_play_dt and itunes_track.local:
+            track_path = itunes_track.local_path
             for pattern, repl in path_regex_sub:
                 track_path = re.sub(pattern, repl, track_path)
             if not os.path.exists(track_path):
                 sys.stderr.write('{!r}: not found\n'.format(track_path))
             else:
+                symuid_track = symuid.Track(path=track_path)
                 # TODO dt=dt.datetime.now()
-                set_play_count_tag(
-                    track_path=track_path,
+                symuid_track.register_play_count(
                     player='itunes',
                     library_id=lib.id,
-                    reg_dt=track.last_play_dt,
-                    play_count=track.play_count,
+                    register_dt=itunes_track.last_play_dt,
+                    play_count=itunes_track.play_count,
+                    tag_set_cb=lambda tr, tag: print('{!r}: set tag {!r}'.format(tr.path, tag)),
                 )
 
 

+ 78 - 0
symuid/__init__.py

@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+
+import datetime as dt
+import mutagen
+import mutagen.id3
+import mutagen.mp4
+
+
+class Track:
+
+    def __init__(self, path):
+        self._mutagen = mutagen.File(filename=path)
+
+    @property
+    def path(self):
+        return self._mutagen.filename
+
+    @property
+    def has_tags(self):
+        return self._mutagen and self._mutagen.tags
+
+    @property
+    def has_id3_tags(self):
+        return self.has_tags and isinstance(self._mutagen.tags, mutagen.id3.ID3)
+
+    @property
+    def has_mp4_tags(self):
+        return self.has_tags and isinstance(self._mutagen.tags, mutagen.mp4.MP4Tags)
+
+    def register_play_count(self, player, library_id, register_dt, play_count, tag_set_cb=None):
+        assert isinstance(register_dt, dt.datetime), register_dt
+        assert isinstance(play_count, int), play_count
+        tag_label = 'symuid:pcnt:{}:{}:{:.0f}'.format(
+            player, library_id, register_dt.timestamp(),
+        )
+        if self.has_id3_tags:
+            tag_label_id3 = 'TXXX:' + tag_label
+            if tag_label_id3 in self._mutagen.tags:
+                reg_data = self._mutagen.tags[tag_label_id3].text
+                assert len(reg_data) == 1, reg_data
+                assert int(reg_data[0]) == play_count, (reg_data, play_count)
+            else:
+                # mutagen.id3._specs.EncodedTextSpec.write encodes
+                # 'desc' and 'text'
+                tag = mutagen.id3.TXXX(
+                    encoding=mutagen.id3.Encoding.LATIN1,
+                    desc=tag_label,
+                    text=[str(play_count)],
+                )
+                self._mutagen.tags.add(tag)
+                self._mutagen.save()
+                if tag_set_cb:
+                    tag_set_cb(self, tag)
+        elif self.has_mp4_tags:
+            tag_label_mp4 = '----:' + tag_label
+            if tag_label_mp4 in self._mutagen.tags:
+                reg_data = self._mutagen.tags[tag_label_mp4]
+                assert len(reg_data) == 1, reg_data
+                reg_count = int.from_bytes(
+                    bytes=reg_data[0],
+                    byteorder='big',
+                    signed=True,
+                )
+                assert reg_count == play_count, (reg_count, play_count)
+            else:
+                self._mutagen.tags[tag_label_mp4] = tag = mutagen.mp4.MP4FreeForm(
+                    # "a signed big-endian integer with length one of { 1,2,3,4,8 } bytes"
+                    # TODO set byte length properly
+                    # setting signed=True just to be explicit here
+                    #  (irrelevant for positive integers)
+                    data=play_count.to_bytes(1, byteorder='big', signed=True),
+                    dataformat=mutagen.mp4.AtomDataType.INTEGER,
+                )
+                self._mutagen.save()
+                if tag_set_cb:
+                    tag_set_cb(self, tag)
+        else:
+            raise NotImplementedError()

+ 1 - 0
symuid/library/itunes.py

@@ -63,6 +63,7 @@ class Track:
         assert isinstance(track_dict, XmlDict)
         self._dict = track_dict
         self._id = self._dict['Track ID']
+        assert isinstance(self._id, int)
         self._location_url = self._dict.get('Location', None)
         self._play_count = self._dict.get('Play Count', 0)
         assert isinstance(self._play_count, int)