Add Beatport hero slider releases scraping support

Introduces a new API endpoint to scrape tracks from multiple Beatport release URLs and integrates hero slider click handling on the rebuild page. The frontend now extracts release URLs from the hero slider and fetches track data using the new backend endpoint, improving rebuild page functionality.
This commit is contained in:
Broque Thomas 2025-09-30 20:50:16 -07:00
parent 4f081bfd97
commit ed5328e6c7
2 changed files with 163 additions and 1 deletions

View file

@ -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"""

View file

@ -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) {