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:
parent
4f081bfd97
commit
ed5328e6c7
2 changed files with 163 additions and 1 deletions
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue