diff --git a/web_server.py b/web_server.py index 5fc7b33e..33b94a32 100644 --- a/web_server.py +++ b/web_server.py @@ -13516,6 +13516,71 @@ def get_beatport_homepage_top10_releases_cards(): "releases_count": 0 }), 500 +@app.route('/api/beatport/scrape-releases', methods=['POST']) +def scrape_beatport_releases(): + """General scraper endpoint - takes release URLs and returns tracks""" + try: + data = request.get_json() + if not data: + return jsonify({ + "success": False, + "error": "No JSON data provided", + "tracks": [], + "track_count": 0 + }), 400 + + release_urls = data.get('release_urls', []) + source_name = data.get('source_name', 'General Release Scraper') + + if not release_urls: + return jsonify({ + "success": False, + "error": "No release URLs provided", + "tracks": [], + "track_count": 0 + }), 400 + + logger.info(f"🎯 API request to scrape {len(release_urls)} release URLs with source: {source_name}") + + # Initialize the Beatport scraper + scraper = BeatportUnifiedScraper() + + # Use our new general scraper function + tracks = scraper.scrape_multiple_releases(release_urls, source_name) + + logger.info(f"✅ Successfully extracted {len(tracks)} tracks from {len(release_urls)} releases") + + # Apply text cleaning to track data + cleaned_tracks = [] + for track in tracks: + cleaned_track = track.copy() + if 'title' in cleaned_track: + cleaned_track['title'] = clean_beatport_text(cleaned_track['title']) + if 'artist' in cleaned_track: + cleaned_track['artist'] = clean_beatport_text(cleaned_track['artist']) + if 'label' in cleaned_track: + cleaned_track['label'] = clean_beatport_text(cleaned_track['label']) + cleaned_tracks.append(cleaned_track) + + return jsonify({ + "success": True, + "tracks": cleaned_tracks, + "track_count": len(cleaned_tracks), + "source": source_name, + "release_urls_processed": len(release_urls) + }) + + except Exception as e: + logger.error(f"❌ Error scraping releases: {e}") + import traceback + logger.error(f"Full traceback: {traceback.format_exc()}") + return jsonify({ + "success": False, + "error": str(e), + "tracks": [], + "track_count": 0 + }), 500 + @app.route('/api/beatport/homepage/featured-charts', methods=['GET']) def get_beatport_homepage_featured_charts(): """Get Beatport Featured Charts from homepage section""" diff --git a/webui/static/script.js b/webui/static/script.js index 2869be7d..4c75ebb1 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -9819,6 +9819,16 @@ function initializeSyncPage() { }); } + // Logic for Rebuild page Hero Slider - follows same pattern as Top 10 lists + const beatportHeroSliderContainer = document.querySelector('.beatport-rebuild-slider-container'); + if (beatportHeroSliderContainer) { + beatportHeroSliderContainer.addEventListener('click', (event) => { + console.log('🎯 Hero slider container clicked on rebuild page'); + event.stopPropagation(); // Prevent event bubbling + handleRebuildHeroSliderClick(); + }); + } + // Logic for the Start Sync button const startSyncBtn = document.getElementById('start-sync-btn'); if (startSyncBtn) { @@ -10742,7 +10752,8 @@ function showBeatportMainView() { // Global variable to store rebuild page track data for reuse let rebuildPageTrackData = { beatport_top10: null, - hype_top10: null + hype_top10: null, + hero_slider: null }; async function handleRebuildBeatportTop10Click() { @@ -10759,6 +10770,31 @@ async function handleRebuildHypeTop10Click() { await handleRebuildChartClick('hype_top10', 'Hype Top 10', 'rebuild_hype_top10'); } +// Debounce flag to prevent double execution +let heroSliderClickInProgress = false; + +async function handleRebuildHeroSliderClick() { + console.log('🎯 Handling Hero Slider click on rebuild page'); + + // Prevent double execution + if (heroSliderClickInProgress) { + console.log('⏳ Hero slider click already in progress, ignoring...'); + return; + } + + heroSliderClickInProgress = true; + + try { + // Use the existing chart creation pattern from Browse Charts EXACTLY + await handleRebuildChartClick('hero_slider', 'Hero Slider Releases', 'rebuild_hero_slider'); + } finally { + // Reset flag after completion (whether success or failure) + setTimeout(() => { + heroSliderClickInProgress = false; + }, 1000); // 1 second cooldown + } +} + async function handleRebuildChartClick(trackDataKey, chartName, chartType) { try { // Create chart hash (following Browse Charts pattern) @@ -10831,6 +10867,9 @@ async function getRebuildPageTrackData(trackDataKey) { } else if (trackDataKey === 'hype_top10') { containerSelector = '#beatport-hype10-list'; cardSelector = '.beatport-hype10-card[data-url]'; + } else if (trackDataKey === 'hero_slider') { + // Hero slider needs special handling - extract release URLs and scrape tracks + return await getHeroSliderTrackData(); } else { throw new Error(`Unknown track data key: ${trackDataKey}`); } @@ -10870,6 +10909,64 @@ async function getRebuildPageTrackData(trackDataKey) { return tracks; } +async function getHeroSliderTrackData() { + console.log('🎯 Getting hero slider track data'); + + try { + // Show loading overlay + showLoadingOverlay('Extracting tracks from releases...'); + + // Extract all release URLs from hero slider slides + const heroSlides = document.querySelectorAll('.beatport-rebuild-slide[data-url]'); + const releaseUrls = Array.from(heroSlides).map(slide => slide.getAttribute('data-url')).filter(url => url); + + console.log(`🎯 Found ${releaseUrls.length} release URLs from hero slider:`, releaseUrls); + + if (releaseUrls.length === 0) { + hideLoadingOverlay(); + throw new Error('No release URLs found in hero slider'); + } + + // Call our general scraper function to get tracks from all releases + const response = await fetch('/api/beatport/scrape-releases', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + release_urls: releaseUrls, + source_name: 'Hero Slider Releases' + }) + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + + if (!data.success || !data.tracks || data.tracks.length === 0) { + hideLoadingOverlay(); + throw new Error('No tracks found in hero slider releases'); + } + + console.log(`🎯 Got ${data.tracks.length} tracks from hero slider releases`); + + // Hide loading overlay + hideLoadingOverlay(); + + // Cache for future use + rebuildPageTrackData.hero_slider = data.tracks; + + return data.tracks; + + } catch (error) { + console.error('❌ Error getting hero slider track data:', error); + hideLoadingOverlay(); + throw error; + } +} + // Hook into the loadBeatportTop10Lists function to cache track data const originalLoadBeatportTop10Lists = window.loadBeatportTop10Lists; if (originalLoadBeatportTop10Lists) {