diff --git a/worker/lyra_worker/scan.py b/worker/lyra_worker/scan.py index 692394a..6cb849d 100644 --- a/worker/lyra_worker/scan.py +++ b/worker/lyra_worker/scan.py @@ -28,7 +28,7 @@ def _first_audio(album_path: str) -> str | None: return None -def _upsert(conn, target, quality_class: int, path: str) -> bool: +def _upsert(conn, target, quality_class: int, fmt: str, path: str) -> bool: """Record have + monitored + followed for a resolved album. Returns True if newly imported.""" with conn.cursor() as cur: watched_id = None @@ -61,7 +61,7 @@ def _upsert(conn, target, quality_class: int, path: str) -> bool: '"qualityClass", "importedAt") ' "VALUES (gen_random_uuid()::text, NULL, %s, %s, %s, 'scan', %s, %s, now()) " 'ON CONFLICT (artist, album) DO NOTHING', - (target.artist, target.album, path, "FLAC", quality_class), + (target.artist, target.album, path, fmt, quality_class), ) newly = cur.rowcount == 1 # a new LibraryItem means this album wasn't recorded before conn.commit() @@ -81,21 +81,20 @@ def scan_library(conn: psycopg.Connection, resolver, probe: AudioProbe, dest_roo continue for album_folder in sorted(os.listdir(artist_path)): album_path = os.path.join(artist_path, album_folder) - if not os.path.isdir(album_path): + if album_folder.startswith(".") or not os.path.isdir(album_path): continue audio = _first_audio(album_path) if audio is None: continue # no audio → not an album + pr = probe.probe(audio) album_name = _parse_album_dir(album_folder) target = resolver.resolve(artist_name, album_name) if artist_name and album_name else None if target is None: # fall back to the file's embedded tags - pr = probe.probe(audio) if pr.artist and pr.album and (pr.artist, pr.album) != (artist_name, album_name): target = resolver.resolve(pr.artist, pr.album) if target is None: skipped += 1 continue - q = probe.probe(audio).quality_class - if _upsert(conn, target, q, album_path): + if _upsert(conn, target, pr.quality_class, pr.fmt, album_path): imported += 1 return ScanResult(imported, skipped) diff --git a/worker/tests/test_scan.py b/worker/tests/test_scan.py index e28716f..67eb877 100644 --- a/worker/tests/test_scan.py +++ b/worker/tests/test_scan.py @@ -96,3 +96,24 @@ def test_scan_is_idempotent(conn, tmp_path): second = scan_library(conn, resolver, FakeProbe(quality_class=3), dest_root=str(tmp_path)) assert second.imported == 0 # already recorded assert _counts(conn) == {"LibraryItem": 1, "MonitoredRelease": 1, "WatchedArtist": 1} + + +def test_scan_records_the_probed_format(conn, tmp_path): + _album(tmp_path, "John Mayer", "Sob Rock (2021)") + resolver = FakeResolver({ + ("John Mayer", "Sob Rock"): MBTarget( + artist="John Mayer", album="Sob Rock", year=2021, rg_mbid="rg2", artist_mbid="a1"), + }) + scan_library(conn, resolver, FakeProbe(fmt="MP3", quality_class=1), dest_root=str(tmp_path)) + with conn.cursor() as cur: + cur.execute('SELECT format, "qualityClass" FROM "LibraryItem" WHERE album=%s', ("Sob Rock",)) + assert cur.fetchone() == ("MP3", 1) # the probed format is recorded, not a hardcoded FLAC + + +def test_scan_skips_hidden_album_dirs(conn, tmp_path): + hidden = tmp_path / "John Mayer" / ".sync" + hidden.mkdir(parents=True) + (hidden / "01 Track.flac").write_bytes(b"") + result = scan_library(conn, FakeResolver({}), FakeProbe(), dest_root=str(tmp_path)) + assert (result.imported, result.skipped) == (0, 0) # hidden album dir not scanned + assert _counts(conn) == {"LibraryItem": 0, "MonitoredRelease": 0, "WatchedArtist": 0}