fix: scan records the probed format + skips hidden album dirs (probe once)
Probe each album exactly once and thread the ProbeResult through for tags, quality, and format instead of hardcoding LibraryItem.format to "FLAC" and re-probing on the tag-fallback path. Also skip album-level directories starting with "." (previously only artist-level dirs were skipped).
This commit is contained in:
@@ -28,7 +28,7 @@ def _first_audio(album_path: str) -> str | None:
|
|||||||
return 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."""
|
"""Record have + monitored + followed for a resolved album. Returns True if newly imported."""
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
watched_id = None
|
watched_id = None
|
||||||
@@ -61,7 +61,7 @@ def _upsert(conn, target, quality_class: int, path: str) -> bool:
|
|||||||
'"qualityClass", "importedAt") '
|
'"qualityClass", "importedAt") '
|
||||||
"VALUES (gen_random_uuid()::text, NULL, %s, %s, %s, 'scan', %s, %s, now()) "
|
"VALUES (gen_random_uuid()::text, NULL, %s, %s, %s, 'scan', %s, %s, now()) "
|
||||||
'ON CONFLICT (artist, album) DO NOTHING',
|
'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
|
newly = cur.rowcount == 1 # a new LibraryItem means this album wasn't recorded before
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -81,21 +81,20 @@ def scan_library(conn: psycopg.Connection, resolver, probe: AudioProbe, dest_roo
|
|||||||
continue
|
continue
|
||||||
for album_folder in sorted(os.listdir(artist_path)):
|
for album_folder in sorted(os.listdir(artist_path)):
|
||||||
album_path = os.path.join(artist_path, album_folder)
|
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
|
continue
|
||||||
audio = _first_audio(album_path)
|
audio = _first_audio(album_path)
|
||||||
if audio is None:
|
if audio is None:
|
||||||
continue # no audio → not an album
|
continue # no audio → not an album
|
||||||
|
pr = probe.probe(audio)
|
||||||
album_name = _parse_album_dir(album_folder)
|
album_name = _parse_album_dir(album_folder)
|
||||||
target = resolver.resolve(artist_name, album_name) if artist_name and album_name else None
|
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
|
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):
|
if pr.artist and pr.album and (pr.artist, pr.album) != (artist_name, album_name):
|
||||||
target = resolver.resolve(pr.artist, pr.album)
|
target = resolver.resolve(pr.artist, pr.album)
|
||||||
if target is None:
|
if target is None:
|
||||||
skipped += 1
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
q = probe.probe(audio).quality_class
|
if _upsert(conn, target, pr.quality_class, pr.fmt, album_path):
|
||||||
if _upsert(conn, target, q, album_path):
|
|
||||||
imported += 1
|
imported += 1
|
||||||
return ScanResult(imported, skipped)
|
return ScanResult(imported, skipped)
|
||||||
|
|||||||
@@ -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))
|
second = scan_library(conn, resolver, FakeProbe(quality_class=3), dest_root=str(tmp_path))
|
||||||
assert second.imported == 0 # already recorded
|
assert second.imported == 0 # already recorded
|
||||||
assert _counts(conn) == {"LibraryItem": 1, "MonitoredRelease": 1, "WatchedArtist": 1}
|
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}
|
||||||
|
|||||||
Reference in New Issue
Block a user