Browse Source

symuid-sync: accept multiple paths to files & folders

Fabian Peter Hammerle 1 month ago
parent
commit
b5225dcbc3
3 changed files with 55 additions and 31 deletions
  1. 1 1
      symuid/__init__.py
  2. 33 17
      symuid/sync.py
  3. 21 13
      tests/test_sync.py

+ 1 - 1
symuid/__init__.py

@@ -159,7 +159,7 @@ class Track:
         for dirpath, _, filenames in os.walk(root_path):
             for filename in filenames:
                 track_path = os.path.join(dirpath, filename)
-                if path_ignore_regex.search(track_path):
+                if path_ignore_regex and path_ignore_regex.search(track_path):
                     if ignored_cb is not None:
                         ignored_cb(track_path)
                 else:

+ 33 - 17
symuid/sync.py

@@ -1,5 +1,6 @@
 import argparse
 import collections
+import os
 import re
 import sys
 import typing
@@ -39,16 +40,9 @@ class _SyncPosition:
     def __repr__(self) -> str:
         return repr(vars(self))
 
-def sync(path, path_ignore_regex, show_ignored=False,
-         play_count_added_cb=None):
+def sync(tracks: typing.Iterator[symuid.Track], play_count_added_cb=None):
     sync_positions = collections.defaultdict(_SyncPosition)
-    for track in symuid.Track.walk(
-            root_path=path,
-            path_ignore_regex=path_ignore_regex,
-            ignored_cb=lambda p: show_ignored and _log_path(p, 'ignored'),
-            unsupported_cb=lambda p, e:
-                _log_path_error(p, 'unsupported type, skipped'),
-    ):
+    for track in tracks:
         if track.get_uuid() is None:
             track.assign_uuid(generate_uuid4_bytes())
             _log_path(track.path, 'assigned uuid {!r}'.format(
@@ -58,9 +52,29 @@ def sync(path, path_ignore_regex, show_ignored=False,
         sync_position.sync(play_count_added_cb=play_count_added_cb)
 
 
-def _init_argparser():
+def _walk_tracks(paths: typing.List[str], path_ignore_regex=None,
+                 ignored_cb=None, unsupported_cb=None) \
+        -> typing.Iterator[symuid.Track]:
+    for path in paths:
+        if os.path.isdir(path):
+            for track in symuid.Track.walk(
+                    root_path=path,
+                    path_ignore_regex=path_ignore_regex,
+                    ignored_cb=ignored_cb,
+                    unsupported_cb=unsupported_cb):
+                yield track
+        else:
+            yield symuid.Track(path)
+
+
+def _main():
     argparser = argparse.ArgumentParser(description=None)
-    argparser.add_argument('path')
+    argparser.add_argument(
+        'paths',
+        metavar='path',
+        nargs='+',
+        help='track or folder containing tracks',
+    )
     argparser.add_argument(
         '--path-ignore-regex',
         default=symuid.Track.PATH_DEFAULT_IGNORE_REGEX,
@@ -74,12 +88,14 @@ def _init_argparser():
         '--show-ignored',
         action='store_true',
     )
-    return argparser
-
-
-def _main():
-    argparser = _init_argparser()
     args = argparser.parse_args()
-    sync(**vars(args),
+    tracks = _walk_tracks(
+        paths=args.paths,
+        path_ignore_regex=args.path_ignore_regex,
+        ignored_cb=lambda p: args.show_ignored and _log_path(p, 'ignored'),
+        unsupported_cb=lambda p, e:
+        _log_path_error(p, 'unsupported type, skipped'),
+    )
+    sync(tracks=tracks,
          play_count_added_cb=lambda track, tag:
          _log_path(track.path, 'added play count tag {!r}'.format(tag)))

+ 21 - 13
tests/test_sync.py

@@ -1,5 +1,4 @@
 import os
-import re
 import shutil
 import unittest.mock
 
@@ -7,28 +6,37 @@ import pytest
 from symuid import PlayCount, Track
 from symuid._datetime import unix_epoch_time_to_datetime_utc
 from symuid._uuid import generate_uuid4_bytes
-from symuid.sync import _main, sync
-
-DUMMY_PATH_IGNORE_REGEX = re.compile(r'\.jpg$')
+from symuid.sync import _main, _walk_tracks, sync
 
 
 def test_add_uuid(empty_ogg_opus_path):
     assert Track(empty_ogg_opus_path).get_uuid() is None
-    sync(os.path.dirname(empty_ogg_opus_path),
-         path_ignore_regex=DUMMY_PATH_IGNORE_REGEX)
+    sync(_walk_tracks([os.path.dirname(empty_ogg_opus_path)]))
     assert Track(empty_ogg_opus_path).get_uuid() is not None
 
 
 def test_add_uuid_idempotent(empty_ogg_opus_path):
-    sync(os.path.dirname(empty_ogg_opus_path),
-         path_ignore_regex=DUMMY_PATH_IGNORE_REGEX)
+    sync(_walk_tracks([os.path.dirname(empty_ogg_opus_path)]))
     uuid = Track(empty_ogg_opus_path).get_uuid()
-    sync(os.path.dirname(empty_ogg_opus_path),
-         path_ignore_regex=DUMMY_PATH_IGNORE_REGEX)
+    sync(_walk_tracks([os.path.dirname(empty_ogg_opus_path)]))
     assert Track(empty_ogg_opus_path).get_uuid() == uuid
 
 
-def test_add_uuid_main(empty_ogg_opus_path):
+def test_add_uuid_main_file(empty_ogg_opus_path):
+    assert Track(empty_ogg_opus_path).get_uuid() is None
+    with unittest.mock.patch('sys.argv', ['', empty_ogg_opus_path]):
+        _main()
+    assert Track(empty_ogg_opus_path).get_uuid() is not None
+
+
+def test_add_uuid_main_multiple_files(empty_ogg_opus_path, empty_ogg_vorbis_path):
+    with unittest.mock.patch('sys.argv', ['', empty_ogg_opus_path, empty_ogg_vorbis_path]):
+        _main()
+    assert Track(empty_ogg_opus_path).get_uuid() is not None
+    assert Track(empty_ogg_vorbis_path).get_uuid() is not None
+
+
+def test_add_uuid_main_dir(empty_ogg_opus_path):
     assert Track(empty_ogg_opus_path).get_uuid() is None
     with unittest.mock.patch('sys.argv', ['', os.path.dirname(empty_ogg_opus_path)]):
         _main()
@@ -92,7 +100,7 @@ def test_sync_play_count(tmpdir, tracks_dir_path):
         register_dt=unix_epoch_time_to_datetime_utc(3),
         count=4,
     ))
-    sync(tmpdir, path_ignore_regex=DUMMY_PATH_IGNORE_REGEX)
+    sync((track_a1, track_a2, track_a3, track_b))
     play_counts_a1 = set(Track(tmpdir.join('a1.mp3')).get_play_counts())
     play_counts_a2 = set(Track(tmpdir.join('a2.ogg')).get_play_counts())
     play_counts_a3 = set(Track(tmpdir.join('a3.opus')).get_play_counts())
@@ -129,7 +137,7 @@ def test_sync_play_count_callback(tmpdir, tracks_dir_path):
         count=21,
     ))
     play_count_added_cb = unittest.mock.MagicMock()
-    sync(tmpdir, path_ignore_regex=DUMMY_PATH_IGNORE_REGEX,
+    sync(_walk_tracks([tmpdir]),
          play_count_added_cb=play_count_added_cb)
     play_count_added_cb.assert_called_once_with(
         track_a2, ('symuid:pcnt:cmus:lib1:0', ['21']))