consistency
This commit is contained in:
parent
417c76e68c
commit
2070e24394
3 changed files with 281 additions and 63 deletions
130
web_server.py
130
web_server.py
|
|
@ -5676,22 +5676,35 @@ def start_tidal_discovery(playlist_id):
|
||||||
if not target_playlist.tracks:
|
if not target_playlist.tracks:
|
||||||
return jsonify({"error": "Playlist has no tracks"}), 400
|
return jsonify({"error": "Playlist has no tracks"}), 400
|
||||||
|
|
||||||
# Initialize or update discovery state
|
# Initialize discovery state if it doesn't exist, or update existing state
|
||||||
if playlist_id in tidal_discovery_states and tidal_discovery_states[playlist_id]['phase'] == 'discovering':
|
if playlist_id in tidal_discovery_states:
|
||||||
return jsonify({"error": "Discovery already in progress"}), 400
|
existing_state = tidal_discovery_states[playlist_id]
|
||||||
|
if existing_state['phase'] == 'discovering':
|
||||||
state = {
|
return jsonify({"error": "Discovery already in progress"}), 400
|
||||||
'playlist': target_playlist,
|
# Update existing state for discovery
|
||||||
'phase': 'discovering',
|
existing_state['phase'] = 'discovering'
|
||||||
'status': 'discovering',
|
existing_state['status'] = 'discovering'
|
||||||
'discovery_progress': 0,
|
existing_state['last_accessed'] = time.time()
|
||||||
'spotify_matches': 0,
|
state = existing_state
|
||||||
'spotify_total': len(target_playlist.tracks),
|
else:
|
||||||
'discovery_results': [],
|
# Create new state for first-time discovery
|
||||||
'last_accessed': time.time()
|
state = {
|
||||||
}
|
'playlist': target_playlist,
|
||||||
|
'phase': 'discovering', # fresh -> discovering -> discovered -> syncing -> sync_complete -> downloading -> download_complete
|
||||||
tidal_discovery_states[playlist_id] = state
|
'status': 'discovering',
|
||||||
|
'discovery_progress': 0,
|
||||||
|
'spotify_matches': 0,
|
||||||
|
'spotify_total': len(target_playlist.tracks),
|
||||||
|
'discovery_results': [],
|
||||||
|
'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,
|
||||||
|
'sync_progress': {}
|
||||||
|
}
|
||||||
|
tidal_discovery_states[playlist_id] = state
|
||||||
|
|
||||||
# Start discovery worker
|
# Start discovery worker
|
||||||
future = tidal_discovery_executor.submit(_run_tidal_discovery_worker, playlist_id)
|
future = tidal_discovery_executor.submit(_run_tidal_discovery_worker, playlist_id)
|
||||||
|
|
@ -5761,6 +5774,91 @@ def get_tidal_playlist_states():
|
||||||
print(f"❌ Error getting Tidal playlist states: {e}")
|
print(f"❌ Error getting Tidal playlist states: {e}")
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/tidal/state/<playlist_id>', methods=['GET'])
|
||||||
|
def get_tidal_playlist_state(playlist_id):
|
||||||
|
"""Get specific Tidal playlist state (detailed version matching YouTube's state endpoint)"""
|
||||||
|
try:
|
||||||
|
if playlist_id not in tidal_discovery_states:
|
||||||
|
return jsonify({"error": "Tidal playlist not found"}), 404
|
||||||
|
|
||||||
|
state = tidal_discovery_states[playlist_id]
|
||||||
|
state['last_accessed'] = time.time()
|
||||||
|
|
||||||
|
# Return full state information (including results for modal hydration)
|
||||||
|
response = {
|
||||||
|
'playlist_id': playlist_id,
|
||||||
|
'playlist': state['playlist'].__dict__ if hasattr(state['playlist'], '__dict__') else state['playlist'],
|
||||||
|
'phase': state['phase'],
|
||||||
|
'status': state['status'],
|
||||||
|
'discovery_progress': state['discovery_progress'],
|
||||||
|
'spotify_matches': state['spotify_matches'],
|
||||||
|
'spotify_total': state['spotify_total'],
|
||||||
|
'discovery_results': state['discovery_results'],
|
||||||
|
'last_accessed': state['last_accessed']
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error getting Tidal playlist state: {e}")
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/tidal/reset/<playlist_id>', methods=['POST'])
|
||||||
|
def reset_tidal_playlist(playlist_id):
|
||||||
|
"""Reset Tidal playlist to fresh phase (clear discovery/sync data)"""
|
||||||
|
try:
|
||||||
|
if playlist_id not in tidal_discovery_states:
|
||||||
|
return jsonify({"error": "Tidal playlist not found"}), 404
|
||||||
|
|
||||||
|
state = tidal_discovery_states[playlist_id]
|
||||||
|
|
||||||
|
# Stop any active discovery
|
||||||
|
if 'discovery_future' in state and state['discovery_future']:
|
||||||
|
state['discovery_future'].cancel()
|
||||||
|
|
||||||
|
# Reset state to fresh (preserve original playlist data)
|
||||||
|
state['phase'] = 'fresh'
|
||||||
|
state['status'] = 'fresh'
|
||||||
|
state['discovery_results'] = []
|
||||||
|
state['discovery_progress'] = 0
|
||||||
|
state['spotify_matches'] = 0
|
||||||
|
state['sync_playlist_id'] = None
|
||||||
|
state['converted_spotify_playlist_id'] = None
|
||||||
|
state['download_process_id'] = None
|
||||||
|
state['sync_progress'] = {}
|
||||||
|
state['discovery_future'] = None
|
||||||
|
state['last_accessed'] = time.time()
|
||||||
|
|
||||||
|
print(f"🔄 Reset Tidal playlist to fresh: {playlist_id}")
|
||||||
|
return jsonify({"success": True, "message": "Playlist reset to fresh phase"})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error resetting Tidal playlist: {e}")
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/tidal/delete/<playlist_id>', methods=['POST'])
|
||||||
|
def delete_tidal_playlist(playlist_id):
|
||||||
|
"""Delete Tidal playlist state completely"""
|
||||||
|
try:
|
||||||
|
if playlist_id not in tidal_discovery_states:
|
||||||
|
return jsonify({"error": "Tidal playlist not found"}), 404
|
||||||
|
|
||||||
|
state = tidal_discovery_states[playlist_id]
|
||||||
|
|
||||||
|
# Stop any active discovery
|
||||||
|
if 'discovery_future' in state and state['discovery_future']:
|
||||||
|
state['discovery_future'].cancel()
|
||||||
|
|
||||||
|
# Remove from state dictionary
|
||||||
|
del tidal_discovery_states[playlist_id]
|
||||||
|
|
||||||
|
print(f"🗑️ Deleted Tidal playlist state: {playlist_id}")
|
||||||
|
return jsonify({"success": True, "message": "Playlist deleted"})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error deleting Tidal playlist: {e}")
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
def _run_tidal_discovery_worker(playlist_id):
|
def _run_tidal_discovery_worker(playlist_id):
|
||||||
"""Background worker for Tidal Spotify discovery process (like sync.py)"""
|
"""Background worker for Tidal Spotify discovery process (like sync.py)"""
|
||||||
|
|
|
||||||
|
|
@ -6420,6 +6420,9 @@ function createTidalCard(playlist) {
|
||||||
<span class="playlist-card-phase-text" style="color: ${phaseColor};">${phaseText}</span>
|
<span class="playlist-card-phase-text" style="color: ${phaseColor};">${phaseText}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="playlist-card-progress ${phase === 'fresh' ? 'hidden' : ''}">
|
||||||
|
♪ ${playlist.track_count} / ✓ 0 / ✗ ${playlist.track_count} / 0%
|
||||||
|
</div>
|
||||||
<button class="playlist-card-action-btn">${buttonText}</button>
|
<button class="playlist-card-action-btn">${buttonText}</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -6436,13 +6439,7 @@ async function handleTidalCardClick(playlistId) {
|
||||||
console.log(`🎵 Using pre-loaded Tidal playlist data for: ${state.playlist.name}`);
|
console.log(`🎵 Using pre-loaded Tidal playlist data for: ${state.playlist.name}`);
|
||||||
console.log(`🎵 Ready with ${state.playlist.tracks.length} Tidal tracks for discovery`);
|
console.log(`🎵 Ready with ${state.playlist.tracks.length} Tidal tracks for discovery`);
|
||||||
|
|
||||||
// Update phase to discovering
|
// Open discovery modal - phase will be updated when discovery actually starts
|
||||||
state.phase = 'discovering';
|
|
||||||
|
|
||||||
// Update card to show discovering state
|
|
||||||
updateTidalCardPhase(playlistId, 'discovering');
|
|
||||||
|
|
||||||
// Open YouTube discovery modal but with Tidal data (exact sync.py pattern)
|
|
||||||
openTidalDiscoveryModal(playlistId, state.playlist);
|
openTidalDiscoveryModal(playlistId, state.playlist);
|
||||||
|
|
||||||
} else if (state.phase === 'discovering' || state.phase === 'discovered') {
|
} else if (state.phase === 'discovering' || state.phase === 'discovered') {
|
||||||
|
|
@ -6479,43 +6476,54 @@ async function openTidalDiscoveryModal(playlistId, playlistData) {
|
||||||
// Create a fake YouTube-style urlHash for the modal system
|
// Create a fake YouTube-style urlHash for the modal system
|
||||||
const fakeUrlHash = `tidal_${playlistId}`;
|
const fakeUrlHash = `tidal_${playlistId}`;
|
||||||
|
|
||||||
// Get current Tidal card state to check if discovery is already done
|
// Get current Tidal card state to check if discovery is already done or in progress
|
||||||
const tidalCardState = tidalPlaylistStates[playlistId];
|
const tidalCardState = tidalPlaylistStates[playlistId];
|
||||||
const isAlreadyDiscovered = tidalCardState && tidalCardState.phase === 'discovered';
|
const isAlreadyDiscovered = tidalCardState && tidalCardState.phase === 'discovered';
|
||||||
|
const isCurrentlyDiscovering = tidalCardState && tidalCardState.phase === 'discovering';
|
||||||
|
|
||||||
// Prepare discovery results in the correct format for modal
|
// Prepare discovery results in the correct format for modal
|
||||||
let transformedResults = [];
|
let transformedResults = [];
|
||||||
|
let actualMatches = 0;
|
||||||
if (isAlreadyDiscovered && tidalCardState.discovery_results) {
|
if (isAlreadyDiscovered && tidalCardState.discovery_results) {
|
||||||
transformedResults = tidalCardState.discovery_results.map((result, index) => ({
|
transformedResults = tidalCardState.discovery_results.map((result, index) => {
|
||||||
index: index,
|
const isFound = result.status === 'found';
|
||||||
yt_track: result.tidal_track ? result.tidal_track.name : 'Unknown',
|
if (isFound) actualMatches++;
|
||||||
yt_artist: result.tidal_track ? (result.tidal_track.artists ? result.tidal_track.artists.join(', ') : 'Unknown') : 'Unknown',
|
|
||||||
status: result.status === 'found' ? '✅ Found' : '❌ Not Found',
|
return {
|
||||||
status_class: result.status === 'found' ? 'found' : 'not-found',
|
index: index,
|
||||||
spotify_track: result.spotify_data ? result.spotify_data.name : '-',
|
yt_track: result.tidal_track ? result.tidal_track.name : 'Unknown',
|
||||||
spotify_artist: result.spotify_data ? result.spotify_data.artists.join(', ') : '-',
|
yt_artist: result.tidal_track ? (result.tidal_track.artists ? result.tidal_track.artists.join(', ') : 'Unknown') : 'Unknown',
|
||||||
spotify_album: result.spotify_data ? result.spotify_data.album : '-'
|
status: isFound ? '✅ Found' : '❌ Not Found',
|
||||||
}));
|
status_class: isFound ? 'found' : 'not-found',
|
||||||
|
spotify_track: result.spotify_data ? result.spotify_data.name : '-',
|
||||||
|
spotify_artist: result.spotify_data ? result.spotify_data.artists.join(', ') : '-',
|
||||||
|
spotify_album: result.spotify_data ? result.spotify_data.album : '-'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
console.log(`🎵 Tidal modal: Calculated ${actualMatches} matches from ${transformedResults.length} results`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create YouTube-compatible state structure
|
// Create YouTube-compatible state structure
|
||||||
|
const modalPhase = isAlreadyDiscovered ? 'discovered' : (isCurrentlyDiscovering ? 'discovering' : 'fresh');
|
||||||
youtubePlaylistStates[fakeUrlHash] = {
|
youtubePlaylistStates[fakeUrlHash] = {
|
||||||
phase: isAlreadyDiscovered ? 'discovered' : 'discovering',
|
phase: modalPhase,
|
||||||
playlist: {
|
playlist: {
|
||||||
name: playlistData.name,
|
name: playlistData.name,
|
||||||
tracks: playlistData.tracks
|
tracks: playlistData.tracks
|
||||||
},
|
},
|
||||||
is_tidal_playlist: true, // Flag to identify this as Tidal
|
is_tidal_playlist: true, // Flag to identify this as Tidal
|
||||||
tidal_playlist_id: playlistId,
|
tidal_playlist_id: playlistId,
|
||||||
discovery_progress: isAlreadyDiscovered ? (tidalCardState.discovery_progress || 100) : 0,
|
discovery_progress: isAlreadyDiscovered ? 100 : 0,
|
||||||
spotify_matches: isAlreadyDiscovered ? (tidalCardState.spotify_matches || 0) : 0,
|
spotify_matches: isAlreadyDiscovered ? actualMatches : 0, // Backend format (snake_case)
|
||||||
|
spotifyMatches: isAlreadyDiscovered ? actualMatches : 0, // Frontend format (camelCase) - for button logic
|
||||||
spotify_total: playlistData.tracks.length,
|
spotify_total: playlistData.tracks.length,
|
||||||
discovery_results: transformedResults,
|
discovery_results: transformedResults,
|
||||||
discoveryResults: transformedResults // Both formats for compatibility
|
discoveryResults: transformedResults, // Both formats for compatibility
|
||||||
|
discoveryProgress: isAlreadyDiscovered ? 100 : 0 // Frontend format for modal progress display
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only start discovery if not already discovered
|
// Only start discovery if not already discovered AND not currently discovering
|
||||||
if (!isAlreadyDiscovered) {
|
if (!isAlreadyDiscovered && !isCurrentlyDiscovering) {
|
||||||
// Start Tidal discovery process automatically (like sync.py)
|
// Start Tidal discovery process automatically (like sync.py)
|
||||||
try {
|
try {
|
||||||
console.log(`🔍 Starting Tidal discovery for: ${playlistData.name}`);
|
console.log(`🔍 Starting Tidal discovery for: ${playlistData.name}`);
|
||||||
|
|
@ -6534,6 +6542,13 @@ async function openTidalDiscoveryModal(playlistId, playlistData) {
|
||||||
|
|
||||||
console.log('✅ Tidal discovery started, beginning polling...');
|
console.log('✅ Tidal discovery started, beginning polling...');
|
||||||
|
|
||||||
|
// Update phase to discovering now that backend discovery is actually started
|
||||||
|
tidalPlaylistStates[playlistId].phase = 'discovering';
|
||||||
|
updateTidalCardPhase(playlistId, 'discovering');
|
||||||
|
|
||||||
|
// Update modal phase to match
|
||||||
|
youtubePlaylistStates[fakeUrlHash].phase = 'discovering';
|
||||||
|
|
||||||
// Start polling for progress
|
// Start polling for progress
|
||||||
startTidalDiscoveryPolling(fakeUrlHash, playlistId);
|
startTidalDiscoveryPolling(fakeUrlHash, playlistId);
|
||||||
|
|
||||||
|
|
@ -6541,6 +6556,10 @@ async function openTidalDiscoveryModal(playlistId, playlistData) {
|
||||||
console.error('❌ Error starting Tidal discovery:', error);
|
console.error('❌ Error starting Tidal discovery:', error);
|
||||||
showToast(`Error starting discovery: ${error.message}`, 'error');
|
showToast(`Error starting discovery: ${error.message}`, 'error');
|
||||||
}
|
}
|
||||||
|
} else if (isCurrentlyDiscovering) {
|
||||||
|
// Resume polling if discovery is already in progress (like YouTube)
|
||||||
|
console.log(`🔄 Resuming Tidal discovery polling for: ${playlistData.name}`);
|
||||||
|
startTidalDiscoveryPolling(fakeUrlHash, playlistId);
|
||||||
} else {
|
} else {
|
||||||
console.log('✅ Using existing discovery results - no need to re-discover');
|
console.log('✅ Using existing discovery results - no need to re-discover');
|
||||||
}
|
}
|
||||||
|
|
@ -6569,34 +6588,38 @@ function startTidalDiscoveryPolling(fakeUrlHash, playlistId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform Tidal results to YouTube modal format first
|
||||||
|
const transformedStatus = {
|
||||||
|
progress: status.progress,
|
||||||
|
spotify_matches: status.spotify_matches,
|
||||||
|
spotify_total: status.spotify_total,
|
||||||
|
results: status.results.map((result, index) => ({
|
||||||
|
index: index,
|
||||||
|
yt_track: result.tidal_track ? result.tidal_track.name : 'Unknown',
|
||||||
|
yt_artist: result.tidal_track ? (result.tidal_track.artists ? result.tidal_track.artists.join(', ') : 'Unknown') : 'Unknown',
|
||||||
|
status: result.status === 'found' ? '✅ Found' : '❌ Not Found',
|
||||||
|
status_class: result.status === 'found' ? 'found' : 'not-found',
|
||||||
|
spotify_track: result.spotify_data ? result.spotify_data.name : '-',
|
||||||
|
spotify_artist: result.spotify_data ? result.spotify_data.artists.join(', ') : '-',
|
||||||
|
spotify_album: result.spotify_data ? result.spotify_data.album : '-'
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
// Update fake YouTube state with Tidal discovery results
|
// Update fake YouTube state with Tidal discovery results
|
||||||
const state = youtubePlaylistStates[fakeUrlHash];
|
const state = youtubePlaylistStates[fakeUrlHash];
|
||||||
if (state) {
|
if (state) {
|
||||||
state.discovery_progress = status.progress;
|
state.discovery_progress = status.progress; // Backend format
|
||||||
state.spotify_matches = status.spotify_matches;
|
state.discoveryProgress = status.progress; // Frontend format - for modal progress display
|
||||||
state.discovery_results = status.results;
|
state.spotify_matches = status.spotify_matches; // Backend format
|
||||||
|
state.spotifyMatches = status.spotify_matches; // Frontend format - for button logic
|
||||||
|
state.discovery_results = status.results; // Backend format
|
||||||
|
state.discoveryResults = transformedStatus.results; // Frontend format - for button logic
|
||||||
state.phase = status.phase;
|
state.phase = status.phase;
|
||||||
|
|
||||||
// Transform Tidal results to YouTube modal format
|
|
||||||
const transformedStatus = {
|
|
||||||
progress: status.progress,
|
|
||||||
spotify_matches: status.spotify_matches,
|
|
||||||
spotify_total: status.spotify_total,
|
|
||||||
results: status.results.map((result, index) => ({
|
|
||||||
index: index,
|
|
||||||
status: result.status === 'found' ? '✅ Found' : '❌ Not Found',
|
|
||||||
status_class: result.status === 'found' ? 'found' : 'not-found',
|
|
||||||
spotify_track: result.spotify_data ? result.spotify_data.name : '-',
|
|
||||||
spotify_artist: result.spotify_data ? result.spotify_data.artists.join(', ') : '-',
|
|
||||||
spotify_album: result.spotify_data ? result.spotify_data.album : '-'
|
|
||||||
// Note: No duration column for Tidal (matches GUI version)
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update modal with transformed data (reuse YouTube modal update logic)
|
// Update modal with transformed data (reuse YouTube modal update logic)
|
||||||
updateYouTubeDiscoveryModal(fakeUrlHash, transformedStatus);
|
updateYouTubeDiscoveryModal(fakeUrlHash, transformedStatus);
|
||||||
|
|
||||||
// Update Tidal card phase and save discovery results
|
// Update Tidal card phase and save discovery results FIRST
|
||||||
if (tidalPlaylistStates[playlistId]) {
|
if (tidalPlaylistStates[playlistId]) {
|
||||||
tidalPlaylistStates[playlistId].phase = status.phase;
|
tidalPlaylistStates[playlistId].phase = status.phase;
|
||||||
tidalPlaylistStates[playlistId].discovery_results = status.results;
|
tidalPlaylistStates[playlistId].discovery_results = status.results;
|
||||||
|
|
@ -6605,6 +6628,9 @@ function startTidalDiscoveryPolling(fakeUrlHash, playlistId) {
|
||||||
updateTidalCardPhase(playlistId, status.phase);
|
updateTidalCardPhase(playlistId, status.phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update Tidal card progress AFTER phase update to avoid being overwritten
|
||||||
|
updateTidalCardProgress(playlistId, status);
|
||||||
|
|
||||||
console.log(`🔄 Tidal discovery progress: ${status.progress}% (${status.spotify_matches}/${status.spotify_total} found)`);
|
console.log(`🔄 Tidal discovery progress: ${status.progress}% (${status.spotify_matches}/${status.spotify_total} found)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6688,9 +6714,42 @@ async function applyTidalPlaylistState(stateInfo) {
|
||||||
tidalPlaylistStates[playlist_id].discovery_results = discovery_results;
|
tidalPlaylistStates[playlist_id].discovery_results = discovery_results;
|
||||||
tidalPlaylistStates[playlist_id].playlist = playlistData; // Ensure playlist data is set
|
tidalPlaylistStates[playlist_id].playlist = playlistData; // Ensure playlist data is set
|
||||||
|
|
||||||
|
// Fetch full discovery results for non-fresh playlists (matching YouTube pattern)
|
||||||
|
if (phase !== 'fresh' && phase !== 'discovering') {
|
||||||
|
try {
|
||||||
|
console.log(`🔍 Fetching full discovery results for Tidal playlist: ${playlistData.name}`);
|
||||||
|
const stateResponse = await fetch(`/api/tidal/state/${playlist_id}`);
|
||||||
|
if (stateResponse.ok) {
|
||||||
|
const fullState = await stateResponse.json();
|
||||||
|
console.log(`📋 Retrieved full Tidal state with ${fullState.discovery_results?.length || 0} discovery results`);
|
||||||
|
|
||||||
|
// Store full discovery results in local state (matching YouTube pattern)
|
||||||
|
if (fullState.discovery_results && tidalPlaylistStates[playlist_id]) {
|
||||||
|
tidalPlaylistStates[playlist_id].discovery_results = fullState.discovery_results;
|
||||||
|
tidalPlaylistStates[playlist_id].discovery_progress = fullState.discovery_progress;
|
||||||
|
tidalPlaylistStates[playlist_id].spotify_matches = fullState.spotify_matches;
|
||||||
|
console.log(`✅ Restored ${fullState.discovery_results.length} discovery results for Tidal playlist: ${playlistData.name}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`⚠️ Could not fetch full discovery results for Tidal playlist: ${playlistData.name}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`⚠️ Error fetching full discovery results for Tidal playlist ${playlistData.name}:`, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the card UI to reflect the saved state
|
// Update the card UI to reflect the saved state
|
||||||
updateTidalCardPhase(playlist_id, phase);
|
updateTidalCardPhase(playlist_id, phase);
|
||||||
|
|
||||||
|
// Update card progress if we have discovery results
|
||||||
|
if (phase === 'discovered' && tidalPlaylistStates[playlist_id]) {
|
||||||
|
const progressInfo = {
|
||||||
|
spotify_total: playlistData.track_count || playlistData.tracks?.length || 0,
|
||||||
|
spotify_matches: tidalPlaylistStates[playlist_id].spotify_matches || 0
|
||||||
|
};
|
||||||
|
updateTidalCardProgress(playlist_id, progressInfo);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`✅ Applied saved state for Tidal playlist: ${playlist_id} -> ${phase}`);
|
console.log(`✅ Applied saved state for Tidal playlist: ${playlist_id} -> ${phase}`);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -6698,6 +6757,27 @@ async function applyTidalPlaylistState(stateInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTidalCardProgress(playlistId, progress) {
|
||||||
|
const state = tidalPlaylistStates[playlistId];
|
||||||
|
if (!state) return;
|
||||||
|
|
||||||
|
const card = document.getElementById(`tidal-card-${playlistId}`);
|
||||||
|
if (!card) return;
|
||||||
|
|
||||||
|
const progressElement = card.querySelector('.playlist-card-progress');
|
||||||
|
if (!progressElement) return;
|
||||||
|
|
||||||
|
const total = progress.spotify_total || 0;
|
||||||
|
const matches = progress.spotify_matches || 0;
|
||||||
|
const failed = total - matches;
|
||||||
|
const percentage = total > 0 ? Math.round((matches / total) * 100) : 0;
|
||||||
|
|
||||||
|
progressElement.textContent = `♪ ${total} / ✓ ${matches} / ✗ ${failed} / ${percentage}%`;
|
||||||
|
progressElement.classList.remove('hidden'); // Show progress during discovery
|
||||||
|
|
||||||
|
console.log('🎵 Updated Tidal card progress:', playlistId, `${matches}/${total} (${percentage}%)`);
|
||||||
|
}
|
||||||
|
|
||||||
// Tidal-specific sync and download functions (placeholder implementations)
|
// Tidal-specific sync and download functions (placeholder implementations)
|
||||||
function startTidalPlaylistSync(urlHash) {
|
function startTidalPlaylistSync(urlHash) {
|
||||||
console.log(`🎵 Starting Tidal playlist sync for: ${urlHash}`);
|
console.log(`🎵 Starting Tidal playlist sync for: ${urlHash}`);
|
||||||
|
|
@ -7659,12 +7739,39 @@ function updateYouTubeDiscoveryModal(urlHash, status) {
|
||||||
const progressText = document.getElementById(`youtube-discovery-progress-text-${urlHash}`);
|
const progressText = document.getElementById(`youtube-discovery-progress-text-${urlHash}`);
|
||||||
const tableBody = document.getElementById(`youtube-discovery-table-${urlHash}`);
|
const tableBody = document.getElementById(`youtube-discovery-table-${urlHash}`);
|
||||||
|
|
||||||
if (!progressBar || !progressText || !tableBody) return;
|
if (!progressBar || !progressText || !tableBody) {
|
||||||
|
console.warn(`⚠️ Missing modal elements for ${urlHash}:`, {
|
||||||
|
progressBar: !!progressBar,
|
||||||
|
progressText: !!progressText,
|
||||||
|
tableBody: !!tableBody
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Update progress bar
|
// Update progress bar
|
||||||
progressBar.style.width = `${status.progress}%`;
|
progressBar.style.width = `${status.progress}%`;
|
||||||
progressText.textContent = `${status.spotify_matches} / ${status.spotify_total} tracks matched (${status.progress}%)`;
|
progressText.textContent = `${status.spotify_matches} / ${status.spotify_total} tracks matched (${status.progress}%)`;
|
||||||
|
|
||||||
|
// Ensure progress bar container has proper height (CSS might not be applying correctly)
|
||||||
|
const progressContainer = progressBar.parentElement;
|
||||||
|
if (progressContainer && progressContainer.classList.contains('progress-bar-container')) {
|
||||||
|
progressContainer.style.height = '8px';
|
||||||
|
progressContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
|
||||||
|
progressContainer.style.borderRadius = '8px';
|
||||||
|
progressContainer.style.overflow = 'hidden';
|
||||||
|
progressContainer.style.marginBottom = '8px';
|
||||||
|
|
||||||
|
// Ensure progress bar fill has proper styling
|
||||||
|
progressBar.style.height = '100%';
|
||||||
|
progressBar.style.background = 'linear-gradient(90deg, #1db954 0%, #1ed760 100%)';
|
||||||
|
progressBar.style.transition = 'width 0.5s ease';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📊 Updated modal progress for ${urlHash}: ${status.progress}% (${status.spotify_matches}/${status.spotify_total})`);
|
||||||
|
console.log(`📊 Progress bar width set to: ${progressBar.style.width}`);
|
||||||
|
console.log(`📊 Progress bar element:`, progressBar);
|
||||||
|
console.log(`📊 Progress bar computed styles:`, window.getComputedStyle(progressBar));
|
||||||
|
|
||||||
// Update table rows
|
// Update table rows
|
||||||
status.results.forEach(result => {
|
status.results.forEach(result => {
|
||||||
const row = document.getElementById(`discovery-row-${urlHash}-${result.index}`);
|
const row = document.getElementById(`discovery-row-${urlHash}-${result.index}`);
|
||||||
|
|
@ -7682,6 +7789,18 @@ function updateYouTubeDiscoveryModal(urlHash, status) {
|
||||||
spotifyArtistCell.textContent = result.spotify_artist || '-';
|
spotifyArtistCell.textContent = result.spotify_artist || '-';
|
||||||
spotifyAlbumCell.textContent = result.spotify_album || '-';
|
spotifyAlbumCell.textContent = result.spotify_album || '-';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update action buttons if discovery is complete (progress = 100%)
|
||||||
|
if (status.progress >= 100) {
|
||||||
|
const state = youtubePlaylistStates[urlHash];
|
||||||
|
if (state && state.phase === 'discovered') {
|
||||||
|
const actionButtonsContainer = document.querySelector(`#youtube-discovery-modal-${urlHash} .modal-footer-left`);
|
||||||
|
if (actionButtonsContainer) {
|
||||||
|
actionButtonsContainer.innerHTML = getModalActionButtons(urlHash, 'discovered', state);
|
||||||
|
console.log(`✨ Updated action buttons for completed discovery: ${urlHash}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshYouTubeDiscoveryModalTable(urlHash) {
|
function refreshYouTubeDiscoveryModalTable(urlHash) {
|
||||||
|
|
|
||||||
|
|
@ -4657,10 +4657,11 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.youtube-discovery-modal .progress-bar-container {
|
.youtube-discovery-modal .progress-bar-container {
|
||||||
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 8px;
|
height: 20px!important;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue