Fix album completeness fix returning 400 for stale findings

When missing_tracks was empty (API unavailable at scan time) or the
album was completed between scan and fix, the handler returned a 400
error. Now it re-fetches missing tracks from Spotify/iTunes/Deezer at
fix time and auto-resolves findings where the album is already complete.
pull/253/head
Broque Thomas 2 months ago
parent 01b0b70515
commit 16be814a67

@ -1447,8 +1447,16 @@ class RepairWorker:
artist_name = details.get('artist', 'Unknown Artist')
spotify_album_id = details.get('spotify_album_id', '')
if not album_id or not missing_tracks:
return {'success': False, 'error': 'Missing album_id or missing_tracks in finding details'}
if not album_id:
return {'success': False, 'error': 'Missing album_id in finding details'}
# If missing_tracks list is empty (scanner couldn't identify them), try to fetch now
if not missing_tracks:
missing_tracks = self._refetch_missing_tracks(album_id, details)
if not missing_tracks:
# Refetch found 0 missing — album is now complete (stale finding)
return {'success': True, 'action': 'auto_resolve',
'message': f'Album "{album_title}" is now complete — no missing tracks found'}
# Phase 1: Gather context from existing album tracks
existing_tracks = self.db.get_tracks_by_album(album_id)
@ -1482,6 +1490,14 @@ class RepairWorker:
resolved_paths.append(rp)
filename_pattern = self._detect_filename_pattern(resolved_paths)
# Filter out tracks that have been added since the scan (stale finding)
owned_track_numbers = {t.track_number for t in existing_tracks if t.track_number}
missing_tracks = [mt for mt in missing_tracks
if mt.get('track_number') not in owned_track_numbers]
if not missing_tracks:
return {'success': True, 'action': 'auto_resolve',
'message': f'Album "{album_title}" is now complete — all tracks present'}
# Phase 2-4: Process each missing track
fixed_count = 0
wishlisted_count = 0
@ -1634,6 +1650,82 @@ class RepairWorker:
'details': track_details,
}
def _refetch_missing_tracks(self, album_id, details):
"""Re-fetch missing track list from APIs when the stored list is empty."""
spotify_album_id = details.get('spotify_album_id', '')
itunes_album_id = details.get('itunes_album_id', '')
deezer_album_id = details.get('deezer_album_id', '')
logger.debug("Refetch missing tracks for album %s: spotify=%s, itunes=%s, deezer=%s",
album_id, spotify_album_id, itunes_album_id, deezer_album_id)
# Get track numbers we already own
owned_numbers = set()
conn = None
try:
conn = self.db._get_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT track_number FROM tracks WHERE album_id = ? AND track_number IS NOT NULL",
(album_id,)
)
for row in cursor.fetchall():
owned_numbers.add(row[0])
except Exception:
return []
finally:
if conn:
conn.close()
# Try Spotify first
api_tracks = None
if spotify_album_id:
try:
sp = self.spotify_client
if sp and hasattr(sp, 'get_album_tracks'):
api_tracks = sp.get_album_tracks(spotify_album_id)
except Exception as e:
logger.debug("Refetch: Spotify album tracks failed for %s: %s", spotify_album_id, e)
# Try fallback client (iTunes/Deezer)
if not api_tracks or 'items' not in (api_tracks or {}):
itunes = self.itunes_client
if itunes:
is_deezer = type(itunes).__name__ == 'DeezerClient'
primary_id = deezer_album_id if is_deezer else itunes_album_id
secondary_id = itunes_album_id if is_deezer else deezer_album_id
for fid in [primary_id, secondary_id]:
if not fid:
continue
try:
api_tracks = itunes.get_album_tracks(fid)
if api_tracks and 'items' in api_tracks:
break
except Exception as e:
logger.debug("Refetch: fallback album tracks failed for %s: %s", fid, e)
if not api_tracks or 'items' not in api_tracks:
return []
missing = []
for item in api_tracks['items']:
tn = item.get('track_number')
if tn and tn not in owned_numbers:
track_artists = []
for a in item.get('artists', []):
if isinstance(a, dict):
track_artists.append(a.get('name', ''))
elif isinstance(a, str):
track_artists.append(a)
missing.append({
'track_number': tn,
'name': item.get('name', ''),
'disc_number': item.get('disc_number', 1),
'spotify_track_id': item.get('id', ''),
'duration_ms': item.get('duration_ms', 0),
'artists': track_artists,
})
return missing
def _perform_album_fill(self, candidate, album_id, album_title, artist_name,
track_name, track_number, disc_number,
album_folder, filename_pattern, download_folder):

Loading…
Cancel
Save