beatport progress
This commit is contained in:
parent
d885316077
commit
df11474951
4 changed files with 850 additions and 0 deletions
|
|
@ -13321,6 +13321,102 @@ def get_beatport_dj_charts_improved():
|
|||
"count": 0
|
||||
}), 500
|
||||
|
||||
|
||||
@app.route('/api/beatport/hype-picks')
|
||||
def get_beatport_hype_picks():
|
||||
"""Get Beatport Hype Picks for the rebuild slider grid (EXACT same pattern as new-releases)"""
|
||||
try:
|
||||
logger.info("🔥 Fetching Beatport hype picks...")
|
||||
|
||||
# Initialize scraper
|
||||
scraper = BeatportUnifiedScraper()
|
||||
|
||||
# Get page and extract releases
|
||||
soup = scraper.get_page(scraper.base_url)
|
||||
if not soup:
|
||||
raise Exception("Could not fetch Beatport homepage")
|
||||
|
||||
# Extract hype pick cards using data-testid selector (equivalent to new-releases CSS selector)
|
||||
hype_pick_cards = soup.select('[data-testid="hype-picks"]')
|
||||
releases = []
|
||||
|
||||
logger.info(f"🔍 Found {len(hype_pick_cards)} hype pick cards")
|
||||
|
||||
for i, card in enumerate(hype_pick_cards[:100]): # Limit to 100 for 10 slides (same as new-releases)
|
||||
release_data = {}
|
||||
|
||||
# Extract title (exact same logic as new-releases)
|
||||
title_elem = card.select_one('[class*="title"], [class*="Title"], h1, h2, h3, h4, h5, h6')
|
||||
if title_elem:
|
||||
title_text = title_elem.get_text(strip=True)
|
||||
if title_text and len(title_text) > 2 and title_text not in ['Hype Picks', 'Buy', 'Play']:
|
||||
release_data['title'] = title_text
|
||||
|
||||
# Extract artist (exact same logic as new-releases)
|
||||
artist_elem = card.select_one('[class*="artist"], [class*="Artist"], a[href*="/artist/"]')
|
||||
if artist_elem:
|
||||
artist_text = artist_elem.get_text(strip=True)
|
||||
if artist_text and len(artist_text) > 1:
|
||||
release_data['artist'] = artist_text
|
||||
|
||||
# Extract label (exact same logic as new-releases)
|
||||
label_elem = card.select_one('[class*="label"], [class*="Label"], a[href*="/label/"]')
|
||||
if label_elem:
|
||||
label_text = label_elem.get_text(strip=True)
|
||||
if label_text and len(label_text) > 1:
|
||||
release_data['label'] = label_text
|
||||
|
||||
# Extract URL (exact same logic as new-releases)
|
||||
url_link = card.select_one('a[href*="/release/"]')
|
||||
if url_link:
|
||||
href = url_link.get('href')
|
||||
if href:
|
||||
release_data['url'] = urljoin(scraper.base_url, href)
|
||||
|
||||
# Extract image (exact same logic as new-releases)
|
||||
img = card.select_one('img')
|
||||
if img:
|
||||
src = img.get('src') or img.get('data-src') or img.get('data-lazy-src')
|
||||
if src:
|
||||
release_data['image_url'] = src
|
||||
|
||||
# URL fallback for title (exact same logic as new-releases)
|
||||
if not release_data.get('title') and release_data.get('url'):
|
||||
url_parts = release_data['url'].split('/release/')
|
||||
if len(url_parts) > 1:
|
||||
slug = url_parts[1].split('/')[0]
|
||||
release_data['title'] = slug.replace('-', ' ').title()
|
||||
|
||||
# Only add if we have essential data (exact same logic as new-releases)
|
||||
if release_data.get('title') and release_data.get('url'):
|
||||
# Add fallbacks for missing data (exact same logic as new-releases)
|
||||
if not release_data.get('artist'):
|
||||
release_data['artist'] = 'Various Artists'
|
||||
if not release_data.get('label'):
|
||||
release_data['label'] = 'Unknown Label'
|
||||
|
||||
releases.append(release_data)
|
||||
|
||||
logger.info(f"✅ Successfully extracted {len(releases)} hype picks")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'releases': releases,
|
||||
'count': len(releases),
|
||||
'slides': (len(releases) + 9) // 10, # Calculate number of slides needed (same as new-releases)
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting Beatport hype picks: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(e),
|
||||
'releases': [],
|
||||
'count': 0
|
||||
}), 500
|
||||
|
||||
|
||||
@app.route('/api/beatport/discovery/start/<url_hash>', methods=['POST'])
|
||||
def start_beatport_discovery(url_hash):
|
||||
"""Start Spotify discovery for Beatport chart tracks"""
|
||||
|
|
|
|||
|
|
@ -839,6 +839,39 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hype Picks Grid Slideshow Section -->
|
||||
<div class="beatport-hype-picks-section">
|
||||
<div class="beatport-hype-picks-header">
|
||||
<h2 class="beatport-hype-picks-title">🔥 Hype Picks</h2>
|
||||
<p class="beatport-hype-picks-subtitle">Editor selected trending tracks from Beatport</p>
|
||||
</div>
|
||||
|
||||
<div class="beatport-hype-picks-slider-container">
|
||||
<div class="beatport-hype-picks-slider" id="beatport-hype-picks-slider">
|
||||
<div class="beatport-hype-picks-slider-track" id="beatport-hype-picks-slider-track">
|
||||
<!-- Loading placeholder -->
|
||||
<div class="beatport-hype-picks-loading">
|
||||
<div class="beatport-hype-picks-loading-content">
|
||||
<h3>🔥 Loading Hype Picks...</h3>
|
||||
<p>Fetching the hottest trending tracks</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slider Navigation -->
|
||||
<div class="beatport-hype-picks-slider-nav">
|
||||
<button class="beatport-hype-picks-nav-btn beatport-hype-picks-prev-btn" id="beatport-hype-picks-prev-btn">‹</button>
|
||||
<button class="beatport-hype-picks-nav-btn beatport-hype-picks-next-btn" id="beatport-hype-picks-next-btn">›</button>
|
||||
</div>
|
||||
|
||||
<!-- Slider Indicators -->
|
||||
<div class="beatport-hype-picks-slider-indicators" id="beatport-hype-picks-slider-indicators">
|
||||
<!-- Indicators will be dynamically generated -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Featured Charts Grid Slideshow Section -->
|
||||
<div class="beatport-charts-section">
|
||||
<div class="beatport-charts-header">
|
||||
|
|
|
|||
|
|
@ -9735,6 +9735,7 @@ function initializeSyncPage() {
|
|||
if (tabId === 'rebuild') {
|
||||
initializeBeatportRebuildSlider();
|
||||
initializeBeatportReleasesSlider();
|
||||
initializeBeatportHypePicksSlider();
|
||||
initializeBeatportChartsSlider();
|
||||
initializeBeatportDJSlider();
|
||||
}
|
||||
|
|
@ -18830,6 +18831,335 @@ function cleanupBeatportReleasesSlider() {
|
|||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// BEATPORT HYPE PICKS SLIDER
|
||||
// ===================================
|
||||
|
||||
// Hype Picks Slider State
|
||||
let beatportHypePicksSliderState = {
|
||||
currentSlide: 0,
|
||||
totalSlides: 0,
|
||||
autoPlayInterval: null,
|
||||
autoPlayDelay: 4000,
|
||||
isInitialized: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the beatport hype picks slider functionality (based on releases slider)
|
||||
*/
|
||||
function initializeBeatportHypePicksSlider() {
|
||||
console.log('🔥 Initializing beatport hype picks slider...');
|
||||
|
||||
const slider = document.getElementById('beatport-hype-picks-slider');
|
||||
if (!slider) {
|
||||
console.warn('Beatport hype picks slider not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if already initialized
|
||||
if (beatportHypePicksSliderState.isInitialized) {
|
||||
console.log('Beatport hype picks slider already initialized, skipping...');
|
||||
startBeatportHypePicksSliderAutoPlay(); // Just restart autoplay
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as initialized
|
||||
beatportHypePicksSliderState.isInitialized = true;
|
||||
|
||||
// Reset state
|
||||
beatportHypePicksSliderState.currentSlide = 0;
|
||||
beatportHypePicksSliderState.totalSlides = 0;
|
||||
|
||||
// Load data and initialize
|
||||
loadBeatportHypePicks().then(success => {
|
||||
if (success) {
|
||||
setupBeatportHypePicksSliderNavigation();
|
||||
setupBeatportHypePicksSliderIndicators();
|
||||
setupBeatportHypePicksSliderHoverPause();
|
||||
startBeatportHypePicksSliderAutoPlay();
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ Beatport hype picks slider initialized successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load hype picks data from API
|
||||
*/
|
||||
async function loadBeatportHypePicks() {
|
||||
try {
|
||||
console.log('🔥 Fetching hype picks data...');
|
||||
|
||||
const response = await fetch('/api/beatport/hype-picks');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.releases && data.releases.length > 0) {
|
||||
console.log(`🔥 Loaded ${data.releases.length} hype picks releases`);
|
||||
populateBeatportHypePicksSlider(data.releases);
|
||||
return true;
|
||||
} else {
|
||||
console.error('Failed to load hype picks:', data.error || 'No hype picks found');
|
||||
showBeatportHypePicksError(data.error || 'No hype picks available');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading hype picks:', error);
|
||||
showBeatportHypePicksError('Failed to load hype picks');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the hype picks slider with data (based on releases slider)
|
||||
*/
|
||||
function populateBeatportHypePicksSlider(releases) {
|
||||
const sliderTrack = document.getElementById('beatport-hype-picks-slider-track');
|
||||
const indicatorsContainer = document.getElementById('beatport-hype-picks-slider-indicators');
|
||||
|
||||
if (!sliderTrack || !indicatorsContainer) return;
|
||||
|
||||
// Clear existing content
|
||||
sliderTrack.innerHTML = '';
|
||||
indicatorsContainer.innerHTML = '';
|
||||
|
||||
// Group releases into slides (10 releases per slide in 5x2 grid)
|
||||
const releasesPerSlide = 10;
|
||||
const slides = [];
|
||||
for (let i = 0; i < releases.length; i += releasesPerSlide) {
|
||||
slides.push(releases.slice(i, i + releasesPerSlide));
|
||||
}
|
||||
|
||||
console.log(`🔥 Hype Picks: Got ${releases.length} releases, creating ${slides.length} slides`);
|
||||
beatportHypePicksSliderState.totalSlides = slides.length;
|
||||
beatportHypePicksSliderState.currentSlide = 0;
|
||||
|
||||
// Create slides
|
||||
slides.forEach((slideReleases, slideIndex) => {
|
||||
const slideHtml = `
|
||||
<div class="beatport-hype-picks-slide ${slideIndex === 0 ? 'active' : ''}"
|
||||
data-slide="${slideIndex}">
|
||||
<div class="beatport-hype-picks-grid">
|
||||
${slideReleases.map(release => createBeatportHypePickCard(release)).join('')}
|
||||
${slideReleases.length < releasesPerSlide ?
|
||||
Array(releasesPerSlide - slideReleases.length).fill(0).map(() =>
|
||||
`<div class="beatport-hype-pick-card beatport-hype-pick-placeholder">
|
||||
<div class="placeholder-icon">🔥</div>
|
||||
</div>`
|
||||
).join('') : ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
sliderTrack.insertAdjacentHTML('beforeend', slideHtml);
|
||||
console.log(`🔥 Created slide ${slideIndex + 1}/${slides.length} with ${slideReleases.length} releases`);
|
||||
|
||||
// Create indicator
|
||||
const indicatorHtml = `<button class="beatport-hype-picks-indicator ${slideIndex === 0 ? 'active' : ''}" data-slide="${slideIndex}"></button>`;
|
||||
indicatorsContainer.insertAdjacentHTML('beforeend', indicatorHtml);
|
||||
});
|
||||
|
||||
// Add click handlers to track cards
|
||||
setupBeatportHypePickCardHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hype pick card HTML (for release cards, same as new releases)
|
||||
*/
|
||||
function createBeatportHypePickCard(release) {
|
||||
const artworkUrl = release.image_url || '';
|
||||
const bgStyle = artworkUrl ? `style="--card-bg-image: url('${artworkUrl}')"` : '';
|
||||
|
||||
return `
|
||||
<div class="beatport-hype-pick-card" data-url="${release.url || ''}" ${bgStyle}>
|
||||
<div class="beatport-hype-pick-card-content">
|
||||
<div class="beatport-hype-pick-artwork">
|
||||
${artworkUrl ? `<img src="${artworkUrl}" alt="${release.title || 'Release'}" loading="lazy">` : ''}
|
||||
</div>
|
||||
<div class="beatport-hype-pick-info">
|
||||
<div class="beatport-hype-pick-title">${release.title || 'Unknown Title'}</div>
|
||||
<div class="beatport-hype-pick-artist">${release.artist || 'Unknown Artist'}</div>
|
||||
<div class="beatport-hype-pick-label">${release.label || 'Hype Pick'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup navigation for hype picks slider (same pattern as releases)
|
||||
*/
|
||||
function setupBeatportHypePicksSliderNavigation() {
|
||||
const prevBtn = document.getElementById('beatport-hype-picks-prev-btn');
|
||||
const nextBtn = document.getElementById('beatport-hype-picks-next-btn');
|
||||
|
||||
if (prevBtn) {
|
||||
// Clone button to remove all existing event listeners
|
||||
const newPrevBtn = prevBtn.cloneNode(true);
|
||||
prevBtn.parentNode.replaceChild(newPrevBtn, prevBtn);
|
||||
|
||||
newPrevBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log('Previous hype picks button clicked, current slide:', beatportHypePicksSliderState.currentSlide);
|
||||
goToBeatportHypePicksSlide(beatportHypePicksSliderState.currentSlide - 1);
|
||||
resetBeatportHypePicksSliderAutoPlay();
|
||||
});
|
||||
}
|
||||
|
||||
if (nextBtn) {
|
||||
// Clone button to remove all existing event listeners
|
||||
const newNextBtn = nextBtn.cloneNode(true);
|
||||
nextBtn.parentNode.replaceChild(newNextBtn, nextBtn);
|
||||
|
||||
newNextBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log('Next hype picks button clicked, current slide:', beatportHypePicksSliderState.currentSlide);
|
||||
goToBeatportHypePicksSlide(beatportHypePicksSliderState.currentSlide + 1);
|
||||
resetBeatportHypePicksSliderAutoPlay();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup indicators for hype picks slider
|
||||
*/
|
||||
function setupBeatportHypePicksSliderIndicators() {
|
||||
const indicators = document.querySelectorAll('.beatport-hype-picks-indicator');
|
||||
|
||||
indicators.forEach((indicator, index) => {
|
||||
indicator.addEventListener('click', () => {
|
||||
goToBeatportHypePicksSlide(index);
|
||||
resetBeatportHypePicksSliderAutoPlay();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to specific slide
|
||||
*/
|
||||
function goToBeatportHypePicksSlide(slideIndex) {
|
||||
console.log('goToBeatportHypePicksSlide called with:', slideIndex, 'current:', beatportHypePicksSliderState.currentSlide);
|
||||
|
||||
// Handle wrap around
|
||||
if (slideIndex < 0) {
|
||||
slideIndex = beatportHypePicksSliderState.totalSlides - 1;
|
||||
} else if (slideIndex >= beatportHypePicksSliderState.totalSlides) {
|
||||
slideIndex = 0;
|
||||
}
|
||||
|
||||
// Update current slide
|
||||
beatportHypePicksSliderState.currentSlide = slideIndex;
|
||||
|
||||
// Update slides
|
||||
const slides = document.querySelectorAll('.beatport-hype-picks-slide');
|
||||
slides.forEach((slide, index) => {
|
||||
slide.classList.remove('active', 'prev', 'next');
|
||||
if (index === slideIndex) {
|
||||
slide.classList.add('active');
|
||||
} else if (index < slideIndex) {
|
||||
slide.classList.add('prev');
|
||||
} else {
|
||||
slide.classList.add('next');
|
||||
}
|
||||
});
|
||||
|
||||
// Update indicators
|
||||
const indicators = document.querySelectorAll('.beatport-hype-picks-indicator');
|
||||
indicators.forEach((indicator, index) => {
|
||||
indicator.classList.toggle('active', index === slideIndex);
|
||||
});
|
||||
|
||||
console.log('Slide updated to:', beatportHypePicksSliderState.currentSlide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start auto-play for hype picks slider
|
||||
*/
|
||||
function startBeatportHypePicksSliderAutoPlay() {
|
||||
if (beatportHypePicksSliderState.autoPlayInterval) {
|
||||
clearInterval(beatportHypePicksSliderState.autoPlayInterval);
|
||||
}
|
||||
|
||||
beatportHypePicksSliderState.autoPlayInterval = setInterval(() => {
|
||||
goToBeatportHypePicksSlide(beatportHypePicksSliderState.currentSlide + 1);
|
||||
}, beatportHypePicksSliderState.autoPlayDelay);
|
||||
|
||||
console.log('🔥 Hype picks slider autoplay started');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset auto-play for hype picks slider
|
||||
*/
|
||||
function resetBeatportHypePicksSliderAutoPlay() {
|
||||
startBeatportHypePicksSliderAutoPlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup hover pause for hype picks slider
|
||||
*/
|
||||
function setupBeatportHypePicksSliderHoverPause() {
|
||||
const sliderContainer = document.querySelector('.beatport-hype-picks-slider-container');
|
||||
if (sliderContainer) {
|
||||
sliderContainer.addEventListener('mouseenter', () => {
|
||||
if (beatportHypePicksSliderState.autoPlayInterval) {
|
||||
clearInterval(beatportHypePicksSliderState.autoPlayInterval);
|
||||
}
|
||||
});
|
||||
|
||||
sliderContainer.addEventListener('mouseleave', () => {
|
||||
startBeatportHypePicksSliderAutoPlay();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup click handlers for hype pick cards
|
||||
*/
|
||||
function setupBeatportHypePickCardHandlers() {
|
||||
const cards = document.querySelectorAll('.beatport-hype-pick-card:not(.beatport-hype-pick-placeholder)');
|
||||
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
const releaseUrl = card.dataset.releaseUrl;
|
||||
|
||||
if (releaseUrl) {
|
||||
console.log('🔥 Clicked hype pick release:', releaseUrl);
|
||||
// You can add release handling logic here
|
||||
// For now, just log the click
|
||||
showToast('🔥 Hype pick release clicked!', 'info');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error state for hype picks slider
|
||||
*/
|
||||
function showBeatportHypePicksError(errorMessage) {
|
||||
const sliderTrack = document.getElementById('beatport-hype-picks-slider-track');
|
||||
if (sliderTrack) {
|
||||
sliderTrack.innerHTML = `
|
||||
<div class="beatport-hype-picks-loading">
|
||||
<div class="beatport-hype-picks-loading-content">
|
||||
<h3>❌ Error Loading Hype Picks</h3>
|
||||
<p>${errorMessage}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up hype picks slider when switching away
|
||||
*/
|
||||
function cleanupBeatportHypePicksSlider() {
|
||||
if (beatportHypePicksSliderState.autoPlayInterval) {
|
||||
clearInterval(beatportHypePicksSliderState.autoPlayInterval);
|
||||
beatportHypePicksSliderState.autoPlayInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// BEATPORT FEATURED CHARTS SLIDER
|
||||
// ===================================
|
||||
|
|
|
|||
|
|
@ -12421,6 +12421,397 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
/* ==================== HYPE PICKS SLIDER STYLES ==================== */
|
||||
|
||||
.beatport-hype-picks-section {
|
||||
margin-top: 40px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-title {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
background: linear-gradient(135deg, #ff6b6b, #ff8787, #ffa8a8);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-subtitle {
|
||||
font-size: 16px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slider-container {
|
||||
position: relative;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(18, 18, 18, 0.95),
|
||||
rgba(30, 30, 30, 0.9));
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.5),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slider {
|
||||
position: relative;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slider-track {
|
||||
position: relative;
|
||||
height: 425px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 400px;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 107, 107, 0.1),
|
||||
rgba(255, 135, 135, 0.05));
|
||||
border-radius: 16px;
|
||||
border: 2px dashed rgba(255, 107, 107, 0.3);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-loading-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-loading-content h3 {
|
||||
font-size: 24px;
|
||||
color: #ff6b6b;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-loading-content p {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Individual Slide (contains 10 cards in 5x2 grid) */
|
||||
.beatport-hype-picks-slide {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transform: translateX(100px);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slide.active {
|
||||
opacity: 1;
|
||||
z-index: 10;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slide.prev {
|
||||
transform: translateX(-100px);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slide.next {
|
||||
transform: translateX(100px);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(16, 16, 16, 0.6),
|
||||
rgba(24, 24, 24, 0.4));
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Hype Pick Card Styling */
|
||||
.beatport-hype-pick-card {
|
||||
background: linear-gradient(145deg,
|
||||
rgba(40, 40, 40, 0.9),
|
||||
rgba(28, 28, 28, 0.95));
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow:
|
||||
0 4px 16px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
width: 100%;
|
||||
height: 160px;
|
||||
min-height: 160px;
|
||||
max-height: 160px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--card-bg-image, none);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
opacity: 0.15;
|
||||
border-radius: 12px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-card:hover {
|
||||
transform: translateY(-2px) scale(1.05);
|
||||
background: linear-gradient(145deg,
|
||||
rgba(255, 107, 107, 0.15),
|
||||
rgba(40, 40, 40, 0.95));
|
||||
border-color: rgba(255, 107, 107, 0.4);
|
||||
box-shadow:
|
||||
0 8px 24px rgba(0, 0, 0, 0.4),
|
||||
0 0 16px rgba(255, 107, 107, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-card-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-artwork {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 8px;
|
||||
margin: 0 auto 12px auto;
|
||||
background: linear-gradient(135deg, #333, #555);
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-artwork img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-card:hover .beatport-hype-pick-artwork img {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.beatport-hype-pick-info {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-artist {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-label {
|
||||
font-size: 10px;
|
||||
color: rgba(255, 107, 107, 0.8);
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Placeholder Card Styling */
|
||||
.beatport-hype-pick-placeholder {
|
||||
opacity: 0.3;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-placeholder .placeholder-icon {
|
||||
font-size: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.beatport-hype-pick-placeholder:hover {
|
||||
transform: none;
|
||||
background: linear-gradient(145deg,
|
||||
rgba(40, 40, 40, 0.9),
|
||||
rgba(28, 28, 28, 0.95));
|
||||
}
|
||||
|
||||
/* Navigation Buttons */
|
||||
.beatport-hype-picks-slider-nav {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-nav-btn {
|
||||
pointer-events: all;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 107, 107, 0.9),
|
||||
rgba(255, 135, 135, 0.8));
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-nav-btn:hover {
|
||||
transform: scale(1.1);
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 107, 107, 1),
|
||||
rgba(255, 135, 135, 0.9));
|
||||
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.6);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-prev-btn {
|
||||
margin-left: -25px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-next-btn {
|
||||
margin-right: -25px;
|
||||
}
|
||||
|
||||
/* Slider Indicators */
|
||||
.beatport-hype-picks-slider-indicators {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-indicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-indicator.active {
|
||||
background: linear-gradient(135deg, #ff6b6b, #ff8787);
|
||||
border-color: rgba(255, 107, 107, 0.6);
|
||||
box-shadow: 0 0 15px rgba(255, 107, 107, 0.5);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.beatport-hype-picks-indicator:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1400px) {
|
||||
.beatport-hype-picks-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.beatport-hype-picks-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.beatport-hype-pick-artwork {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.beatport-hype-picks-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-slider-container {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.beatport-hype-picks-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-section {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.beatport-hype-picks-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== FEATURED CHARTS SLIDER STYLES ==================== */
|
||||
|
||||
/* Charts Section */
|
||||
|
|
|
|||
Loading…
Reference in a new issue