download missing functionality
This commit is contained in:
parent
bd2866b348
commit
b2c1d21bf0
2 changed files with 170 additions and 7 deletions
|
|
@ -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…
Reference in a new issue