download missing functionality

pull/15/head
Broque Thomas 9 months ago
parent bd2866b348
commit b2c1d21bf0

@ -4382,6 +4382,14 @@ def _on_download_completed(batch_id, task_id, success=True):
# Mark batch as complete and process wishlist outside of lock to prevent deadlocks
batch['phase'] = 'complete'
# Update YouTube playlist phase to 'download_complete' if this is a YouTube playlist
playlist_id = batch.get('playlist_id')
if playlist_id and playlist_id.startswith('youtube_'):
url_hash = playlist_id.replace('youtube_', '')
if url_hash in youtube_playlist_states:
youtube_playlist_states[url_hash]['phase'] = 'download_complete'
print(f"📋 Updated YouTube playlist {url_hash} to download_complete phase")
print(f"🎉 [Batch Manager] Batch {batch_id} complete - stopping monitor")
download_monitor.stop_monitoring(batch_id)
@ -4459,6 +4467,13 @@ def _run_full_missing_tracks_process(batch_id, playlist_id, tracks_json):
with tasks_lock:
if batch_id in download_batches:
download_batches[batch_id]['phase'] = 'complete'
# Update YouTube playlist phase to 'download_complete' if this is a YouTube playlist
if playlist_id.startswith('youtube_'):
url_hash = playlist_id.replace('youtube_', '')
if url_hash in youtube_playlist_states:
youtube_playlist_states[url_hash]['phase'] = 'download_complete'
print(f"📋 Updated YouTube playlist {url_hash} to download_complete phase (no missing tracks)")
return
print(f" transitioning batch {batch_id} to download phase with {len(missing_tracks)} tracks.")
@ -4490,6 +4505,13 @@ def _run_full_missing_tracks_process(batch_id, playlist_id, tracks_json):
if batch_id in download_batches:
download_batches[batch_id]['phase'] = 'error'
download_batches[batch_id]['error'] = str(e)
# Reset YouTube playlist phase to 'discovered' if this is a YouTube playlist on error
if playlist_id.startswith('youtube_'):
url_hash = playlist_id.replace('youtube_', '')
if url_hash in youtube_playlist_states:
youtube_playlist_states[url_hash]['phase'] = 'discovered'
print(f"📋 Reset YouTube playlist {url_hash} to discovered phase (error)")
def _download_track_worker(task_id, batch_id=None):
"""
@ -4956,7 +4978,8 @@ def get_active_processes():
"discovery_progress": state['discovery_progress'],
"spotify_matches": state['spotify_matches'],
"spotify_total": state['spotify_total'],
"converted_spotify_playlist_id": state.get('converted_spotify_playlist_id')
"converted_spotify_playlist_id": state.get('converted_spotify_playlist_id'),
"download_process_id": state.get('download_process_id') # batch_id for download modal rehydration
})
print(f"📊 Active processes check: {len([p for p in active_processes if p['type'] == 'batch'])} download batches, {len([p for p in active_processes if p['type'] == 'youtube_playlist'])} YouTube playlists")
@ -5196,6 +5219,14 @@ def cancel_batch(batch_id):
# Mark batch as cancelled
download_batches[batch_id]['phase'] = 'cancelled'
# Reset YouTube playlist phase to 'discovered' if this is a YouTube playlist
playlist_id = download_batches[batch_id].get('playlist_id')
if playlist_id and playlist_id.startswith('youtube_'):
url_hash = playlist_id.replace('youtube_', '')
if url_hash in youtube_playlist_states:
youtube_playlist_states[url_hash]['phase'] = 'discovered'
print(f"📋 Reset YouTube playlist {url_hash} to discovered phase (batch cancelled)")
# Cancel all individual tasks in the batch
cancelled_count = 0
for task_id in download_batches[batch_id].get('queue', []):
@ -5296,6 +5327,15 @@ def start_missing_tracks_process(playlist_id):
'analysis_results': []
}
# Link YouTube playlist to download process if this is a YouTube playlist
if playlist_id.startswith('youtube_'):
url_hash = playlist_id.replace('youtube_', '')
if url_hash in youtube_playlist_states:
youtube_playlist_states[url_hash]['download_process_id'] = batch_id
youtube_playlist_states[url_hash]['phase'] = 'downloading'
youtube_playlist_states[url_hash]['converted_spotify_playlist_id'] = playlist_id
print(f"🔗 Linked YouTube playlist {url_hash} to download process {batch_id} (converted ID: {playlist_id})")
missing_download_executor.submit(_run_full_missing_tracks_process, batch_id, playlist_id, tracks)
return jsonify({
@ -5543,6 +5583,7 @@ def parse_youtube_playlist_endpoint():
'url': url,
'sync_playlist_id': None,
'converted_spotify_playlist_id': None,
'download_process_id': None, # Track associated download missing tracks process
'created_at': time.time(),
'last_accessed': time.time(),
'discovery_future': None,
@ -5903,6 +5944,8 @@ def get_all_youtube_playlists():
'discovery_progress': state['discovery_progress'],
'spotify_matches': state['spotify_matches'],
'spotify_total': state['spotify_total'],
'converted_spotify_playlist_id': state.get('converted_spotify_playlist_id'),
'download_process_id': state.get('download_process_id'),
'created_at': state['created_at'],
'last_accessed': state['last_accessed']
}

@ -1790,9 +1790,8 @@ async function checkForActiveProcesses() {
}
}
// Note: YouTube playlists are now handled by loadYouTubePlaylistsFromBackend()
// in loadSyncData(), which provides more complete data than active processes.
// Skip YouTube rehydration here to avoid conflicts and duplicate cards.
// Note: YouTube playlists are handled by loadYouTubePlaylistsFromBackend() and rehydrateYouTubePlaylist()
// in loadSyncData(), which provides more complete data than active processes and handles download modal rehydration.
console.log(` Skipping ${youtubeProcesses.length} YouTube playlists - handled by full backend loading`);
}
} catch (error) {
@ -1804,6 +1803,12 @@ async function rehydrateModal(processInfo, userRequested = false) {
const { playlist_id, playlist_name, batch_id } = processInfo;
console.log(`💧 Rehydrating modal for "${playlist_name}" (batch: ${batch_id}) - User requested: ${userRequested}`);
// Handle YouTube virtual playlists - skip rehydration here, handled by YouTube system
if (playlist_id.startsWith('youtube_')) {
console.log(`⏭️ Skipping YouTube virtual playlist rehydration - handled by YouTube system`);
return;
}
// Handle wishlist processes specially
if (playlist_id === "wishlist") {
console.log(`🔍 Current activeDownloadProcesses keys: [${Object.keys(activeDownloadProcesses).join(', ')}]`);
@ -1957,6 +1962,57 @@ async function loadYouTubePlaylistsFromBackend() {
}
}
// Rehydrate download modals for YouTube playlists in downloading/download_complete phases
for (const playlistInfo of playlists) {
if ((playlistInfo.phase === 'downloading' || playlistInfo.phase === 'download_complete') &&
playlistInfo.converted_spotify_playlist_id && playlistInfo.download_process_id) {
const convertedPlaylistId = playlistInfo.converted_spotify_playlist_id;
if (!activeDownloadProcesses[convertedPlaylistId]) {
console.log(`💧 Rehydrating download modal for YouTube playlist: ${playlistInfo.playlist.name}`);
try {
// Create the download modal using the YouTube-specific function
const spotifyTracks = youtubePlaylistStates[playlistInfo.url_hash]?.discoveryResults
?.filter(result => result.spotify_data)
?.map(result => result.spotify_data) || [];
if (spotifyTracks.length > 0) {
await openDownloadMissingModalForYouTube(
convertedPlaylistId,
playlistInfo.playlist.name,
spotifyTracks
);
// Set the modal to running state with the correct batch ID
const process = activeDownloadProcesses[convertedPlaylistId];
if (process) {
process.status = 'running';
process.batchId = playlistInfo.download_process_id;
// Update UI to running state
const beginBtn = document.getElementById(`begin-analysis-btn-${convertedPlaylistId}`);
const cancelBtn = document.getElementById(`cancel-all-btn-${convertedPlaylistId}`);
if (beginBtn) beginBtn.style.display = 'none';
if (cancelBtn) cancelBtn.style.display = 'inline-block';
// Start polling for this process
startModalDownloadPolling(convertedPlaylistId);
// Hide modal since this is background rehydration
process.modalElement.style.display = 'none';
console.log(`✅ Rehydrated download modal for YouTube playlist: ${playlistInfo.playlist.name}`);
}
} else {
console.warn(`⚠️ No Spotify tracks found for YouTube download modal: ${playlistInfo.playlist.name}`);
}
} catch (error) {
console.error(`❌ Error rehydrating download modal for ${playlistInfo.playlist.name}:`, error);
}
}
}
}
console.log(`✅ Successfully hydrated ${playlists.length} YouTube playlists from backend`);
} catch (error) {
@ -2878,6 +2934,13 @@ function closeDownloadMissingModal(playlistId) {
process.modalElement.style.display = 'none';
} else {
console.log(`Closing and cleaning up download modal for playlist ${playlistId}.`);
// Reset YouTube playlist phase to 'discovered' when modal is closed after completion
if (playlistId.startsWith('youtube_')) {
const urlHash = playlistId.replace('youtube_', '');
updateYouTubeCardPhase(urlHash, 'discovered');
}
cleanupDownloadProcess(playlistId);
}
}
@ -3098,6 +3161,12 @@ async function startMissingTracksProcess(playlistId) {
process.status = 'running';
updatePlaylistCardUI(playlistId);
updateRefreshButtonState();
// Update YouTube playlist phase to 'downloading' if this is a YouTube playlist
if (playlistId.startsWith('youtube_')) {
const urlHash = playlistId.replace('youtube_', '');
updateYouTubeCardPhase(urlHash, 'downloading');
}
document.getElementById(`begin-analysis-btn-${playlistId}`).style.display = 'none';
document.getElementById(`cancel-all-btn-${playlistId}`).style.display = 'inline-block';
@ -3235,15 +3304,35 @@ function startModalDownloadPolling(playlistId) {
if (data.phase === 'cancelled') {
process.status = 'cancelled';
// Reset YouTube playlist phase to 'discovered' if this is a YouTube playlist on cancel
if (playlistId.startsWith('youtube_')) {
const urlHash = playlistId.replace('youtube_', '');
updateYouTubeCardPhase(urlHash, 'discovered');
}
showToast(`Process cancelled for ${process.playlist.name}.`, 'info');
} else if (data.phase === 'error') {
process.status = 'complete'; // Treat as complete to allow cleanup
updatePlaylistCardUI(playlistId); // Update card to show ready for review
// Reset YouTube playlist phase to 'discovered' if this is a YouTube playlist on error
if (playlistId.startsWith('youtube_')) {
const urlHash = playlistId.replace('youtube_', '');
updateYouTubeCardPhase(urlHash, 'discovered');
}
showToast(`Process for ${process.playlist.name} failed!`, 'error');
} else {
process.status = 'complete';
updatePlaylistCardUI(playlistId); // Update card to show ready for review
// Update YouTube playlist phase to 'download_complete' if this is a YouTube playlist
if (playlistId.startsWith('youtube_')) {
const urlHash = playlistId.replace('youtube_', '');
updateYouTubeCardPhase(urlHash, 'download_complete');
}
// Handle background wishlist processing completion specially
if (isBackgroundWishlist) {
console.log(`🎉 Background wishlist processing complete: ${completedCount} downloaded, ${failedOrCancelledCount} failed`);
@ -6574,7 +6663,39 @@ function handleYouTubeCardClick(urlHash) {
// Need to get playlist ID from converted Spotify data
const spotifyPlaylistId = state.convertedSpotifyPlaylistId;
if (spotifyPlaylistId) {
openDownloadMissingModal(spotifyPlaylistId);
// Check if we have discovery results, if not load them first
if (!state.discoveryResults || state.discoveryResults.length === 0) {
console.log('🔍 Loading discovery results for download modal...');
fetch(`/api/youtube/state/${urlHash}`)
.then(response => response.json())
.then(fullState => {
if (fullState.discovery_results) {
state.discoveryResults = fullState.discovery_results;
console.log(`✅ Loaded ${state.discoveryResults.length} discovery results`);
// Now open the modal with the loaded data
const playlistName = `[YouTube] ${state.playlist.name}`;
const spotifyTracks = state.discoveryResults
.filter(result => result.spotify_data)
.map(result => result.spotify_data);
openDownloadMissingModalForYouTube(spotifyPlaylistId, playlistName, spotifyTracks);
} else {
console.error('❌ No discovery results found for downloads');
showToast('Unable to open download modal - no discovery data', 'error');
}
})
.catch(error => {
console.error('❌ Error loading discovery results:', error);
showToast('Error loading playlist data', 'error');
});
} else {
// Use the YouTube-specific function to maintain proper state linking
const playlistName = `[YouTube] ${state.playlist.name}`;
const spotifyTracks = state.discoveryResults
.filter(result => result.spotify_data)
.map(result => result.spotify_data);
openDownloadMissingModalForYouTube(spotifyPlaylistId, playlistName, spotifyTracks);
}
} else {
console.error('❌ No converted Spotify playlist ID found for downloads');
showToast('Unable to open download modal - missing playlist data', 'error');
@ -7256,8 +7377,7 @@ async function startYouTubeDownloadMissing(urlHash) {
// Open download missing tracks modal for YouTube playlist
await openDownloadMissingModalForYouTube(virtualPlaylistId, playlistName, spotifyTracks);
// Update YouTube card phase when download process starts
updateYouTubeCardPhase(urlHash, 'downloading');
// Phase will change to 'downloading' when user clicks "Begin Analysis" button
} catch (error) {
console.error('❌ Error starting download missing tracks:', error);

Loading…
Cancel
Save