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.
This commit is contained in:
parent
01b0b70515
commit
16be814a67
1 changed files with 94 additions and 2 deletions
|
|
@ -1447,8 +1447,16 @@ class RepairWorker:
|
||||||
artist_name = details.get('artist', 'Unknown Artist')
|
artist_name = details.get('artist', 'Unknown Artist')
|
||||||
spotify_album_id = details.get('spotify_album_id', '')
|
spotify_album_id = details.get('spotify_album_id', '')
|
||||||
|
|
||||||
if not album_id or not missing_tracks:
|
if not album_id:
|
||||||
return {'success': False, 'error': 'Missing album_id or missing_tracks in finding details'}
|
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
|
# Phase 1: Gather context from existing album tracks
|
||||||
existing_tracks = self.db.get_tracks_by_album(album_id)
|
existing_tracks = self.db.get_tracks_by_album(album_id)
|
||||||
|
|
@ -1482,6 +1490,14 @@ class RepairWorker:
|
||||||
resolved_paths.append(rp)
|
resolved_paths.append(rp)
|
||||||
filename_pattern = self._detect_filename_pattern(resolved_paths)
|
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
|
# Phase 2-4: Process each missing track
|
||||||
fixed_count = 0
|
fixed_count = 0
|
||||||
wishlisted_count = 0
|
wishlisted_count = 0
|
||||||
|
|
@ -1634,6 +1650,82 @@ class RepairWorker:
|
||||||
'details': track_details,
|
'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,
|
def _perform_album_fill(self, candidate, album_id, album_title, artist_name,
|
||||||
track_name, track_number, disc_number,
|
track_name, track_number, disc_number,
|
||||||
album_folder, filename_pattern, download_folder):
|
album_folder, filename_pattern, download_folder):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue