Preserve server source during album fill
- derive the destination server_source from the target album context - write it on copied rows and retarget moved rows too - cover the copy branch with a regression test
This commit is contained in:
parent
e7e32652f5
commit
8603cd6680
2 changed files with 57 additions and 17 deletions
|
|
@ -12,6 +12,7 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import sqlite3
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
|
|
@ -2231,6 +2232,46 @@ class RepairWorker:
|
|||
album_folder, filename_pattern, download_folder):
|
||||
"""Move or copy a candidate track into the album folder and update DB."""
|
||||
try:
|
||||
def _fallback_server_source():
|
||||
if getattr(candidate, 'server_source', None):
|
||||
return candidate.server_source
|
||||
if self._config_manager:
|
||||
getter = getattr(self._config_manager, 'get_active_media_server', None)
|
||||
if callable(getter):
|
||||
return getter() or 'plex'
|
||||
return self._config_manager.get('active_media_server', 'plex')
|
||||
return 'plex'
|
||||
|
||||
def _resolve_target_context(cursor):
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT artist_id, server_source
|
||||
FROM tracks
|
||||
WHERE album_id = ?
|
||||
ORDER BY track_number, title
|
||||
LIMIT 1
|
||||
""",
|
||||
(album_id,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return row[0] or candidate.artist_id, row[1] or _fallback_server_source()
|
||||
|
||||
try:
|
||||
cursor.execute(
|
||||
"SELECT artist_id, server_source FROM albums WHERE id = ? LIMIT 1",
|
||||
(album_id,),
|
||||
)
|
||||
except sqlite3.OperationalError:
|
||||
row = None
|
||||
else:
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row:
|
||||
return row[0] or candidate.artist_id, row[1] or _fallback_server_source()
|
||||
|
||||
return candidate.artist_id, _fallback_server_source()
|
||||
|
||||
# Resolve source file
|
||||
src_path = _resolve_file_path(candidate.file_path, self.transfer_folder, download_folder, config_manager=self._config_manager)
|
||||
if not src_path or not os.path.exists(src_path):
|
||||
|
|
@ -2264,18 +2305,15 @@ class RepairWorker:
|
|||
# Update existing DB record to point to new album and path
|
||||
conn = self.db._get_connection()
|
||||
cursor = conn.cursor()
|
||||
# Get the target album's artist_id for consistency
|
||||
cursor.execute("SELECT artist_id FROM tracks WHERE album_id = ? LIMIT 1", (album_id,))
|
||||
artist_row = cursor.fetchone()
|
||||
target_artist_id = artist_row[0] if artist_row else candidate.artist_id
|
||||
target_artist_id, target_server_source = _resolve_target_context(cursor)
|
||||
cursor.execute("""
|
||||
UPDATE tracks
|
||||
SET album_id = ?, artist_id = ?, title = ?,
|
||||
file_path = ?, track_number = ?,
|
||||
file_path = ?, track_number = ?, server_source = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
""", (album_id, target_artist_id, track_name,
|
||||
target_path, track_number, candidate.id))
|
||||
target_path, track_number, target_server_source, candidate.id))
|
||||
|
||||
# Clean up the source single's album if it's now empty
|
||||
cursor.execute("SELECT COUNT(*) FROM tracks WHERE album_id = ?", (candidate.album_id,))
|
||||
|
|
@ -2300,17 +2338,14 @@ class RepairWorker:
|
|||
|
||||
conn = self.db._get_connection()
|
||||
cursor = conn.cursor()
|
||||
# Get artist_id from existing album tracks
|
||||
cursor.execute("SELECT artist_id FROM tracks WHERE album_id = ? LIMIT 1", (album_id,))
|
||||
artist_row = cursor.fetchone()
|
||||
target_artist_id = artist_row[0] if artist_row else candidate.artist_id
|
||||
target_artist_id, target_server_source = _resolve_target_context(cursor)
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO tracks (id, album_id, artist_id, title, track_number, duration,
|
||||
file_path, bitrate, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
file_path, bitrate, server_source, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
""", (new_track_id, album_id, target_artist_id, track_name, track_number,
|
||||
candidate.duration, target_path, candidate.bitrate))
|
||||
candidate.duration, target_path, candidate.bitrate, target_server_source))
|
||||
conn.commit()
|
||||
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
duration INTEGER,
|
||||
file_path TEXT,
|
||||
bitrate INTEGER,
|
||||
server_source TEXT,
|
||||
created_at TEXT,
|
||||
updated_at TEXT
|
||||
)
|
||||
|
|
@ -69,10 +70,10 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO tracks (id, album_id, artist_id, title, track_number, duration, file_path, bitrate, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
INSERT INTO tracks (id, album_id, artist_id, title, track_number, duration, file_path, bitrate, server_source, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
""",
|
||||
("target-track-1", "target-album", "target-artist", "Existing Track", 1, 180000, str(src_path), 320),
|
||||
("target-track-1", "target-album", "target-artist", "Existing Track", 1, 180000, str(src_path), 320, "navidrome"),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
|
@ -89,6 +90,7 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
duration=180000,
|
||||
file_path=str(src_path),
|
||||
bitrate=320,
|
||||
server_source="soulsync",
|
||||
),
|
||||
SimpleNamespace(
|
||||
id="source-track-2",
|
||||
|
|
@ -97,6 +99,7 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
duration=181000,
|
||||
file_path=str(src_path),
|
||||
bitrate=320,
|
||||
server_source="soulsync",
|
||||
),
|
||||
]
|
||||
|
||||
|
|
@ -120,6 +123,7 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
duration=180000,
|
||||
file_path=str(src_path),
|
||||
bitrate=320,
|
||||
server_source="soulsync",
|
||||
),
|
||||
album_id="target-album",
|
||||
album_title="Target Album",
|
||||
|
|
@ -137,7 +141,7 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
|
||||
with sqlite3.connect(db_path) as verify_conn:
|
||||
row = verify_conn.execute(
|
||||
"SELECT id, title, file_path FROM tracks WHERE title = ?",
|
||||
"SELECT id, title, file_path, server_source FROM tracks WHERE title = ?",
|
||||
("New Track",),
|
||||
).fetchone()
|
||||
assert row is not None
|
||||
|
|
@ -145,4 +149,5 @@ def test_perform_album_fill_copy_branch_generates_track_id(tmp_path, monkeypatch
|
|||
assert row[0].startswith("album_fill_source-track-1_deadbeef")
|
||||
assert row[1] == "New Track"
|
||||
assert Path(row[2]).exists()
|
||||
assert row[3] == "navidrome"
|
||||
assert verify_conn.execute("SELECT COUNT(*) FROM tracks WHERE id IS NULL").fetchone()[0] == 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue