|
@@ -6,6 +6,7 @@ import mutagen.id3
|
|
|
import mutagen.mp4
|
|
|
import os
|
|
|
import subprocess
|
|
|
+import xml.etree.ElementTree
|
|
|
|
|
|
# http://id3.org/id3v2.4.0-frames#4.1.
|
|
|
TRACK_UUID_ID3_OWNER_ID = 'symuid'
|
|
@@ -13,6 +14,7 @@ TRACK_UUID_ID3_OWNER_ID = 'symuid'
|
|
|
# http://mutagen.readthedocs.io/en/latest/api/mp4.html
|
|
|
TRACK_UUID_MP4_TAG = '----:symuid:uuid'
|
|
|
|
|
|
+
|
|
|
def generate_uuid():
|
|
|
return subprocess.check_output(['uuid', '-v', '4', '-F', 'BIN']).strip()
|
|
|
|
|
@@ -41,12 +43,13 @@ def get_or_assign_uuid_id3(id3_tags):
|
|
|
assert uuid is not None
|
|
|
return uuid
|
|
|
|
|
|
+
|
|
|
def get_or_assign_uuid_mp4(mp4_file):
|
|
|
if not TRACK_UUID_MP4_TAG in mp4_file:
|
|
|
mp4_file[TRACK_UUID_MP4_TAG] = mutagen.mp4.MP4FreeForm(
|
|
|
data=generate_uuid(),
|
|
|
# https://mutagen.readthedocs.io/en/latest/api/mp4.html#mutagen.mp4.AtomDataType.UUID
|
|
|
- dataformat= mutagen.mp4.AtomDataType.UUID,
|
|
|
+ dataformat=mutagen.mp4.AtomDataType.UUID,
|
|
|
)
|
|
|
mp4_file.save()
|
|
|
mp4_file.load(filename=mp4_file.filename)
|
|
@@ -56,7 +59,45 @@ def get_or_assign_uuid_mp4(mp4_file):
|
|
|
return mp4_file[TRACK_UUID_MP4_TAG][0]
|
|
|
|
|
|
|
|
|
-def symuid(path):
|
|
|
+def get_itunes_dict_value_node(dict_node, key):
|
|
|
+ assert isinstance(dict_node, xml.etree.ElementTree.Element)
|
|
|
+ assert isinstance(key, str)
|
|
|
+ # WORKAROUND method getnext() is sadly not available
|
|
|
+ for child_idx, child_node in enumerate(dict_node):
|
|
|
+ if child_node.tag == 'key' and child_node.text == key:
|
|
|
+ return dict_node[child_idx + 1]
|
|
|
+ raise KeyError()
|
|
|
+
|
|
|
+
|
|
|
+def get_itunes_dict_value(dict_node, key):
|
|
|
+ value_node = get_itunes_dict_value_node(dict_node, key)
|
|
|
+ if value_node.tag == 'string':
|
|
|
+ return value_node.text
|
|
|
+ elif value_node.tag == 'integer':
|
|
|
+ return int(value_node.text)
|
|
|
+ else:
|
|
|
+ return value_node
|
|
|
+
|
|
|
+
|
|
|
+def import_itunes_play_count(itunes_library_path):
|
|
|
+ lib = xml.etree.ElementTree.parse(itunes_library_path)
|
|
|
+ # WORKAROUND find('.//key[.="Library Persistent ID"]')
|
|
|
+ # -> SyntaxError: invalid predicate
|
|
|
+ lib_root_dict = lib.find('./dict')
|
|
|
+ lib_id = get_itunes_dict_value(lib_root_dict, 'Library Persistent ID')
|
|
|
+ assert isinstance(lib_id, str) and len(lib_id) > 0, lib_id
|
|
|
+ for track_node in get_itunes_dict_value(lib_root_dict, 'Tracks').iterfind('./dict'):
|
|
|
+ try:
|
|
|
+ track_url = get_itunes_dict_value(track_node, 'Location')
|
|
|
+ except KeyError:
|
|
|
+ track_url = None
|
|
|
+ if track_url and track_url.startswith('file://'):
|
|
|
+ print(track_url)
|
|
|
+
|
|
|
+
|
|
|
+def symuid(path, itunes_library_path=None):
|
|
|
+ if itunes_library_path:
|
|
|
+ import_itunes_play_count(itunes_library_path)
|
|
|
if os.path.isdir(path):
|
|
|
for dirpath, dirnames, filenames in os.walk(path):
|
|
|
for filename in filenames:
|
|
@@ -80,6 +121,7 @@ def _init_argparser():
|
|
|
import argparse
|
|
|
argparser = argparse.ArgumentParser(description=None)
|
|
|
argparser.add_argument('path')
|
|
|
+ argparser.add_argument('--itunes-library-path')
|
|
|
return argparser
|
|
|
|
|
|
|