Fix null-pointer error in acoustid_scanner
The root cause (null track ids) needs to be solved elsewhere, but this is a band-aid for now
This commit is contained in:
parent
2429d87dbe
commit
88e2527b96
2 changed files with 99 additions and 2 deletions
|
|
@ -84,14 +84,16 @@ class AcoustIDScannerJob(RepairJob):
|
||||||
checkpoint_id = context.config_manager.get(
|
checkpoint_id = context.config_manager.get(
|
||||||
f'repair.jobs.{self.job_id}.checkpoint_id', None
|
f'repair.jobs.{self.job_id}.checkpoint_id', None
|
||||||
)
|
)
|
||||||
|
if checkpoint_id is not None:
|
||||||
|
checkpoint_id = str(checkpoint_id)
|
||||||
|
|
||||||
# Build ordered list of (track_id, info) sorted by ID for deterministic order
|
# Build ordered list of (track_id, info) sorted by ID for deterministic order
|
||||||
track_list = sorted(db_tracks.items(), key=lambda x: x[0])
|
track_list = sorted(db_tracks.items(), key=lambda x: str(x[0]))
|
||||||
|
|
||||||
# Skip past checkpoint if resuming
|
# Skip past checkpoint if resuming
|
||||||
if checkpoint_id is not None:
|
if checkpoint_id is not None:
|
||||||
original_len = len(track_list)
|
original_len = len(track_list)
|
||||||
track_list = [(tid, info) for tid, info in track_list if tid > checkpoint_id]
|
track_list = [(tid, info) for tid, info in track_list if str(tid) > checkpoint_id]
|
||||||
if len(track_list) < original_len:
|
if len(track_list) < original_len:
|
||||||
logger.info("Resuming AcoustID scan from checkpoint ID %s (%d tracks remaining)",
|
logger.info("Resuming AcoustID scan from checkpoint ID %s (%d tracks remaining)",
|
||||||
checkpoint_id, len(track_list))
|
checkpoint_id, len(track_list))
|
||||||
|
|
@ -258,6 +260,13 @@ class AcoustIDScannerJob(RepairJob):
|
||||||
""")
|
""")
|
||||||
for row in cursor.fetchall():
|
for row in cursor.fetchall():
|
||||||
track_id = row[0]
|
track_id = row[0]
|
||||||
|
if track_id is None:
|
||||||
|
logger.warning(
|
||||||
|
"Skipping track row with null ID while loading AcoustID scan candidates: %s",
|
||||||
|
row[3] or "<unknown file>",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
track_id = str(track_id)
|
||||||
tracks[track_id] = {
|
tracks[track_id] = {
|
||||||
'title': row[1] or '',
|
'title': row[1] or '',
|
||||||
'artist': row[2] or '',
|
'artist': row[2] or '',
|
||||||
|
|
|
||||||
88
tests/test_acoustid_scanner.py
Normal file
88
tests/test_acoustid_scanner.py
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from core.repair_jobs.acoustid_scanner import AcoustIDScannerJob
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeCursor:
|
||||||
|
def __init__(self, rows):
|
||||||
|
self._rows = rows
|
||||||
|
self.executed = []
|
||||||
|
|
||||||
|
def execute(self, query, params=None):
|
||||||
|
self.executed.append((query, params))
|
||||||
|
return self
|
||||||
|
|
||||||
|
def fetchall(self):
|
||||||
|
return self._rows
|
||||||
|
|
||||||
|
def fetchone(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeConnection:
|
||||||
|
def __init__(self, rows):
|
||||||
|
self._cursor = _FakeCursor(rows)
|
||||||
|
|
||||||
|
def cursor(self):
|
||||||
|
return self._cursor
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _make_context(rows):
|
||||||
|
conn = _FakeConnection(rows)
|
||||||
|
config_manager = SimpleNamespace(
|
||||||
|
get=lambda key, default=None: default,
|
||||||
|
set=lambda *args, **kwargs: None,
|
||||||
|
)
|
||||||
|
db = SimpleNamespace(_get_connection=lambda: conn)
|
||||||
|
return SimpleNamespace(
|
||||||
|
db=db,
|
||||||
|
transfer_folder="/music",
|
||||||
|
config_manager=config_manager,
|
||||||
|
acoustid_client=object(),
|
||||||
|
create_finding=None,
|
||||||
|
report_progress=lambda **kwargs: None,
|
||||||
|
update_progress=lambda *args, **kwargs: None,
|
||||||
|
check_stop=lambda: False,
|
||||||
|
wait_if_paused=lambda: False,
|
||||||
|
sleep_or_stop=lambda *args, **kwargs: False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_db_tracks_skips_null_ids_and_normalizes_track_ids():
|
||||||
|
job = AcoustIDScannerJob()
|
||||||
|
context = _make_context([
|
||||||
|
(None, "Broken Track", "Artist", "/music/broken.flac", 1, "Album", None, None),
|
||||||
|
(42, "Good Track", "Artist", "/music/good.flac", 2, "Album", "album-thumb", "artist-thumb"),
|
||||||
|
])
|
||||||
|
|
||||||
|
tracks = job._load_db_tracks(context)
|
||||||
|
|
||||||
|
assert list(tracks.keys()) == ["42"]
|
||||||
|
assert tracks["42"]["title"] == "Good Track"
|
||||||
|
assert tracks["42"]["artist"] == "Artist"
|
||||||
|
|
||||||
|
|
||||||
|
def test_scan_handles_mixed_track_id_types(monkeypatch):
|
||||||
|
job = AcoustIDScannerJob()
|
||||||
|
context = _make_context([
|
||||||
|
(None, "Broken Track", "Artist", "/music/broken.flac", 1, "Album", None, None),
|
||||||
|
(42, "Good Track", "Artist", "/music/good.flac", 2, "Album", "album-thumb", "artist-thumb"),
|
||||||
|
])
|
||||||
|
|
||||||
|
monkeypatch.setattr(job, "_resolve_path", lambda file_path, _context: file_path)
|
||||||
|
|
||||||
|
scanned_track_ids = []
|
||||||
|
|
||||||
|
def fake_scan_file(fpath, track_id, expected, acoustid_client, context, result,
|
||||||
|
fp_threshold, title_threshold, artist_threshold):
|
||||||
|
scanned_track_ids.append(track_id)
|
||||||
|
|
||||||
|
monkeypatch.setattr(job, "_scan_file", fake_scan_file)
|
||||||
|
|
||||||
|
result = job.scan(context)
|
||||||
|
|
||||||
|
assert result.scanned == 1
|
||||||
|
assert scanned_track_ids == ["42"]
|
||||||
Loading…
Reference in a new issue