Fix Deezer ARL sync/download rehydration and add album data caching
Sync rehydration: after loading Deezer ARL playlists, checks each for active syncs via /api/sync/status and re-attaches polling with live card updates. Download rehydration: rehydrateModal now handles deezer_arl_ playlist IDs, and openDownloadMissingModal routes cache misses to the correct ARL endpoint. Fix All now prompts for dead file action. Album data caching: get_playlist_tracks now checks the metadata cache before fetching album release dates from the Deezer API. Cache hits are instant, misses are fetched and stored for future use across all playlists. Import fixed from core.metadata_cache instead of web_server to avoid circular dependency.
This commit is contained in:
parent
37d325ee10
commit
4178c1eb56
2 changed files with 75 additions and 5 deletions
|
|
@ -315,20 +315,41 @@ class DeezerDownloadClient:
|
|||
break
|
||||
raw_tracks.extend(page_tracks)
|
||||
|
||||
# Batch-fetch release dates for unique albums
|
||||
# Batch-fetch release dates for unique albums (cache-first)
|
||||
album_ids = set()
|
||||
for t in raw_tracks:
|
||||
aid = t.get('album', {}).get('id')
|
||||
if aid:
|
||||
album_ids.add(str(aid))
|
||||
album_release_dates = {}
|
||||
try:
|
||||
from core.metadata_cache import get_metadata_cache
|
||||
cache = get_metadata_cache()
|
||||
except Exception:
|
||||
cache = None
|
||||
for aid in album_ids:
|
||||
# Check metadata cache first
|
||||
if cache:
|
||||
try:
|
||||
cached = cache.get_entity('deezer', 'album', aid)
|
||||
if cached and cached.get('release_date'):
|
||||
album_release_dates[aid] = cached['release_date']
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
# Cache miss — fetch from API
|
||||
try:
|
||||
time.sleep(0.3) # Respect rate limits
|
||||
a_resp = self._session.get(f'https://api.deezer.com/album/{aid}', timeout=10)
|
||||
if a_resp.ok:
|
||||
a_data = a_resp.json()
|
||||
album_release_dates[aid] = a_data.get('release_date', '')
|
||||
# Store in metadata cache for future use
|
||||
if cache:
|
||||
try:
|
||||
cache.store_entity('deezer', 'album', aid, a_data)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -10195,7 +10195,29 @@ async function rehydrateModal(processInfo, userRequested = false) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Handle regular Spotify playlist processes
|
||||
// Handle Deezer ARL playlist processes — ensure playlist data is in spotifyPlaylists for modal reuse
|
||||
if (playlist_id.startsWith('deezer_arl_') && !spotifyPlaylists.find(p => p.id === playlist_id)) {
|
||||
const rawId = playlist_id.replace('deezer_arl_', '');
|
||||
const deezerPlaylist = deezerArlPlaylists.find(p => String(p.id) === rawId);
|
||||
if (deezerPlaylist) {
|
||||
spotifyPlaylists.push({
|
||||
id: playlist_id,
|
||||
name: deezerPlaylist.name,
|
||||
track_count: deezerPlaylist.track_count || 0,
|
||||
image_url: deezerPlaylist.image_url || '',
|
||||
owner: deezerPlaylist.owner || '',
|
||||
});
|
||||
} else {
|
||||
// Playlists not loaded yet — use process info as fallback
|
||||
spotifyPlaylists.push({
|
||||
id: playlist_id,
|
||||
name: playlist_name || 'Deezer Playlist',
|
||||
track_count: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle regular Spotify / Deezer ARL playlist processes
|
||||
let playlistData = spotifyPlaylists.find(p => p.id === playlist_id);
|
||||
if (!playlistData) {
|
||||
console.warn(`Cannot rehydrate modal: Playlist data for ${playlist_id} not loaded.`);
|
||||
|
|
@ -11707,7 +11729,10 @@ async function openDownloadMissingModal(playlistId) {
|
|||
let tracks = playlistTrackCache[playlistId];
|
||||
if (!tracks) {
|
||||
try {
|
||||
const response = await fetch(`/api/spotify/playlist/${playlistId}`);
|
||||
const fetchUrl = playlistId.startsWith('deezer_arl_')
|
||||
? `/api/deezer/arl-playlist/${playlistId.replace('deezer_arl_', '')}`
|
||||
: `/api/spotify/playlist/${playlistId}`;
|
||||
const response = await fetch(fetchUrl);
|
||||
const fullPlaylist = await response.json();
|
||||
if (fullPlaylist.error) throw new Error(fullPlaylist.error);
|
||||
tracks = fullPlaylist.tracks;
|
||||
|
|
@ -26570,6 +26595,27 @@ async function loadDeezerArlPlaylists() {
|
|||
renderDeezerArlPlaylists();
|
||||
deezerArlPlaylistsLoaded = true;
|
||||
|
||||
// Check for active syncs or downloads and rehydrate UI
|
||||
await checkForActiveProcesses();
|
||||
for (const p of deezerArlPlaylists) {
|
||||
const arlId = `deezer_arl_${p.id}`;
|
||||
try {
|
||||
const syncResp = await fetch(`/api/sync/status/${arlId}`);
|
||||
if (syncResp.ok) {
|
||||
const syncState = await syncResp.json();
|
||||
if (syncState.status === 'syncing') {
|
||||
// Re-attach sync polling and update card UI
|
||||
if (!spotifyPlaylists.find(sp => sp.id === arlId)) {
|
||||
spotifyPlaylists.push({ id: arlId, name: p.name, track_count: p.track_count || 0, image_url: p.image_url || '', owner: p.owner || '' });
|
||||
}
|
||||
updateCardToSyncing(arlId, syncState.progress?.progress || 0, syncState.progress);
|
||||
startSyncPolling(arlId);
|
||||
console.log(`🔄 Rehydrated active sync for Deezer ARL playlist: ${p.name}`);
|
||||
}
|
||||
}
|
||||
} catch (e) { /* No active sync — normal */ }
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
container.innerHTML = `<div class="playlist-placeholder">❌ Error: ${error.message}</div>`;
|
||||
showToast(`Error loading Deezer playlists: ${error.message}`, 'error');
|
||||
|
|
@ -62894,9 +62940,12 @@ async function fixAllMatchingFindings() {
|
|||
const jobId = jobFilter ? jobFilter.value : '';
|
||||
const severity = severityFilter ? severityFilter.value : '';
|
||||
|
||||
// If fixing orphan files, prompt for action FIRST (staging vs delete)
|
||||
// If fixing orphan files or dead files, prompt for action FIRST
|
||||
let fixAction = null;
|
||||
if (jobId === 'orphan_file_detector' || _isMassOrphanFix(jobId, _repairFindingsTotal)) {
|
||||
if (jobId === 'dead_file_cleaner') {
|
||||
fixAction = await _promptDeadFileAction();
|
||||
if (!fixAction) return;
|
||||
} else if (jobId === 'orphan_file_detector' || _isMassOrphanFix(jobId, _repairFindingsTotal)) {
|
||||
fixAction = await _promptOrphanAction();
|
||||
if (!fixAction) return;
|
||||
// Confirm before proceeding
|
||||
|
|
|
|||
Loading…
Reference in a new issue