beatport progress
This commit is contained in:
parent
ef69eb698b
commit
a993a972fd
2 changed files with 874 additions and 20 deletions
141
web_server.py
141
web_server.py
|
|
@ -12532,6 +12532,147 @@ def cancel_beatport_sync(url_hash):
|
|||
print(f"❌ Error cancelling Beatport sync: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
# ===================================================================
|
||||
# BEATPORT CHART PERSISTENCE API ENDPOINTS
|
||||
# ===================================================================
|
||||
|
||||
@app.route('/api/beatport/charts', methods=['GET'])
|
||||
def get_beatport_charts():
|
||||
"""Get all persistent Beatport chart states for frontend hydration"""
|
||||
try:
|
||||
charts = []
|
||||
current_time = time.time()
|
||||
|
||||
# Clean up old charts (older than 24 hours)
|
||||
to_remove = []
|
||||
for chart_hash, state in beatport_chart_states.items():
|
||||
last_accessed = state.get('last_accessed', 0)
|
||||
if current_time - last_accessed > 86400: # 24 hours
|
||||
to_remove.append(chart_hash)
|
||||
else:
|
||||
# Include in response
|
||||
chart_info = {
|
||||
'hash': chart_hash,
|
||||
'name': state['chart']['name'],
|
||||
'track_count': len(state['chart']['tracks']),
|
||||
'phase': state.get('phase', 'fresh'),
|
||||
'discovery_progress': state.get('discovery_progress', 0),
|
||||
'spotify_matches': state.get('spotify_matches', 0),
|
||||
'spotify_total': state.get('spotify_total', 0),
|
||||
'converted_spotify_playlist_id': state.get('converted_spotify_playlist_id'),
|
||||
'download_process_id': state.get('download_process_id'),
|
||||
'last_accessed': last_accessed,
|
||||
'chart_data': state['chart'] # Full chart data for restoration
|
||||
}
|
||||
charts.append(chart_info)
|
||||
|
||||
# Remove old charts
|
||||
for chart_hash in to_remove:
|
||||
del beatport_chart_states[chart_hash]
|
||||
logger.info(f"🧹 Cleaned up old Beatport chart: {chart_hash}")
|
||||
|
||||
logger.info(f"📊 Returning {len(charts)} Beatport charts for hydration")
|
||||
return jsonify(charts)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting Beatport charts: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/beatport/charts/status/<chart_hash>', methods=['GET'])
|
||||
def get_beatport_chart_status(chart_hash):
|
||||
"""Get individual Beatport chart status with full state data"""
|
||||
try:
|
||||
if chart_hash not in beatport_chart_states:
|
||||
return jsonify({"error": "Beatport chart not found"}), 404
|
||||
|
||||
state = beatport_chart_states[chart_hash]
|
||||
state['last_accessed'] = time.time() # Update access time
|
||||
|
||||
# Return full state including discovery results for modal restoration
|
||||
response = {
|
||||
'hash': chart_hash,
|
||||
'phase': state.get('phase', 'fresh'),
|
||||
'status': state.get('status', 'fresh'),
|
||||
'discovery_progress': state.get('discovery_progress', 0),
|
||||
'spotify_matches': state.get('spotify_matches', 0),
|
||||
'spotify_total': state.get('spotify_total', 0),
|
||||
'discovery_results': state.get('discovery_results', []),
|
||||
'converted_spotify_playlist_id': state.get('converted_spotify_playlist_id'),
|
||||
'download_process_id': state.get('download_process_id'),
|
||||
'sync_playlist_id': state.get('sync_playlist_id'),
|
||||
'sync_progress': state.get('sync_progress', {}),
|
||||
'chart_data': state['chart'] # Full chart data
|
||||
}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting Beatport chart status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/beatport/charts/update-phase/<chart_hash>', methods=['POST'])
|
||||
def update_beatport_chart_phase(chart_hash):
|
||||
"""Update Beatport chart phase (for modal close operations and reset)"""
|
||||
try:
|
||||
if chart_hash not in beatport_chart_states:
|
||||
return jsonify({"error": "Beatport chart not found"}), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
new_phase = data.get('phase')
|
||||
is_reset = data.get('reset', False)
|
||||
|
||||
if not new_phase:
|
||||
return jsonify({"error": "Phase is required"}), 400
|
||||
|
||||
state = beatport_chart_states[chart_hash]
|
||||
state['phase'] = new_phase
|
||||
state['last_accessed'] = time.time()
|
||||
|
||||
# Handle reset operation - clear discovery data
|
||||
if is_reset and new_phase == 'fresh':
|
||||
state['discovery_results'] = []
|
||||
state['discovery_progress'] = 0
|
||||
state['spotify_matches'] = 0
|
||||
state['status'] = 'fresh'
|
||||
state['converted_spotify_playlist_id'] = None
|
||||
state['download_process_id'] = None
|
||||
state['sync_playlist_id'] = None
|
||||
state['sync_progress'] = {}
|
||||
logger.info(f"🎧 Reset Beatport chart {chart_hash} to fresh state")
|
||||
else:
|
||||
# Handle other phase updates (like download phase transitions)
|
||||
converted_playlist_id = data.get('converted_spotify_playlist_id')
|
||||
if converted_playlist_id:
|
||||
state['converted_spotify_playlist_id'] = converted_playlist_id
|
||||
|
||||
download_process_id = data.get('download_process_id')
|
||||
if download_process_id:
|
||||
state['download_process_id'] = download_process_id
|
||||
|
||||
logger.info(f"🎧 Updated Beatport chart {chart_hash} phase to: {new_phase}")
|
||||
return jsonify({"success": True, "phase": new_phase})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error updating Beatport chart phase: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/beatport/charts/delete/<chart_hash>', methods=['DELETE'])
|
||||
def delete_beatport_chart(chart_hash):
|
||||
"""Delete a Beatport chart from backend storage"""
|
||||
try:
|
||||
if chart_hash not in beatport_chart_states:
|
||||
return jsonify({"error": "Beatport chart not found"}), 404
|
||||
|
||||
chart_name = beatport_chart_states[chart_hash]['chart']['name']
|
||||
del beatport_chart_states[chart_hash]
|
||||
|
||||
logger.info(f"🗑️ Deleted Beatport chart: {chart_name}")
|
||||
return jsonify({"success": True, "message": f"Deleted chart: {chart_name}"})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error deleting Beatport chart: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
def convert_beatport_results_to_spotify_tracks(discovery_results):
|
||||
"""Convert Beatport discovery results to Spotify tracks format for sync"""
|
||||
spotify_tracks = []
|
||||
|
|
|
|||
|
|
@ -1982,9 +1982,12 @@ async function loadSyncData() {
|
|||
if (!spotifyPlaylistsLoaded) {
|
||||
await loadSpotifyPlaylists();
|
||||
}
|
||||
|
||||
|
||||
// Load YouTube playlists from backend (always refresh to get latest state)
|
||||
await loadYouTubePlaylistsFromBackend();
|
||||
|
||||
// Load Beatport charts from backend (always refresh to get latest state)
|
||||
await loadBeatportChartsFromBackend();
|
||||
}
|
||||
|
||||
async function checkForActiveProcesses() {
|
||||
|
|
@ -2106,6 +2109,12 @@ async function rehydrateModal(processInfo, userRequested = false) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Handle Beatport virtual playlists - skip rehydration here, handled by Beatport system
|
||||
if (playlist_id.startsWith('beatport_')) {
|
||||
console.log(`⏭️ Skipping Beatport virtual playlist rehydration - handled by Beatport system`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle artist album virtual playlists
|
||||
if (playlist_id.startsWith('artist_album_')) {
|
||||
console.log(`💧 Rehydrating artist album virtual playlist: ${playlist_id}`);
|
||||
|
|
@ -2352,13 +2361,359 @@ async function loadYouTubePlaylistsFromBackend() {
|
|||
}
|
||||
|
||||
console.log(`✅ Successfully hydrated ${playlists.length} YouTube playlists from backend`);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error loading YouTube playlists from backend:', error);
|
||||
showToast(`Error loading YouTube playlists: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadBeatportChartsFromBackend() {
|
||||
// Load all stored Beatport charts from backend and recreate cards (similar to YouTube hydration)
|
||||
try {
|
||||
console.log('📋 Loading Beatport charts from backend...');
|
||||
|
||||
const response = await fetch('/api/beatport/charts');
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to fetch Beatport charts');
|
||||
}
|
||||
|
||||
const charts = await response.json();
|
||||
|
||||
console.log(`🎧 Found ${charts.length} stored Beatport charts in backend`);
|
||||
|
||||
if (charts.length === 0) {
|
||||
console.log('📋 No Beatport charts to hydrate');
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document.getElementById('beatport-playlist-container');
|
||||
|
||||
// Create cards for charts that don't already exist (avoid duplicates)
|
||||
for (const chartInfo of charts) {
|
||||
const chartHash = chartInfo.hash;
|
||||
|
||||
// Check if card already exists (from previous loading)
|
||||
if (beatportChartStates[chartHash] && beatportChartStates[chartHash].cardElement &&
|
||||
document.body.contains(beatportChartStates[chartHash].cardElement)) {
|
||||
console.log(`⏭️ Skipping existing Beatport chart card: ${chartInfo.name}`);
|
||||
|
||||
// Update existing state with backend data
|
||||
const state = beatportChartStates[chartHash];
|
||||
state.phase = chartInfo.phase;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`🎧 Creating Beatport chart card: ${chartInfo.name} (Phase: ${chartInfo.phase})`);
|
||||
createBeatportCardFromBackendState(chartInfo);
|
||||
|
||||
// Fetch full state for non-fresh charts to restore discovery results
|
||||
if (chartInfo.phase !== 'fresh' && chartInfo.phase !== 'discovering') {
|
||||
try {
|
||||
console.log(`🔍 Fetching full state for: ${chartInfo.name}`);
|
||||
const stateResponse = await fetch(`/api/beatport/charts/status/${chartHash}`);
|
||||
if (stateResponse.ok) {
|
||||
const fullState = await stateResponse.json();
|
||||
console.log(`📋 Retrieved full state with ${fullState.discovery_results?.length || 0} discovery results`);
|
||||
|
||||
// Store in YouTube state system (since Beatport reuses it)
|
||||
if (fullState.discovery_results && fullState.discovery_results.length > 0) {
|
||||
// Transform backend results to frontend format (like Tidal does)
|
||||
const transformedResults = fullState.discovery_results.map((result, index) => ({
|
||||
index: result.index !== undefined ? result.index : index,
|
||||
yt_track: result.beatport_track ? result.beatport_track.title : 'Unknown',
|
||||
yt_artist: result.beatport_track ? result.beatport_track.artist : 'Unknown',
|
||||
status: result.status === 'found' ? '✅ Found' : (result.status === 'error' ? '❌ Error' : '❌ Not Found'),
|
||||
status_class: result.status_class || (result.status === 'found' ? 'found' : (result.status === 'error' ? 'error' : 'not-found')),
|
||||
spotify_track: result.spotify_data ? result.spotify_data.name : '-',
|
||||
spotify_artist: result.spotify_data && result.spotify_data.artists ?
|
||||
result.spotify_data.artists.map(a => a.name || a).join(', ') : '-',
|
||||
spotify_album: result.spotify_data ? result.spotify_data.album : '-'
|
||||
}));
|
||||
|
||||
// Create Beatport state in YouTube system for modal functionality
|
||||
youtubePlaylistStates[chartHash] = {
|
||||
phase: fullState.phase,
|
||||
playlist: {
|
||||
name: chartInfo.name,
|
||||
tracks: chartInfo.chart_data.tracks,
|
||||
description: `${chartInfo.track_count} tracks from ${chartInfo.name}`,
|
||||
source: 'beatport'
|
||||
},
|
||||
is_beatport_playlist: true,
|
||||
beatport_chart_type: chartInfo.chart_data.chart_type,
|
||||
beatport_chart_hash: chartHash,
|
||||
discovery_progress: fullState.discovery_progress,
|
||||
discoveryProgress: fullState.discovery_progress,
|
||||
spotify_matches: fullState.spotify_matches,
|
||||
spotifyMatches: fullState.spotify_matches,
|
||||
discovery_results: fullState.discovery_results,
|
||||
discoveryResults: transformedResults,
|
||||
convertedSpotifyPlaylistId: fullState.converted_spotify_playlist_id,
|
||||
download_process_id: fullState.download_process_id,
|
||||
syncPlaylistId: fullState.sync_playlist_id,
|
||||
syncProgress: fullState.sync_progress || {}
|
||||
};
|
||||
|
||||
console.log(`✅ Restored ${transformedResults.length} discovery results for: ${chartInfo.name}`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`⚠️ Could not fetch full state for: ${chartInfo.name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Error fetching full state for ${chartInfo.name}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rehydrate download modals for Beatport charts in downloading/download_complete phases
|
||||
for (const chartInfo of charts) {
|
||||
if ((chartInfo.phase === 'downloading' || chartInfo.phase === 'download_complete') &&
|
||||
chartInfo.converted_spotify_playlist_id && chartInfo.download_process_id) {
|
||||
|
||||
const convertedPlaylistId = chartInfo.converted_spotify_playlist_id;
|
||||
console.log(`📥 Rehydrating download modal for Beatport chart: ${chartInfo.name} (Playlist: ${convertedPlaylistId})`);
|
||||
|
||||
// Set up active download process for Beatport chart (like YouTube/Tidal)
|
||||
try {
|
||||
// Rehydrate the chart state first to get discovery results
|
||||
await rehydrateBeatportChart(chartInfo, false);
|
||||
|
||||
// Create/update active download process with tracks
|
||||
if (!activeDownloadProcesses[convertedPlaylistId]) {
|
||||
// Get tracks from the rehydrated state
|
||||
const ytState = youtubePlaylistStates[chartInfo.hash];
|
||||
let spotifyTracks = [];
|
||||
|
||||
if (ytState && ytState.discovery_results) {
|
||||
spotifyTracks = ytState.discovery_results
|
||||
.filter(result => result.spotify_data)
|
||||
.map(result => {
|
||||
const track = result.spotify_data;
|
||||
// Ensure artists is an array of strings
|
||||
if (track.artists && Array.isArray(track.artists)) {
|
||||
track.artists = track.artists.map(artist =>
|
||||
typeof artist === 'string' ? artist : (artist.name || artist)
|
||||
);
|
||||
} else if (track.artists && typeof track.artists === 'string') {
|
||||
track.artists = [track.artists];
|
||||
} else {
|
||||
track.artists = ['Unknown Artist'];
|
||||
}
|
||||
return {
|
||||
id: track.id,
|
||||
name: track.name,
|
||||
artists: track.artists,
|
||||
album: track.album || 'Unknown Album',
|
||||
duration_ms: track.duration_ms || 0,
|
||||
external_urls: track.external_urls || {}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
activeDownloadProcesses[convertedPlaylistId] = {
|
||||
status: chartInfo.phase === 'download_complete' ? 'complete' : 'running',
|
||||
batchId: chartInfo.download_process_id,
|
||||
tracks: spotifyTracks,
|
||||
modalElement: null
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`✅ Set up active download process for Beatport chart: ${chartInfo.name} with ${activeDownloadProcesses[convertedPlaylistId].tracks.length} tracks`);
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ Error setting up download process for Beatport chart "${chartInfo.name}":`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Successfully loaded and rehydrated ${charts.length} Beatport charts`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error loading Beatport charts from backend:', error);
|
||||
showToast(`Error loading Beatport charts: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function createBeatportCardFromBackendState(chartInfo) {
|
||||
// Create Beatport chart card from backend state data
|
||||
const chartHash = chartInfo.hash;
|
||||
const chartData = chartInfo.chart_data;
|
||||
const phase = chartInfo.phase;
|
||||
|
||||
const container = document.getElementById('beatport-playlist-container');
|
||||
|
||||
// Remove placeholder if it exists
|
||||
const placeholder = container.querySelector('.playlist-placeholder');
|
||||
if (placeholder) {
|
||||
placeholder.remove();
|
||||
}
|
||||
|
||||
// Create card HTML using same structure as createBeatportCard
|
||||
const cardHtml = `
|
||||
<div class="youtube-playlist-card beatport-chart-card" id="beatport-card-${chartHash}">
|
||||
<div class="playlist-card-icon">🎧</div>
|
||||
<div class="playlist-card-content">
|
||||
<div class="playlist-card-name">${escapeHtml(chartInfo.name)}</div>
|
||||
<div class="playlist-card-info">
|
||||
<span class="playlist-card-track-count">${chartInfo.track_count} tracks</span>
|
||||
<span class="playlist-card-phase-text" style="color: ${getPhaseColor(phase)};">${getPhaseText(phase)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-card-progress ${phase === 'fresh' ? 'hidden' : ''}">
|
||||
♪ ${chartInfo.spotify_total} / ✓ ${chartInfo.spotify_matches} / ✗ ${chartInfo.spotify_total - chartInfo.spotify_matches} (${Math.round((chartInfo.spotify_matches / chartInfo.spotify_total) * 100) || 0}%)
|
||||
</div>
|
||||
<button class="playlist-card-action-btn">${getActionButtonText(phase)}</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.insertAdjacentHTML('beforeend', cardHtml);
|
||||
|
||||
// Initialize state
|
||||
beatportChartStates[chartHash] = {
|
||||
phase: phase,
|
||||
chart: chartData,
|
||||
cardElement: document.getElementById(`beatport-card-${chartHash}`)
|
||||
};
|
||||
|
||||
// Add click handler
|
||||
const card = document.getElementById(`beatport-card-${chartHash}`);
|
||||
if (card) {
|
||||
card.addEventListener('click', async () => await handleBeatportCardClick(chartHash));
|
||||
}
|
||||
|
||||
console.log(`🃏 Created Beatport card from backend state: ${chartInfo.name} (${phase})`);
|
||||
}
|
||||
|
||||
async function rehydrateBeatportChart(chartInfo, userRequested = false) {
|
||||
// Rehydrate Beatport chart state and optionally open modal (similar to rehydrateYouTubePlaylist)
|
||||
const chartHash = chartInfo.hash;
|
||||
const chartName = chartInfo.name;
|
||||
|
||||
try {
|
||||
console.log(`🔄 [Rehydration] Starting rehydration for Beatport chart: ${chartName}`);
|
||||
|
||||
// Get full state from backend including discovery results
|
||||
let fullState;
|
||||
try {
|
||||
const stateResponse = await fetch(`/api/beatport/charts/status/${chartHash}`);
|
||||
if (stateResponse.ok) {
|
||||
fullState = await stateResponse.json();
|
||||
console.log(`📋 [Rehydration] Retrieved full backend state with ${fullState.discovery_results?.length || 0} discovery results`);
|
||||
} else {
|
||||
console.warn(`⚠️ [Rehydration] Could not fetch full state, using basic info`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ [Rehydration] Error fetching full state:`, error.message);
|
||||
}
|
||||
|
||||
const phase = chartInfo.phase;
|
||||
|
||||
// Create or update Beatport chart state
|
||||
if (!beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash] = {
|
||||
phase: 'fresh',
|
||||
chart: chartInfo.chart_data,
|
||||
cardElement: null
|
||||
};
|
||||
}
|
||||
|
||||
const state = beatportChartStates[chartHash];
|
||||
state.phase = phase;
|
||||
|
||||
// Transform discovery results if available (like Tidal does)
|
||||
let transformedResults = [];
|
||||
if (fullState && fullState.discovery_results) {
|
||||
transformedResults = fullState.discovery_results.map((result, index) => ({
|
||||
index: result.index !== undefined ? result.index : index,
|
||||
yt_track: result.beatport_track ? result.beatport_track.title : 'Unknown',
|
||||
yt_artist: result.beatport_track ? result.beatport_track.artist : 'Unknown',
|
||||
status: result.status === 'found' ? '✅ Found' : (result.status === 'error' ? '❌ Error' : '❌ Not Found'),
|
||||
status_class: result.status_class || (result.status === 'found' ? 'found' : (result.status === 'error' ? 'error' : 'not-found')),
|
||||
spotify_track: result.spotify_data ? result.spotify_data.name : '-',
|
||||
spotify_artist: result.spotify_data && result.spotify_data.artists ?
|
||||
result.spotify_data.artists.map(a => a.name || a).join(', ') : '-',
|
||||
spotify_album: result.spotify_data ? result.spotify_data.album : '-'
|
||||
}));
|
||||
}
|
||||
|
||||
// Store in YouTube state system (since Beatport reuses it)
|
||||
youtubePlaylistStates[chartHash] = {
|
||||
phase: phase,
|
||||
playlist: {
|
||||
name: chartName,
|
||||
tracks: chartInfo.chart_data.tracks,
|
||||
description: `${chartInfo.track_count} tracks from ${chartName}`,
|
||||
source: 'beatport'
|
||||
},
|
||||
is_beatport_playlist: true,
|
||||
beatport_chart_type: chartInfo.chart_data.chart_type,
|
||||
beatport_chart_hash: chartHash,
|
||||
discovery_progress: fullState?.discovery_progress || chartInfo.discovery_progress,
|
||||
discoveryProgress: fullState?.discovery_progress || chartInfo.discovery_progress,
|
||||
spotify_matches: fullState?.spotify_matches || chartInfo.spotify_matches,
|
||||
spotifyMatches: fullState?.spotify_matches || chartInfo.spotify_matches,
|
||||
discovery_results: fullState?.discovery_results || [],
|
||||
discoveryResults: transformedResults,
|
||||
convertedSpotifyPlaylistId: fullState?.converted_spotify_playlist_id || chartInfo.converted_spotify_playlist_id,
|
||||
download_process_id: fullState?.download_process_id || chartInfo.download_process_id,
|
||||
syncPlaylistId: fullState?.sync_playlist_id,
|
||||
syncProgress: fullState?.sync_progress || {}
|
||||
};
|
||||
|
||||
// Restore discovery results if we have them
|
||||
if (fullState && fullState.discovery_results) {
|
||||
console.log(`✅ Restored ${fullState.discovery_results.length} discovery results from backend`);
|
||||
|
||||
// Update modal if it already exists
|
||||
const existingModal = document.getElementById(`youtube-discovery-modal-${chartHash}`);
|
||||
if (existingModal && !existingModal.classList.contains('hidden')) {
|
||||
console.log(`🔄 Refreshing existing modal with restored discovery results`);
|
||||
refreshYouTubeDiscoveryModalTable(chartHash);
|
||||
}
|
||||
}
|
||||
|
||||
// Update card display
|
||||
updateBeatportCardPhase(chartHash, phase);
|
||||
updateBeatportCardProgress(chartHash, {
|
||||
spotify_total: chartInfo.spotify_total,
|
||||
spotify_matches: chartInfo.spotify_matches,
|
||||
failed: chartInfo.spotify_total - chartInfo.spotify_matches
|
||||
});
|
||||
|
||||
// Handle active discovery polling
|
||||
if (phase === 'discovering') {
|
||||
console.log(`🔍 Resuming discovery polling for: ${chartName}`);
|
||||
startBeatportDiscoveryPolling(chartHash);
|
||||
}
|
||||
|
||||
// Open modal if user requested
|
||||
if (userRequested) {
|
||||
switch (phase) {
|
||||
case 'discovering':
|
||||
case 'discovered':
|
||||
case 'syncing':
|
||||
case 'sync_complete':
|
||||
openYouTubeDiscoveryModal(chartHash);
|
||||
break;
|
||||
case 'downloading':
|
||||
case 'download_complete':
|
||||
// Open download modal if we have the converted playlist ID
|
||||
if (chartInfo.converted_spotify_playlist_id) {
|
||||
await openDownloadMissingModal(chartInfo.converted_spotify_playlist_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Successfully rehydrated Beatport chart: ${chartName}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error rehydrating Beatport chart "${chartName}":`, error);
|
||||
}
|
||||
}
|
||||
|
||||
function createYouTubeCardFromBackendState(playlistInfo) {
|
||||
// Create YouTube playlist card from backend state data
|
||||
const urlHash = playlistInfo.url_hash;
|
||||
|
|
@ -3451,6 +3806,41 @@ async function closeDownloadMissingModal(playlistId) {
|
|||
}
|
||||
}
|
||||
|
||||
// Reset Beatport chart phase to 'discovered' when modal is closed
|
||||
if (playlistId.startsWith('beatport_')) {
|
||||
const urlHash = playlistId.replace('beatport_', '');
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
|
||||
if (state && state.is_beatport_playlist) {
|
||||
console.log(`🧹 [Modal Close] Processing Beatport chart close: playlistId="${playlistId}", urlHash="${urlHash}"`);
|
||||
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
|
||||
// Reset to discovered phase (unless download actually started and completed)
|
||||
if (state.phase !== 'download_complete') {
|
||||
updateBeatportCardPhase(chartHash, 'discovered');
|
||||
state.phase = 'discovered';
|
||||
|
||||
// Update Beatport chart state
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = 'discovered';
|
||||
}
|
||||
|
||||
// Update backend state
|
||||
try {
|
||||
await fetch(`/api/beatport/charts/update-phase/${chartHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phase: 'discovered' })
|
||||
});
|
||||
console.log(`✅ [Modal Close] Updated backend phase for Beatport chart ${chartHash} to 'discovered'`);
|
||||
} catch (error) {
|
||||
console.error(`❌ [Modal Close] Error updating backend phase for Beatport chart ${chartHash}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced Tidal playlist state management (based on GUI sync.py patterns)
|
||||
if (playlistId.startsWith('tidal_')) {
|
||||
const tidalPlaylistId = playlistId.replace('tidal_', '');
|
||||
|
|
@ -3815,6 +4205,38 @@ async function startMissingTracksProcess(playlistId) {
|
|||
console.log(`🔄 Updated Tidal playlist ${tidalPlaylistId} to downloading phase`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Beatport chart phase to 'downloading' if this is a Beatport chart
|
||||
if (playlistId.startsWith('beatport_')) {
|
||||
const urlHash = playlistId.replace('beatport_', '');
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
|
||||
if (state && state.is_beatport_playlist) {
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
|
||||
// Update frontend states
|
||||
state.phase = 'downloading';
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = 'downloading';
|
||||
}
|
||||
|
||||
// Update card UI
|
||||
updateBeatportCardPhase(chartHash, 'downloading');
|
||||
|
||||
// Update backend state
|
||||
try {
|
||||
fetch(`/api/beatport/charts/update-phase/${chartHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phase: 'downloading' })
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error updating backend Beatport phase to downloading:', error);
|
||||
}
|
||||
|
||||
console.log(`🔄 Updated Beatport chart ${chartHash} to downloading phase`);
|
||||
}
|
||||
}
|
||||
document.getElementById(`begin-analysis-btn-${playlistId}`).style.display = 'none';
|
||||
document.getElementById(`cancel-all-btn-${playlistId}`).style.display = 'inline-block';
|
||||
|
||||
|
|
@ -4358,6 +4780,42 @@ function processModalStatusUpdate(playlistId, data) {
|
|||
console.log(`✅ [Status Complete] Updated Tidal playlist ${tidalPlaylistId} to download_complete phase`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Beatport chart phase to 'download_complete' if this is a Beatport chart
|
||||
if (playlistId.startsWith('beatport_')) {
|
||||
const urlHash = playlistId.replace('beatport_', '');
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
|
||||
if (state && state.is_beatport_playlist) {
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
|
||||
// Update frontend states
|
||||
state.phase = 'download_complete';
|
||||
state.download_process_id = process.batchId;
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = 'download_complete';
|
||||
}
|
||||
|
||||
// Update card UI
|
||||
updateBeatportCardPhase(chartHash, 'download_complete');
|
||||
|
||||
// Update backend state
|
||||
try {
|
||||
fetch(`/api/beatport/charts/update-phase/${chartHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
phase: 'download_complete',
|
||||
download_process_id: process.batchId
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error updating backend Beatport phase to download_complete:', error);
|
||||
}
|
||||
|
||||
console.log(`✅ [Status Complete] Updated Beatport chart ${chartHash} to download_complete phase`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle background wishlist processing completion specially
|
||||
if (isBackgroundWishlist) {
|
||||
|
|
@ -10047,6 +10505,11 @@ function startBeatportDiscoveryPolling(urlHash) {
|
|||
failed: (status.spotify_total || 0) - (status.spotify_matches || 0)
|
||||
});
|
||||
|
||||
// Sync with backend Beatport chart state
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = status.phase || 'discovering';
|
||||
}
|
||||
|
||||
// Update modal display with transformed data
|
||||
updateYouTubeDiscoveryModal(urlHash, transformedStatus);
|
||||
}
|
||||
|
|
@ -10165,7 +10628,7 @@ function addBeatportCardToContainer(chartData) {
|
|||
// Add click handler
|
||||
const card = document.getElementById(`beatport-card-${chartData.hash}`);
|
||||
if (card) {
|
||||
card.addEventListener('click', () => handleBeatportCardClick(chartData.hash));
|
||||
card.addEventListener('click', async () => await handleBeatportCardClick(chartData.hash));
|
||||
}
|
||||
|
||||
console.log(`🃏 Created Beatport card: ${chartData.name}`);
|
||||
|
|
@ -10195,9 +10658,53 @@ async function handleBeatportCardClick(chartHash) {
|
|||
console.log(`🎧 [Card Click] Opening Beatport discovery modal for ${state.phase} phase`);
|
||||
openYouTubeDiscoveryModal(chartHash);
|
||||
} else if (state.phase === 'downloading' || state.phase === 'download_complete') {
|
||||
// Show download modal
|
||||
console.log(`📥 [Card Click] Opening Beatport download modal for ${state.phase} phase`);
|
||||
showToast('Download modal for Beatport coming soon!', 'info');
|
||||
// Open download modal if we have the converted playlist ID
|
||||
const ytState = youtubePlaylistStates[chartHash];
|
||||
if (ytState && ytState.is_beatport_playlist && ytState.convertedSpotifyPlaylistId) {
|
||||
console.log(`📥 [Card Click] Opening Beatport download modal for ${state.phase} phase`);
|
||||
|
||||
// Get Spotify tracks from discovery results (like startBeatportDownloadMissing does)
|
||||
if (ytState.discovery_results && ytState.discovery_results.length > 0) {
|
||||
const spotifyTracks = ytState.discovery_results
|
||||
.filter(result => result.spotify_data)
|
||||
.map(result => {
|
||||
const track = result.spotify_data;
|
||||
// Ensure artists is an array of strings
|
||||
if (track.artists && Array.isArray(track.artists)) {
|
||||
track.artists = track.artists.map(artist =>
|
||||
typeof artist === 'string' ? artist : (artist.name || artist)
|
||||
);
|
||||
} else if (track.artists && typeof track.artists === 'string') {
|
||||
track.artists = [track.artists];
|
||||
} else {
|
||||
track.artists = ['Unknown Artist'];
|
||||
}
|
||||
return {
|
||||
id: track.id,
|
||||
name: track.name,
|
||||
artists: track.artists,
|
||||
album: track.album || 'Unknown Album',
|
||||
duration_ms: track.duration_ms || 0,
|
||||
external_urls: track.external_urls || {}
|
||||
};
|
||||
});
|
||||
|
||||
const playlistName = `[Beatport] ${ytState.playlist.name}`;
|
||||
try {
|
||||
await openDownloadMissingModalForYouTube(ytState.convertedSpotifyPlaylistId, playlistName, spotifyTracks);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error opening Beatport download modal:`, error);
|
||||
hideLoadingOverlay();
|
||||
showToast('Error opening download modal', 'error');
|
||||
}
|
||||
} else {
|
||||
console.warn(`⚠️ [Card Click] No discovery results found for Beatport chart: ${chartHash}`);
|
||||
showToast('No tracks found - try refreshing the page', 'error');
|
||||
}
|
||||
} else {
|
||||
console.warn(`⚠️ [Card Click] No converted playlist ID found for Beatport chart: ${chartHash}`);
|
||||
showToast('Download data not found - try refreshing the page', 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10332,15 +10839,27 @@ function startBeatportSyncPolling(urlHash) {
|
|||
// Update final state
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
if (state) {
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
if (status.complete) {
|
||||
state.phase = 'sync_complete';
|
||||
state.convertedSpotifyPlaylistId = status.converted_spotify_playlist_id;
|
||||
updateBeatportCardPhase(state.beatport_chart_hash || urlHash, 'sync_complete');
|
||||
updateBeatportCardPhase(chartHash, 'sync_complete');
|
||||
updateBeatportModalButtons(urlHash, 'sync_complete');
|
||||
|
||||
// Sync with backend Beatport chart state
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = 'sync_complete';
|
||||
}
|
||||
|
||||
console.log('✅ Beatport sync complete:', urlHash);
|
||||
} else {
|
||||
state.phase = 'discovered'; // Revert on error
|
||||
updateBeatportCardPhase(state.beatport_chart_hash || urlHash, 'discovered');
|
||||
updateBeatportCardPhase(chartHash, 'discovered');
|
||||
|
||||
// Sync with backend Beatport chart state
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = 'discovered';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10393,7 +10912,7 @@ function updateBeatportModalButtons(urlHash, phase) {
|
|||
|
||||
async function startBeatportDownloadMissing(urlHash) {
|
||||
try {
|
||||
console.log('🔍 Starting download missing tracks for Beatport playlist:', urlHash);
|
||||
console.log('🔍 Starting download missing tracks for Beatport chart:', urlHash);
|
||||
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
if (!state || !state.discovery_results) {
|
||||
|
|
@ -10401,18 +10920,35 @@ async function startBeatportDownloadMissing(urlHash) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Convert Beatport results to a format compatible with the download modal (same as YouTube)
|
||||
if (!state.is_beatport_playlist) {
|
||||
console.error('❌ State is not a Beatport playlist');
|
||||
showToast('Invalid Beatport chart state', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert Beatport discovery results to Spotify tracks format (like Tidal does)
|
||||
const spotifyTracks = state.discovery_results
|
||||
.filter(result => result.spotify_data)
|
||||
.map(result => {
|
||||
const track = result.spotify_data;
|
||||
// Ensure artists is an array of strings (like YouTube format)
|
||||
// Ensure artists is an array of strings
|
||||
if (track.artists && Array.isArray(track.artists)) {
|
||||
track.artists = track.artists.map(artist =>
|
||||
typeof artist === 'string' ? artist : (artist.name || artist)
|
||||
);
|
||||
} else if (track.artists && typeof track.artists === 'string') {
|
||||
track.artists = [track.artists];
|
||||
} else {
|
||||
track.artists = ['Unknown Artist'];
|
||||
}
|
||||
return track;
|
||||
return {
|
||||
id: track.id,
|
||||
name: track.name,
|
||||
artists: track.artists,
|
||||
album: track.album || 'Unknown Album',
|
||||
duration_ms: track.duration_ms || 0,
|
||||
external_urls: track.external_urls || {}
|
||||
};
|
||||
});
|
||||
|
||||
if (spotifyTracks.length === 0) {
|
||||
|
|
@ -10420,27 +10956,49 @@ async function startBeatportDownloadMissing(urlHash) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Create a virtual playlist for the download system (same as YouTube)
|
||||
console.log(`🎧 Found ${spotifyTracks.length} Spotify tracks for Beatport download`);
|
||||
|
||||
// Create a virtual playlist for the download system
|
||||
const virtualPlaylistId = `beatport_${urlHash}`;
|
||||
const playlistName = `[Beatport] ${state.playlist.name}`;
|
||||
|
||||
// Store reference for card navigation (same as YouTube)
|
||||
// Store reference for card navigation (but don't change phase yet)
|
||||
state.convertedSpotifyPlaylistId = virtualPlaylistId;
|
||||
|
||||
// Close the discovery modal if it's open (same as YouTube)
|
||||
// Store converted playlist ID in backend but keep current phase
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
if (beatportChartStates[chartHash]) {
|
||||
try {
|
||||
await fetch(`/api/beatport/charts/update-phase/${chartHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
phase: state.phase, // Keep current phase (should be 'discovered')
|
||||
converted_spotify_playlist_id: virtualPlaylistId
|
||||
})
|
||||
});
|
||||
console.log('✅ Updated backend with Beatport converted playlist ID (phase unchanged)');
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error updating backend Beatport state:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the discovery modal if it's open
|
||||
const discoveryModal = document.getElementById(`youtube-discovery-modal-${urlHash}`);
|
||||
if (discoveryModal) {
|
||||
discoveryModal.classList.add('hidden');
|
||||
console.log('🔄 Closed Beatport discovery modal to show download modal');
|
||||
}
|
||||
|
||||
// Open download missing tracks modal for Beatport playlist (same as YouTube)
|
||||
// DON'T update card phase here - let the download modal handle phase changes when "Begin Analysis" is clicked
|
||||
|
||||
// Open download missing tracks modal using the same system as YouTube/Tidal
|
||||
await openDownloadMissingModalForYouTube(virtualPlaylistId, playlistName, spotifyTracks);
|
||||
|
||||
// Phase will change to 'downloading' when user clicks "Begin Analysis" button
|
||||
console.log(`✅ Opened download modal for Beatport chart: ${state.playlist.name}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting download missing tracks:', error);
|
||||
console.error('❌ Error starting Beatport download missing tracks:', error);
|
||||
showToast(`Error starting downloads: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
|
@ -11153,6 +11711,8 @@ function getModalActionButtons(urlHash, phase, state = null) {
|
|||
if (isTidal) {
|
||||
// Tidal doesn't have a reset function yet, but could be added
|
||||
// syncCompleteButtons += `<button class="modal-btn modal-btn-secondary" onclick="resetTidalPlaylist('${urlHash}')">🔄 Reset</button>`;
|
||||
} else if (isBeatport) {
|
||||
syncCompleteButtons += `<button class="modal-btn modal-btn-secondary" onclick="resetBeatportChart('${urlHash}')">🔄 Reset</button>`;
|
||||
} else {
|
||||
syncCompleteButtons += `<button class="modal-btn modal-btn-secondary" onclick="resetYouTubePlaylist('${urlHash}')">🔄 Reset</button>`;
|
||||
}
|
||||
|
|
@ -11322,7 +11882,91 @@ function closeYouTubeDiscoveryModal(urlHash) {
|
|||
modal.classList.add('hidden');
|
||||
console.log('🚪 Hidden YouTube discovery modal (preserving state):', urlHash);
|
||||
}
|
||||
|
||||
|
||||
// Handle phase reset for completed discovery (Tidal/Beatport pattern)
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
if (state) {
|
||||
const isTidal = state.is_tidal_playlist;
|
||||
const isBeatport = state.is_beatport_playlist;
|
||||
|
||||
// Reset to 'discovered' phase if modal is closed after completion (like Tidal does)
|
||||
if (state.phase === 'sync_complete' || state.phase === 'download_complete') {
|
||||
console.log(`🧹 [Modal Close] Resetting ${isBeatport ? 'Beatport' : (isTidal ? 'Tidal' : 'YouTube')} state after completion`);
|
||||
|
||||
if (isTidal) {
|
||||
// Tidal: Extract playlist ID and reset Tidal state
|
||||
const tidalPlaylistId = state.beatport_chart_hash ? state.beatport_chart_hash.replace('tidal_', '') : null;
|
||||
if (tidalPlaylistId && tidalPlaylistStates[tidalPlaylistId]) {
|
||||
// Preserve discovery data but reset phase
|
||||
const preservedData = {
|
||||
playlist: tidalPlaylistStates[tidalPlaylistId].playlist,
|
||||
discovery_results: tidalPlaylistStates[tidalPlaylistId].discovery_results,
|
||||
spotify_matches: tidalPlaylistStates[tidalPlaylistId].spotify_matches,
|
||||
discovery_progress: tidalPlaylistStates[tidalPlaylistId].discovery_progress,
|
||||
convertedSpotifyPlaylistId: tidalPlaylistStates[tidalPlaylistId].convertedSpotifyPlaylistId
|
||||
};
|
||||
|
||||
// Clear download state
|
||||
delete tidalPlaylistStates[tidalPlaylistId].download_process_id;
|
||||
delete tidalPlaylistStates[tidalPlaylistId].phase;
|
||||
|
||||
// Restore preserved data and set to discovered phase
|
||||
Object.assign(tidalPlaylistStates[tidalPlaylistId], preservedData);
|
||||
tidalPlaylistStates[tidalPlaylistId].phase = 'discovered';
|
||||
|
||||
updateTidalCardPhase(tidalPlaylistId, 'discovered');
|
||||
|
||||
// Update backend state
|
||||
try {
|
||||
fetch(`/api/tidal/update-phase/${tidalPlaylistId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phase: 'discovered' })
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error updating backend Tidal phase:', error);
|
||||
}
|
||||
}
|
||||
} else if (isBeatport) {
|
||||
// Beatport: Reset chart state
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
if (beatportChartStates[chartHash]) {
|
||||
beatportChartStates[chartHash].phase = 'discovered';
|
||||
updateBeatportCardPhase(chartHash, 'discovered');
|
||||
|
||||
// Update backend state
|
||||
try {
|
||||
fetch(`/api/beatport/charts/update-phase/${chartHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phase: 'discovered' })
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error updating backend Beatport phase:', error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// YouTube: Reset to discovered phase
|
||||
updateYouTubeCardPhase(urlHash, 'discovered');
|
||||
|
||||
// Update backend state
|
||||
try {
|
||||
fetch(`/api/youtube/update-phase/${urlHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phase: 'discovered' })
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Error updating backend YouTube phase:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset frontend state to discovered
|
||||
state.phase = 'discovered';
|
||||
console.log(`✅ [Modal Close] Reset to discovered phase: ${urlHash}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep modal reference and all state intact
|
||||
// Discovery polling continues in background if active
|
||||
}
|
||||
|
|
@ -11624,13 +12268,82 @@ async function resetYouTubePlaylist(urlHash) {
|
|||
|
||||
showToast(`Reset "${state.playlist.name}" to fresh state`, 'success');
|
||||
console.log(`✅ Successfully reset YouTube playlist: ${state.playlist.name}`);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error resetting YouTube playlist:`, error);
|
||||
showToast(`Error resetting playlist: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function resetBeatportChart(urlHash) {
|
||||
const state = youtubePlaylistStates[urlHash];
|
||||
const chartState = beatportChartStates[urlHash];
|
||||
|
||||
if (!state || !state.is_beatport_playlist || !chartState) {
|
||||
console.error('❌ Invalid Beatport chart state for reset');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`🔄 Resetting Beatport chart to fresh state: ${state.playlist.name}`);
|
||||
|
||||
// Call backend reset endpoint for Beatport
|
||||
const chartHash = state.beatport_chart_hash || urlHash;
|
||||
const response = await fetch(`/api/beatport/charts/update-phase/${chartHash}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
phase: 'fresh',
|
||||
reset: true
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'Failed to reset Beatport chart');
|
||||
}
|
||||
|
||||
// Stop any active polling
|
||||
if (activeYouTubePollers[urlHash]) {
|
||||
clearInterval(activeYouTubePollers[urlHash]);
|
||||
delete activeYouTubePollers[urlHash];
|
||||
}
|
||||
|
||||
// Update client state to match backend reset
|
||||
state.phase = 'fresh';
|
||||
state.discoveryResults = [];
|
||||
state.discoveryProgress = 0;
|
||||
state.spotifyMatches = 0;
|
||||
state.discovery_results = [];
|
||||
state.discovery_progress = 0;
|
||||
state.spotify_matches = 0;
|
||||
state.syncPlaylistId = null;
|
||||
state.syncProgress = {};
|
||||
state.convertedSpotifyPlaylistId = null;
|
||||
|
||||
// Update Beatport chart state
|
||||
chartState.phase = 'fresh';
|
||||
|
||||
// Update card to reflect fresh state
|
||||
updateBeatportCardPhase(chartHash, 'fresh');
|
||||
updateBeatportCardProgress(chartHash, {
|
||||
spotify_total: state.playlist.tracks.length,
|
||||
spotify_matches: 0,
|
||||
failed: 0
|
||||
});
|
||||
|
||||
// Close modal
|
||||
closeYouTubeDiscoveryModal(urlHash);
|
||||
|
||||
showToast(`Reset "${state.playlist.name}" to fresh state`, 'success');
|
||||
console.log(`✅ Successfully reset Beatport chart: ${state.playlist.name}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error resetting Beatport chart:`, error);
|
||||
showToast(`Error resetting chart: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ARTISTS PAGE FUNCTIONALITY - ELEGANT SEARCH & DISCOVERY
|
||||
// ============================================================================
|
||||
|
|
|
|||
Loading…
Reference in a new issue