artist design

pull/15/head
Broque Thomas 8 months ago
parent c29c5d6495
commit cff48b2498

@ -1596,8 +1596,9 @@ def get_artist_discography(artist_id):
print(f"🎤 Fetching discography for artist: {artist_id}")
# Get all albums and singles for the artist
albums = spotify_client.get_artist_albums(artist_id, album_type='album,single,appears_on', limit=50)
# Get artist's albums and singles (temporarily include appears_on for debugging)
albums = spotify_client.get_artist_albums(artist_id, album_type='album,single', limit=50)
print(f"📊 Raw albums returned from Spotify: {len(albums)}")
if not albums:
return jsonify({
@ -1618,13 +1619,22 @@ def get_artist_discography(artist_id):
continue
seen_albums.add(album.id)
# Skip compilations and appears_on that aren't the main artist's work
if hasattr(album, 'album_type') and album.album_type == 'appears_on':
# Only include if the artist is the main artist
if hasattr(album, 'artists') and album.artists:
main_artist_id = album.artists[0].id if hasattr(album.artists[0], 'id') else None
if main_artist_id != artist_id:
continue
# Debug: Check artist information
print(f"🔍 Checking album: {album.name}")
if hasattr(album, 'artists') and album.artists:
primary_artist_id = album.artists[0].id if hasattr(album.artists[0], 'id') else None
primary_artist_name = album.artists[0].name if hasattr(album.artists[0], 'name') else None
print(f" Primary artist: {primary_artist_name} (ID: {primary_artist_id})")
print(f" Requested artist ID: {artist_id}")
# Skip if the primary artist doesn't match our requested artist
if primary_artist_id and primary_artist_id != artist_id:
print(f"🚫 Skipping '{album.name}' - primary artist mismatch")
continue
elif not primary_artist_id:
print(f"⚠️ No primary artist ID found for '{album.name}' - including anyway")
else:
print(f"⚠️ No artists found for '{album.name}' - including anyway")
album_data = {
"id": album.id,
@ -1636,11 +1646,15 @@ def get_artist_discography(artist_id):
"external_urls": album.external_urls if hasattr(album, 'external_urls') else {}
}
# Skip obvious compilation issues but be more lenient for now
if hasattr(album, 'album_type') and album.album_type == 'compilation':
print(f"📀 Found compilation: '{album.name}' - including for now")
# Categorize by album type
if hasattr(album, 'album_type'):
if album.album_type in ['single', 'ep']:
singles_list.append(album_data)
else: # 'album' or 'compilation'
else: # 'album' or approved 'compilation'
album_list.append(album_data)
else:
# Default to album if no type specified
@ -1659,7 +1673,13 @@ def get_artist_discography(artist_id):
album_list.sort(key=get_release_year, reverse=True)
singles_list.sort(key=get_release_year, reverse=True)
print(f"✅ Found {len(album_list)} albums and {len(singles_list)} singles for artist")
print(f"✅ Found {len(album_list)} albums and {len(singles_list)} singles for artist {artist_id}")
# Debug: Log the final album list
for album in album_list:
print(f"📀 Album: {album['name']} ({album['album_type']}) - {album['release_date']}")
for single in singles_list:
print(f"🎵 Single/EP: {single['name']} ({single['album_type']}) - {single['release_date']}")
return jsonify({
"albums": album_list,

@ -59,7 +59,8 @@ let artistsPageState = {
},
cache: {
searches: {}, // Cache search results by query
discography: {} // Cache discography by artist ID
discography: {}, // Cache discography by artist ID
colors: {} // Cache extracted colors by image URL
}
};
let artistsSearchTimeout = null;
@ -9293,6 +9294,14 @@ function displayArtistsResults(query, results) {
// Add event listeners to cards
container.querySelectorAll('.artist-card').forEach((card, index) => {
card.addEventListener('click', () => selectArtist(results[index]));
// Extract colors from artist image for dynamic glow
const artist = results[index];
if (artist.image_url) {
extractImageColors(artist.image_url, (colors) => {
applyDynamicGlow(card, colors);
});
}
});
// Add mouse wheel horizontal scrolling
@ -9421,6 +9430,16 @@ function displayArtistDiscography(discography) {
if (albumsContainer) {
if (discography.albums?.length > 0) {
albumsContainer.innerHTML = discography.albums.map(album => createAlbumCard(album)).join('');
// Add dynamic glow effects to album cards
albumsContainer.querySelectorAll('.album-card').forEach((card, index) => {
const album = discography.albums[index];
if (album.image_url) {
extractImageColors(album.image_url, (colors) => {
applyDynamicGlow(card, colors);
});
}
});
} else {
albumsContainer.innerHTML = `
<div style="grid-column: 1 / -1; text-align: center; padding: 40px 20px; color: rgba(255, 255, 255, 0.6);">
@ -9436,6 +9455,16 @@ function displayArtistDiscography(discography) {
if (singlesContainer) {
if (discography.singles?.length > 0) {
singlesContainer.innerHTML = discography.singles.map(single => createAlbumCard(single)).join('');
// Add dynamic glow effects to singles cards
singlesContainer.querySelectorAll('.album-card').forEach((card, index) => {
const single = discography.singles[index];
if (single.image_url) {
extractImageColors(single.image_url, (colors) => {
applyDynamicGlow(card, colors);
});
}
});
} else {
singlesContainer.innerHTML = `
<div style="grid-column: 1 / -1; text-align: center; padding: 40px 20px; color: rgba(255, 255, 255, 0.6);">
@ -9717,6 +9746,184 @@ function showSearchLoadingCards() {
container.innerHTML = loadingCardHtml.repeat(6);
}
/**
* Extract dominant colors from an image for dynamic glow effects
*/
async function extractImageColors(imageUrl, callback) {
if (!imageUrl) {
callback(['#1db954', '#1ed760']); // Fallback to Spotify green
return;
}
// Check cache first for performance
if (artistsPageState.cache.colors[imageUrl]) {
callback(artistsPageState.cache.colors[imageUrl]);
return;
}
try {
// Create a canvas to analyze the image
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function() {
// Resize to small dimensions for faster processing
const size = 50;
canvas.width = size;
canvas.height = size;
// Draw image to canvas
ctx.drawImage(img, 0, 0, size, size);
try {
// Get image data
const imageData = ctx.getImageData(0, 0, size, size);
const data = imageData.data;
// Extract colors (sample every few pixels for performance)
const colors = [];
for (let i = 0; i < data.length; i += 16) { // Sample every 4th pixel
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const alpha = data[i + 3];
// Skip transparent or very dark pixels
if (alpha > 128 && (r + g + b) > 150) {
colors.push({ r, g, b });
}
}
if (colors.length === 0) {
callback(['#1db954', '#1ed760']); // Fallback
return;
}
// Find dominant colors using a simple clustering approach
const dominantColors = findDominantColors(colors, 2);
// Convert to CSS hex colors
const hexColors = dominantColors.map(color =>
`#${((1 << 24) + (color.r << 16) + (color.g << 8) + color.b).toString(16).slice(1)}`
);
// Cache the colors for future use
artistsPageState.cache.colors[imageUrl] = hexColors;
callback(hexColors);
} catch (e) {
console.warn('Color extraction failed, using fallback colors:', e);
callback(['#1db954', '#1ed760']);
}
};
img.onerror = function() {
callback(['#1db954', '#1ed760']); // Fallback on error
};
img.src = imageUrl;
} catch (error) {
console.warn('Image color extraction error:', error);
callback(['#1db954', '#1ed760']);
}
}
/**
* Simple color clustering to find dominant colors
*/
function findDominantColors(colors, numColors = 2) {
if (colors.length === 0) return [{ r: 29, g: 185, b: 84 }];
// Simple k-means clustering
let centroids = [];
// Initialize centroids randomly
for (let i = 0; i < numColors; i++) {
centroids.push(colors[Math.floor(Math.random() * colors.length)]);
}
// Run a few iterations of k-means
for (let iteration = 0; iteration < 5; iteration++) {
const clusters = Array(numColors).fill().map(() => []);
// Assign each color to nearest centroid
colors.forEach(color => {
let minDistance = Infinity;
let nearestCluster = 0;
centroids.forEach((centroid, i) => {
const distance = Math.sqrt(
Math.pow(color.r - centroid.r, 2) +
Math.pow(color.g - centroid.g, 2) +
Math.pow(color.b - centroid.b, 2)
);
if (distance < minDistance) {
minDistance = distance;
nearestCluster = i;
}
});
clusters[nearestCluster].push(color);
});
// Update centroids
centroids = clusters.map(cluster => {
if (cluster.length === 0) return centroids[0]; // Fallback
const avgR = cluster.reduce((sum, c) => sum + c.r, 0) / cluster.length;
const avgG = cluster.reduce((sum, c) => sum + c.g, 0) / cluster.length;
const avgB = cluster.reduce((sum, c) => sum + c.b, 0) / cluster.length;
return { r: Math.round(avgR), g: Math.round(avgG), b: Math.round(avgB) };
});
}
// Ensure we have vibrant colors by boosting saturation
return centroids.map(color => {
const max = Math.max(color.r, color.g, color.b);
const min = Math.min(color.r, color.g, color.b);
const saturation = max === 0 ? 0 : (max - min) / max;
// Boost low saturation colors
if (saturation < 0.4) {
const factor = 1.3;
return {
r: Math.min(255, Math.round(color.r * factor)),
g: Math.min(255, Math.round(color.g * factor)),
b: Math.min(255, Math.round(color.b * factor))
};
}
return color;
});
}
/**
* Apply dynamic glow effect to a card element
*/
function applyDynamicGlow(cardElement, colors) {
if (!cardElement || colors.length < 2) return;
const color1 = colors[0];
const color2 = colors[1];
// Add a small delay to make the effect feel more natural
setTimeout(() => {
// Create CSS custom properties for the dynamic colors
cardElement.style.setProperty('--glow-color-1', color1);
cardElement.style.setProperty('--glow-color-2', color2);
cardElement.classList.add('has-dynamic-glow');
console.log(`🎨 Applied dynamic glow: ${color1}, ${color2}`);
}, Math.random() * 200 + 100); // Random delay between 100-300ms
}
/**
* Utility function to escape HTML
*/

@ -5861,7 +5861,7 @@ body {
gap: 20px;
overflow-x: auto;
overflow-y: visible;
padding: 20px;
padding: 30px;
scroll-behavior: smooth;
/* Custom scrollbar styling */
@ -5914,14 +5914,27 @@ body {
}
.artist-card:hover {
transform: translateY(-8px) scale(1.02);
z-index: 10;
box-shadow:
0 20px 50px rgba(0, 0, 0, 0.6),
0 0 0 1px rgba(29, 185, 84, 0.15),
0 0 30px rgba(29, 185, 84, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
transform: translateY(-5px) scale(1.01);
z-index: 10;
box-shadow:
0 12px 15px rgba(0, 0, 0, 0.4),
0 0 0 1px rgba(29, 185, 84, 0.15),
0 0 20px rgba(29, 185, 84, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* Dynamic glow effects based on image colors */
.artist-card.has-dynamic-glow {
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1),
filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.artist-card.has-dynamic-glow:hover {
filter: drop-shadow(0 0 8px var(--glow-color-1, #1db954))
drop-shadow(0 0 16px var(--glow-color-2, #1ed760));
border-color: var(--glow-color-1, rgba(29, 185, 84, 0.3));
}
.artist-card-background {
@ -6259,6 +6272,19 @@ body {
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* Dynamic glow effects for album cards */
.album-card.has-dynamic-glow {
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1),
filter 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.album-card.has-dynamic-glow:hover {
filter: drop-shadow(0 0 6px var(--glow-color-1, #1db954))
drop-shadow(0 0 12px var(--glow-color-2, #1ed760));
border-color: var(--glow-color-1, rgba(29, 185, 84, 0.3));
}
.album-card-image {
width: 100%;
aspect-ratio: 1;

Loading…
Cancel
Save