beatport progress
This commit is contained in:
parent
4f33ac82ba
commit
5e73b53a0f
5 changed files with 788 additions and 0 deletions
|
|
@ -1284,6 +1284,125 @@ class BeatportUnifiedScraper:
|
|||
print(f" ❌ Error converting track data: {e}")
|
||||
return None
|
||||
|
||||
def scrape_homepage_top10_lists(self) -> Dict[str, List[Dict]]:
|
||||
"""Scrape Top 10 Lists from homepage - Beatport Top 10 and Hype Top 10"""
|
||||
print("\n🏆 Scraping Top 10 Lists from homepage...")
|
||||
|
||||
soup = self.get_page(self.base_url)
|
||||
if not soup:
|
||||
return {"beatport_top10": [], "hype_top10": []}
|
||||
|
||||
# Extract Beatport Top 10 tracks
|
||||
beatport_top10_items = soup.select('[data-testid="top-10-item"]')
|
||||
print(f" Found {len(beatport_top10_items)} Beatport Top 10 items")
|
||||
|
||||
beatport_tracks = []
|
||||
for i, item in enumerate(beatport_top10_items, 1):
|
||||
try:
|
||||
track_data = self.extract_track_from_top10_item(item, i, "Beatport Top 10")
|
||||
if track_data:
|
||||
beatport_tracks.append(track_data)
|
||||
except Exception as e:
|
||||
print(f" ❌ Error extracting Beatport track {i}: {e}")
|
||||
|
||||
# Extract Hype Top 10 tracks
|
||||
hype_top10_items = soup.select('[data-testid="hype-top-10-item"]')
|
||||
print(f" Found {len(hype_top10_items)} Hype Top 10 items")
|
||||
|
||||
hype_tracks = []
|
||||
for i, item in enumerate(hype_top10_items, 1):
|
||||
try:
|
||||
track_data = self.extract_track_from_top10_item(item, i, "Hype Top 10")
|
||||
if track_data:
|
||||
hype_tracks.append(track_data)
|
||||
except Exception as e:
|
||||
print(f" ❌ Error extracting Hype track {i}: {e}")
|
||||
|
||||
print(f"✅ Extracted {len(beatport_tracks)} Beatport Top 10 + {len(hype_tracks)} Hype Top 10 tracks")
|
||||
|
||||
return {
|
||||
"beatport_top10": beatport_tracks,
|
||||
"hype_top10": hype_tracks
|
||||
}
|
||||
|
||||
def extract_track_from_top10_item(self, item, rank, list_name):
|
||||
"""Extract track data from a top 10 list item"""
|
||||
try:
|
||||
# Get the track URL
|
||||
link_elem = item.select_one('a[href*="/track/"]')
|
||||
track_url = ""
|
||||
if link_elem and link_elem.get('href'):
|
||||
track_url = f"https://www.beatport.com{link_elem.get('href')}"
|
||||
|
||||
# Extract track title
|
||||
title = "Unknown Title"
|
||||
title_selectors = [
|
||||
'[class*="ItemName"]',
|
||||
'[class*="TrackName"]',
|
||||
'[class*="track-name"]',
|
||||
'a[href*="/track/"]'
|
||||
]
|
||||
|
||||
for selector in title_selectors:
|
||||
title_elem = item.select_one(selector)
|
||||
if title_elem:
|
||||
title = title_elem.get_text(strip=True)
|
||||
if title and title != "Unknown Title":
|
||||
break
|
||||
|
||||
# Extract artist name
|
||||
artist = "Unknown Artist"
|
||||
artist_selectors = [
|
||||
'[class*="Artists"]',
|
||||
'[class*="artist"]',
|
||||
'[class*="Artist"]',
|
||||
'[class*="ItemArtist"]',
|
||||
'a[href*="/artist/"]'
|
||||
]
|
||||
|
||||
for selector in artist_selectors:
|
||||
artist_elem = item.select_one(selector)
|
||||
if artist_elem:
|
||||
artist = artist_elem.get_text(strip=True)
|
||||
if artist and artist != "Unknown Artist":
|
||||
break
|
||||
|
||||
# Extract label name
|
||||
label = "Unknown Label"
|
||||
label_selectors = [
|
||||
'[class*="Label"]',
|
||||
'[class*="label"]',
|
||||
'[class*="ItemLabel"]',
|
||||
'a[href*="/label/"]'
|
||||
]
|
||||
|
||||
for selector in label_selectors:
|
||||
label_elem = item.select_one(selector)
|
||||
if label_elem:
|
||||
label = label_elem.get_text(strip=True)
|
||||
if label and label != "Unknown Label":
|
||||
break
|
||||
|
||||
# Extract artwork if available
|
||||
artwork_url = ""
|
||||
img_elem = item.select_one('img')
|
||||
if img_elem and img_elem.get('src'):
|
||||
artwork_url = img_elem.get('src')
|
||||
|
||||
return {
|
||||
"rank": rank,
|
||||
"title": title,
|
||||
"artist": artist,
|
||||
"label": label,
|
||||
"url": track_url,
|
||||
"artwork_url": artwork_url,
|
||||
"list_name": list_name
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error extracting track data: {e}")
|
||||
return None
|
||||
|
||||
def scrape_new_on_beatport_hero(self, limit: int = 10) -> List[Dict]:
|
||||
"""Scrape the 'New on Beatport' hero slideshow from homepage"""
|
||||
print("\n🎯 Scraping 'New on Beatport' hero slideshow...")
|
||||
|
|
|
|||
|
|
@ -13228,6 +13228,40 @@ def get_beatport_homepage_top_10_releases():
|
|||
"track_count": 0
|
||||
}), 500
|
||||
|
||||
@app.route('/api/beatport/homepage/top-10-lists', methods=['GET'])
|
||||
def get_beatport_homepage_top10_lists():
|
||||
"""Get Beatport Top 10 Lists from homepage - both Beatport Top 10 and Hype Top 10"""
|
||||
try:
|
||||
logger.info("🏆 API request for Beatport homepage Top 10 Lists")
|
||||
|
||||
# Initialize the Beatport scraper
|
||||
scraper = BeatportUnifiedScraper()
|
||||
|
||||
# Get top 10 lists from homepage
|
||||
top10_lists = scraper.scrape_homepage_top10_lists()
|
||||
|
||||
logger.info(f"✅ Successfully extracted Beatport Top 10: {len(top10_lists['beatport_top10'])}, Hype Top 10: {len(top10_lists['hype_top10'])}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"beatport_top10": top10_lists["beatport_top10"],
|
||||
"hype_top10": top10_lists["hype_top10"],
|
||||
"beatport_count": len(top10_lists["beatport_top10"]),
|
||||
"hype_count": len(top10_lists["hype_top10"]),
|
||||
"source": "beatport_homepage_top10_lists"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Error getting Beatport homepage top 10 lists: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"beatport_top10": [],
|
||||
"hype_top10": [],
|
||||
"beatport_count": 0,
|
||||
"hype_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"""
|
||||
|
|
|
|||
|
|
@ -806,6 +806,50 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top 10 Lists Section -->
|
||||
<div class="beatport-top10-section">
|
||||
<div class="beatport-top10-header">
|
||||
<h2 class="beatport-top10-title">🏆 Top 10 Lists</h2>
|
||||
<p class="beatport-top10-subtitle">Current trending tracks from Beatport charts</p>
|
||||
</div>
|
||||
|
||||
<div class="beatport-top10-container">
|
||||
<!-- Beatport Top 10 List -->
|
||||
<div class="beatport-top10-list" id="beatport-top10-list">
|
||||
<div class="beatport-top10-list-header">
|
||||
<h3 class="beatport-top10-list-title">🎵 Beatport Top 10</h3>
|
||||
<p class="beatport-top10-list-subtitle">Most popular tracks on Beatport</p>
|
||||
</div>
|
||||
<div class="beatport-top10-tracks" id="beatport-top10-tracks">
|
||||
<!-- Loading placeholder -->
|
||||
<div class="beatport-top10-loading">
|
||||
<div class="beatport-top10-loading-content">
|
||||
<h4>🎵 Loading Beatport Top 10...</h4>
|
||||
<p>Fetching trending tracks</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hype Top 10 List -->
|
||||
<div class="beatport-hype10-list" id="beatport-hype10-list">
|
||||
<div class="beatport-hype10-list-header">
|
||||
<h3 class="beatport-hype10-list-title">🔥 Hype Top 10</h3>
|
||||
<p class="beatport-hype10-list-subtitle">Editor's hottest trending picks</p>
|
||||
</div>
|
||||
<div class="beatport-hype10-tracks" id="beatport-hype10-tracks">
|
||||
<!-- Loading placeholder -->
|
||||
<div class="beatport-hype10-loading">
|
||||
<div class="beatport-hype10-loading-content">
|
||||
<h4>🔥 Loading Hype Top 10...</h4>
|
||||
<p>Fetching editor's picks</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New Releases Grid Slideshow Section -->
|
||||
<div class="beatport-releases-section">
|
||||
<div class="beatport-releases-header">
|
||||
|
|
|
|||
|
|
@ -9734,6 +9734,7 @@ function initializeSyncPage() {
|
|||
// Initialize rebuild slider if rebuild tab is selected
|
||||
if (tabId === 'rebuild') {
|
||||
initializeBeatportRebuildSlider();
|
||||
loadBeatportTop10Lists();
|
||||
initializeBeatportReleasesSlider();
|
||||
initializeBeatportHypePicksSlider();
|
||||
initializeBeatportChartsSlider();
|
||||
|
|
@ -19719,3 +19720,127 @@ function cleanupBeatportDJSlider() {
|
|||
beatportDJSliderState.autoPlayInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load top 10 lists data from API and populate both lists
|
||||
*/
|
||||
async function loadBeatportTop10Lists() {
|
||||
try {
|
||||
console.log('🏆 Loading top 10 lists data...');
|
||||
const response = await fetch('/api/beatport/homepage/top-10-lists');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
console.log(`🎵 Loaded ${data.beatport_count} Beatport Top 10 + ${data.hype_count} Hype Top 10 tracks`);
|
||||
|
||||
// Populate both lists
|
||||
populateBeatportTop10List(data.beatport_top10);
|
||||
populateHypeTop10List(data.hype_top10);
|
||||
return true;
|
||||
} else {
|
||||
console.error('Failed to load top 10 lists:', data.error);
|
||||
showTop10ListsError(data.error || 'No data available');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading top 10 lists:', error);
|
||||
showTop10ListsError('Failed to load top 10 lists');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate Beatport Top 10 list with data
|
||||
*/
|
||||
function populateBeatportTop10List(tracks) {
|
||||
const container = document.getElementById('beatport-top10-list');
|
||||
if (!container || !tracks || tracks.length === 0) return;
|
||||
|
||||
// Generate HTML for the tracks
|
||||
let tracksHtml = `
|
||||
<div class="beatport-top10-list-header">
|
||||
<h3 class="beatport-top10-list-title">🎵 Beatport Top 10</h3>
|
||||
<p class="beatport-top10-list-subtitle">Most popular tracks on Beatport</p>
|
||||
</div>
|
||||
<div class="beatport-top10-tracks">
|
||||
`;
|
||||
|
||||
tracks.forEach((track, index) => {
|
||||
tracksHtml += `
|
||||
<div class="beatport-top10-card" data-url="${track.url || '#'}">
|
||||
<div class="beatport-top10-card-rank">${track.rank || index + 1}</div>
|
||||
<div class="beatport-top10-card-artwork">
|
||||
${track.artwork_url ?
|
||||
`<img src="${track.artwork_url}" alt="${track.title}" loading="lazy">` :
|
||||
'<div class="beatport-top10-card-placeholder">🎵</div>'
|
||||
}
|
||||
</div>
|
||||
<div class="beatport-top10-card-info">
|
||||
<h4 class="beatport-top10-card-title">${track.title || 'Unknown Title'}</h4>
|
||||
<p class="beatport-top10-card-artist">${track.artist || 'Unknown Artist'}</p>
|
||||
<p class="beatport-top10-card-label">${track.label || 'Unknown Label'}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
tracksHtml += '</div>';
|
||||
container.innerHTML = tracksHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate Hype Top 10 list with data
|
||||
*/
|
||||
function populateHypeTop10List(tracks) {
|
||||
const container = document.getElementById('beatport-hype10-list');
|
||||
if (!container || !tracks || tracks.length === 0) return;
|
||||
|
||||
// Generate HTML for the tracks
|
||||
let tracksHtml = `
|
||||
<div class="beatport-hype10-list-header">
|
||||
<h3 class="beatport-hype10-list-title">🔥 Hype Top 10</h3>
|
||||
<p class="beatport-hype10-list-subtitle">Editor's trending picks</p>
|
||||
</div>
|
||||
<div class="beatport-hype10-tracks">
|
||||
`;
|
||||
|
||||
tracks.forEach((track, index) => {
|
||||
tracksHtml += `
|
||||
<div class="beatport-hype10-card" data-url="${track.url || '#'}">
|
||||
<div class="beatport-hype10-card-rank">${track.rank || index + 1}</div>
|
||||
<div class="beatport-hype10-card-artwork">
|
||||
${track.artwork_url ?
|
||||
`<img src="${track.artwork_url}" alt="${track.title}" loading="lazy">` :
|
||||
'<div class="beatport-hype10-card-placeholder">🔥</div>'
|
||||
}
|
||||
</div>
|
||||
<div class="beatport-hype10-card-info">
|
||||
<h4 class="beatport-hype10-card-title">${track.title || 'Unknown Title'}</h4>
|
||||
<p class="beatport-hype10-card-artist">${track.artist || 'Unknown Artist'}</p>
|
||||
<p class="beatport-hype10-card-label">${track.label || 'Unknown Label'}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
tracksHtml += '</div>';
|
||||
container.innerHTML = tracksHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message for top 10 lists
|
||||
*/
|
||||
function showTop10ListsError(errorMessage) {
|
||||
const beatportContainer = document.getElementById('beatport-top10-list');
|
||||
const hypeContainer = document.getElementById('beatport-hype10-list');
|
||||
|
||||
const errorHtml = `
|
||||
<div class="beatport-top10-error">
|
||||
<h3>❌ Error Loading Data</h3>
|
||||
<p>${errorMessage}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (beatportContainer) beatportContainer.innerHTML = errorHtml;
|
||||
if (hypeContainer) hypeContainer.innerHTML = errorHtml;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12040,6 +12040,472 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
/* =======================================
|
||||
BEATPORT TOP 10 LISTS SECTION
|
||||
======================================= */
|
||||
|
||||
.beatport-top10-section {
|
||||
margin-top: 40px;
|
||||
padding: 0 30px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.beatport-top10-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.beatport-top10-title {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.beatport-top10-subtitle {
|
||||
font-size: 16px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Two-column container for both lists */
|
||||
.beatport-top10-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 40px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden; /* Prevent overflow */
|
||||
}
|
||||
|
||||
/* Individual list styling */
|
||||
.beatport-top10-list, .beatport-hype10-list {
|
||||
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);
|
||||
width: 100%;
|
||||
min-width: 0; /* Allow shrinking */
|
||||
overflow: hidden; /* Prevent content overflow */
|
||||
}
|
||||
|
||||
/* Color-coded themes */
|
||||
.beatport-top10-list {
|
||||
border-left: 4px solid #00ff88;
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(0, 255, 136, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.beatport-hype10-list {
|
||||
border-left: 4px solid #ff3366;
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(255, 51, 102, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* List headers */
|
||||
.beatport-top10-list-header, .beatport-hype10-list-header {
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.beatport-top10-list-title, .beatport-hype10-list-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 5px;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.beatport-top10-list-subtitle, .beatport-hype10-list-subtitle {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Track containers */
|
||||
.beatport-top10-tracks, .beatport-hype10-tracks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Individual track cards - CORRECTED CLASS NAMES */
|
||||
.beatport-top10-card, .beatport-hype10-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(25, 25, 25, 0.9),
|
||||
rgba(35, 35, 35, 0.8));
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(10px);
|
||||
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-width: 0; /* Allow shrinking */
|
||||
box-sizing: border-box;
|
||||
min-height: 80px; /* Consistent height */
|
||||
max-height: 80px; /* Prevent expansion */
|
||||
}
|
||||
|
||||
/* Hover animations for cards */
|
||||
.beatport-top10-card:hover {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 255, 136, 0.12),
|
||||
rgba(35, 35, 35, 0.95));
|
||||
border-color: rgba(0, 255, 136, 0.3);
|
||||
transform: translateY(-3px);
|
||||
box-shadow:
|
||||
0 12px 32px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.beatport-hype10-card:hover {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 51, 102, 0.12),
|
||||
rgba(35, 35, 35, 0.95));
|
||||
border-color: rgba(255, 51, 102, 0.3);
|
||||
transform: translateY(-3px);
|
||||
box-shadow:
|
||||
0 12px 32px rgba(0, 0, 0, 0.5),
|
||||
0 0 20px rgba(255, 51, 102, 0.2);
|
||||
}
|
||||
|
||||
/* Track rank number */
|
||||
.beatport-top10-card-rank {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: #00ff88;
|
||||
min-width: 35px;
|
||||
text-align: center;
|
||||
margin-right: 16px;
|
||||
text-shadow: 0 2px 6px rgba(0, 255, 136, 0.3);
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.1), rgba(0, 255, 136, 0.05));
|
||||
border-radius: 12px;
|
||||
padding: 8px 0;
|
||||
border: 1px solid rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.beatport-hype10-card-rank {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: #ff3366;
|
||||
min-width: 35px;
|
||||
text-align: center;
|
||||
margin-right: 16px;
|
||||
text-shadow: 0 2px 6px rgba(255, 51, 102, 0.3);
|
||||
background: linear-gradient(135deg, rgba(255, 51, 102, 0.1), rgba(255, 51, 102, 0.05));
|
||||
border-radius: 12px;
|
||||
padding: 8px 0;
|
||||
border: 1px solid rgba(255, 51, 102, 0.2);
|
||||
}
|
||||
|
||||
/* Track artwork */
|
||||
.beatport-top10-card-artwork, .beatport-hype10-card-artwork {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 12px;
|
||||
margin-right: 16px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.beatport-top10-card-artwork img, .beatport-hype10-card-artwork img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
.beatport-top10-card:hover .beatport-top10-card-artwork img,
|
||||
.beatport-hype10-card:hover .beatport-hype10-card-artwork img {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
/* Artwork placeholder */
|
||||
.beatport-top10-card-placeholder, .beatport-hype10-card-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
background: linear-gradient(135deg, rgba(40, 40, 40, 0.8), rgba(60, 60, 60, 0.6));
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* Track info */
|
||||
.beatport-top10-card-info, .beatport-hype10-card-info {
|
||||
flex: 1;
|
||||
min-width: 0; /* Critical for text truncation */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
overflow: hidden; /* Prevent text overflow */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.beatport-top10-card-title, .beatport-hype10-card-title {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
line-height: 1.2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.beatport-top10-card-artist, .beatport-hype10-card-artist {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.beatport-top10-card-label {
|
||||
font-size: 11px;
|
||||
color: rgba(0, 255, 136, 0.9);
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.beatport-hype10-card-label {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 51, 102, 0.9);
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Error states */
|
||||
.beatport-top10-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 51, 51, 0.1),
|
||||
rgba(40, 40, 40, 0.8));
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 51, 51, 0.2);
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.beatport-top10-error h3 {
|
||||
color: #ff3366;
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.beatport-top10-error p {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Loading states */
|
||||
.beatport-top10-loading, .beatport-hype10-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 300px;
|
||||
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.05);
|
||||
}
|
||||
|
||||
.beatport-top10-loading-content, .beatport-hype10-loading-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.beatport-top10-loading-content h4, .beatport-hype10-loading-content h4 {
|
||||
font-size: 18px;
|
||||
color: #ffffff;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.beatport-top10-loading-content p, .beatport-hype10-loading-content p {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Top 10 Lists Responsive Design */
|
||||
@media (max-width: 1300px) {
|
||||
.beatport-top10-card-title, .beatport-hype10-card-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-artist, .beatport-hype10-card-artist {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-label, .beatport-hype10-card-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-rank, .beatport-hype10-card-rank {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.beatport-top10-container {
|
||||
gap: 30px;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.beatport-top10-list, .beatport-hype10-list {
|
||||
padding: 25px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.beatport-top10-card, .beatport-hype10-card {
|
||||
min-height: 75px;
|
||||
max-height: 75px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-artwork, .beatport-hype10-card-artwork {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.beatport-top10-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.beatport-top10-section {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.beatport-top10-title {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.beatport-top10-list-title, .beatport-hype10-list-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.beatport-top10-card, .beatport-hype10-card {
|
||||
padding: 12px;
|
||||
min-height: 70px;
|
||||
max-height: 70px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-artwork, .beatport-hype10-card-artwork {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-rank, .beatport-hype10-card-rank {
|
||||
font-size: 16px;
|
||||
min-width: 30px;
|
||||
margin-right: 12px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.beatport-top10-section {
|
||||
padding: 0 15px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.beatport-top10-header {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.beatport-top10-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.beatport-top10-list, .beatport-hype10-list {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.beatport-top10-list-title, .beatport-hype10-list-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.beatport-top10-card, .beatport-hype10-card {
|
||||
padding: 10px;
|
||||
min-height: 65px;
|
||||
max-height: 65px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-artwork, .beatport-hype10-card-artwork {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-rank, .beatport-hype10-card-rank {
|
||||
font-size: 14px;
|
||||
min-width: 26px;
|
||||
padding: 4px 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-title, .beatport-hype10-card-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-artist, .beatport-hype10-card-artist {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.beatport-top10-card-label, .beatport-hype10-card-label {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================
|
||||
BEATPORT NEW RELEASES SECTION
|
||||
================================ */
|
||||
|
|
|
|||
Loading…
Reference in a new issue