add musicbrainz icon for items where matched
This commit is contained in:
parent
8cdf548561
commit
22eb62bb77
3 changed files with 238 additions and 6 deletions
|
|
@ -4033,13 +4033,14 @@ class MusicDatabase:
|
||||||
a.name,
|
a.name,
|
||||||
a.thumb_url,
|
a.thumb_url,
|
||||||
a.genres,
|
a.genres,
|
||||||
|
a.musicbrainz_id,
|
||||||
COUNT(DISTINCT al.id) as album_count,
|
COUNT(DISTINCT al.id) as album_count,
|
||||||
COUNT(DISTINCT t.id) as track_count
|
COUNT(DISTINCT t.id) as track_count
|
||||||
FROM artists a
|
FROM artists a
|
||||||
LEFT JOIN albums al ON a.id = al.artist_id
|
LEFT JOIN albums al ON a.id = al.artist_id
|
||||||
LEFT JOIN tracks t ON al.id = t.album_id
|
LEFT JOIN tracks t ON al.id = t.album_id
|
||||||
WHERE {where_clause}
|
WHERE {where_clause}
|
||||||
GROUP BY a.id, a.name, a.thumb_url, a.genres
|
GROUP BY a.id, a.name, a.thumb_url, a.genres, a.musicbrainz_id
|
||||||
ORDER BY a.name COLLATE NOCASE
|
ORDER BY a.name COLLATE NOCASE
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
"""
|
"""
|
||||||
|
|
@ -4076,6 +4077,7 @@ class MusicDatabase:
|
||||||
'name': artist.name,
|
'name': artist.name,
|
||||||
'image_url': artist.thumb_url,
|
'image_url': artist.thumb_url,
|
||||||
'genres': artist.genres,
|
'genres': artist.genres,
|
||||||
|
'musicbrainz_id': row['musicbrainz_id'],
|
||||||
'album_count': row['album_count'] or 0,
|
'album_count': row['album_count'] or 0,
|
||||||
'track_count': row['track_count'] or 0
|
'track_count': row['track_count'] or 0
|
||||||
}
|
}
|
||||||
|
|
@ -4130,7 +4132,7 @@ class MusicDatabase:
|
||||||
# Get artist information
|
# Get artist information
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT
|
SELECT
|
||||||
id, name, thumb_url, genres, server_source
|
id, name, thumb_url, genres, server_source, musicbrainz_id
|
||||||
FROM artists
|
FROM artists
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
""", (artist_id,))
|
""", (artist_id,))
|
||||||
|
|
@ -4173,6 +4175,7 @@ class MusicDatabase:
|
||||||
a.year,
|
a.year,
|
||||||
SUM(a.track_count) as track_count,
|
SUM(a.track_count) as track_count,
|
||||||
MAX(a.thumb_url) as thumb_url,
|
MAX(a.thumb_url) as thumb_url,
|
||||||
|
MAX(a.musicbrainz_release_id) as musicbrainz_release_id,
|
||||||
COUNT(t.id) as owned_tracks
|
COUNT(t.id) as owned_tracks
|
||||||
FROM albums a
|
FROM albums a
|
||||||
LEFT JOIN tracks t ON a.id = t.album_id
|
LEFT JOIN tracks t ON a.id = t.album_id
|
||||||
|
|
@ -4225,6 +4228,7 @@ class MusicDatabase:
|
||||||
'owned': True, # All albums in our DB are owned
|
'owned': True, # All albums in our DB are owned
|
||||||
'track_count': album_row['track_count'],
|
'track_count': album_row['track_count'],
|
||||||
'owned_tracks': owned_tracks,
|
'owned_tracks': owned_tracks,
|
||||||
|
'musicbrainz_release_id': album_row['musicbrainz_release_id'],
|
||||||
'track_completion': completion_percentage
|
'track_completion': completion_percentage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4265,6 +4269,7 @@ class MusicDatabase:
|
||||||
'image_url': artist_image_url,
|
'image_url': artist_image_url,
|
||||||
'genres': genres,
|
'genres': genres,
|
||||||
'server_source': artist_row['server_source'],
|
'server_source': artist_row['server_source'],
|
||||||
|
'musicbrainz_id': artist_row['musicbrainz_id'],
|
||||||
'album_count': album_count,
|
'album_count': album_count,
|
||||||
'track_count': track_count
|
'track_count': track_count
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -20011,8 +20011,20 @@ function createArtistCardHTML(artist) {
|
||||||
// Track if image needs to be lazy loaded
|
// Track if image needs to be lazy loaded
|
||||||
const needsImage = imageUrl ? 'false' : 'true';
|
const needsImage = imageUrl ? 'false' : 'true';
|
||||||
|
|
||||||
|
// Check for MusicBrainz ID
|
||||||
|
let mbIconHTML = '';
|
||||||
|
if (artist.musicbrainz_id) {
|
||||||
|
const mbLogoUrl = 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/MusicBrainz_Logo_%282016%29.svg/500px-MusicBrainz_Logo_%282016%29.svg.png'; // Use official SVG logo
|
||||||
|
mbIconHTML = `
|
||||||
|
<div class="mb-card-icon" title="View on MusicBrainz" onclick="event.stopPropagation(); window.open('https://musicbrainz.org/artist/${artist.musicbrainz_id}', '_blank')">
|
||||||
|
<img src="${mbLogoUrl}" style="width: 20px; height: auto; display: block;">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="artist-card" data-artist-id="${artist.id}" data-needs-image="${needsImage}">
|
<div class="artist-card" data-artist-id="${artist.id}" data-needs-image="${needsImage}">
|
||||||
|
${mbIconHTML}
|
||||||
<div class="artist-card-background" style="${backgroundStyle}"></div>
|
<div class="artist-card-background" style="${backgroundStyle}"></div>
|
||||||
<div class="artist-card-overlay"></div>
|
<div class="artist-card-overlay"></div>
|
||||||
<div class="artist-card-content">
|
<div class="artist-card-content">
|
||||||
|
|
@ -20122,6 +20134,17 @@ async function loadArtistDiscography(artistId, artistName = null) {
|
||||||
singles: data.singles || []
|
singles: data.singles || []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update selected artist with full details from backend (includes MusicBrainz ID)
|
||||||
|
if (data.artist) {
|
||||||
|
console.log('✨ Updating artist details with fresh data from backend');
|
||||||
|
artistsPageState.selectedArtist = {
|
||||||
|
...artistsPageState.selectedArtist,
|
||||||
|
...data.artist
|
||||||
|
};
|
||||||
|
// Refresh header to show MusicBrainz link
|
||||||
|
updateArtistDetailHeader(artistsPageState.selectedArtist);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`✅ Loaded ${discography.albums.length} albums and ${discography.singles.length} singles`);
|
console.log(`✅ Loaded ${discography.albums.length} albums and ${discography.singles.length} singles`);
|
||||||
|
|
||||||
// Cache the results
|
// Cache the results
|
||||||
|
|
@ -21120,6 +21143,35 @@ function updateArtistDetailHeader(artist) {
|
||||||
|
|
||||||
if (nameElement) {
|
if (nameElement) {
|
||||||
nameElement.textContent = artist.name;
|
nameElement.textContent = artist.name;
|
||||||
|
|
||||||
|
// DEBUG: Log the artist object to check for MBID
|
||||||
|
console.log(`🔍 [DEBUG] Updating header for ${artist.name}`, artist);
|
||||||
|
console.log(`🔍 [DEBUG] MBID present?`, artist.musicbrainz_id);
|
||||||
|
|
||||||
|
// Add MusicBrainz link if available
|
||||||
|
if (artist.musicbrainz_id) {
|
||||||
|
console.log('✅ [DEBUG] Adding MusicBrainz link to header');
|
||||||
|
// Remove existing MB link if any
|
||||||
|
const existingMb = nameElement.parentNode.querySelector('.mb-link-btn');
|
||||||
|
if (existingMb) existingMb.remove();
|
||||||
|
|
||||||
|
const mbLink = document.createElement('a');
|
||||||
|
mbLink.className = 'mb-link-btn';
|
||||||
|
mbLink.href = `https://musicbrainz.org/artist/${artist.musicbrainz_id}`;
|
||||||
|
mbLink.target = '_blank';
|
||||||
|
mbLink.title = 'View on MusicBrainz';
|
||||||
|
mbLink.innerHTML = `
|
||||||
|
<img src="https://custom-icon-badges.demolab.com/badge/-MusicBrainz-353535?style=flat&logo=musicbrainz&logoColor=white" style="width: auto; height: 16px; margin: 0;">
|
||||||
|
`;
|
||||||
|
// Simplified button style to just be the badge/icon for cleaner look header
|
||||||
|
mbLink.style.padding = '0';
|
||||||
|
mbLink.style.border = 'none';
|
||||||
|
mbLink.style.background = 'transparent';
|
||||||
|
mbLink.style.marginLeft = '12px';
|
||||||
|
mbLink.style.verticalAlign = 'middle';
|
||||||
|
|
||||||
|
nameElement.appendChild(mbLink);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (genresElement) {
|
if (genresElement) {
|
||||||
|
|
@ -25117,6 +25169,26 @@ function createLibraryArtistCard(artist) {
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "library-artist-card";
|
card.className = "library-artist-card";
|
||||||
card.setAttribute("data-artist-id", artist.id);
|
card.setAttribute("data-artist-id", artist.id);
|
||||||
|
// Add relative positioning for icon and smooth transition
|
||||||
|
card.style.position = 'relative';
|
||||||
|
card.style.transition = 'transform 0.2s, box-shadow 0.2s';
|
||||||
|
|
||||||
|
// Add MusicBrainz icon if ID exists
|
||||||
|
if (artist.musicbrainz_id) {
|
||||||
|
const mbIcon = document.createElement('div');
|
||||||
|
mbIcon.className = 'mb-card-icon';
|
||||||
|
mbIcon.title = 'View on MusicBrainz';
|
||||||
|
|
||||||
|
// Use official SVG logo
|
||||||
|
const mbLogoUrl = 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/MusicBrainz_Logo_%282016%29.svg/500px-MusicBrainz_Logo_%282016%29.svg.png';
|
||||||
|
mbIcon.innerHTML = `<img src="${mbLogoUrl}" style="width: 20px; height: auto; display: block;">`;
|
||||||
|
|
||||||
|
mbIcon.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
window.open(`https://musicbrainz.org/artist/${artist.musicbrainz_id}`, '_blank');
|
||||||
|
};
|
||||||
|
card.appendChild(mbIcon);
|
||||||
|
}
|
||||||
|
|
||||||
// Create image element
|
// Create image element
|
||||||
const imageContainer = document.createElement("div");
|
const imageContainer = document.createElement("div");
|
||||||
|
|
@ -25329,12 +25401,12 @@ async function loadArtistDetailData(artistId, artistName) {
|
||||||
console.log(`🎨 Main content visibility:`, document.getElementById('artist-detail-main'));
|
console.log(`🎨 Main content visibility:`, document.getElementById('artist-detail-main'));
|
||||||
console.log(`🎨 Albums section:`, document.getElementById('albums-section'));
|
console.log(`🎨 Albums section:`, document.getElementById('albums-section'));
|
||||||
|
|
||||||
// Update header with artist name now that data is loaded
|
// Populate the page with data (which updates the hero section and sets textContent)
|
||||||
updateArtistDetailPageHeader(data.artist.name);
|
|
||||||
|
|
||||||
// Populate the page with data
|
|
||||||
populateArtistDetailPage(data);
|
populateArtistDetailPage(data);
|
||||||
|
|
||||||
|
// Update header with artist name and MusicBrainz link LAST to avoid overwrite
|
||||||
|
updateArtistDetailPageHeaderWithData(data.artist);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ Error loading artist detail data:`, error);
|
console.error(`❌ Error loading artist detail data:`, error);
|
||||||
|
|
||||||
|
|
@ -25358,6 +25430,63 @@ function updateArtistDetailPageHeader(artistName) {
|
||||||
const mainTitle = document.getElementById("artist-info-name");
|
const mainTitle = document.getElementById("artist-info-name");
|
||||||
if (mainTitle) {
|
if (mainTitle) {
|
||||||
mainTitle.textContent = artistName;
|
mainTitle.textContent = artistName;
|
||||||
|
|
||||||
|
// Try to find the artist object in memory to get the MBID
|
||||||
|
// We can look at the data passed to populateArtistDetailPage if this function accepted the full object
|
||||||
|
// Or access the state if it was saved
|
||||||
|
|
||||||
|
// Actually, let's look at how this is invoked. It's called from loadArtistDetailData which has the full 'data' object.
|
||||||
|
// But this function only accepts 'artistName'.
|
||||||
|
// We should query the state or modify the function signature.
|
||||||
|
// For now, let's try to find the MB link element and update it if we can find the artist in data.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateArtistDetailPageHeaderWithData(artist) {
|
||||||
|
// Target the visible header element
|
||||||
|
const mainTitle = document.getElementById("artist-detail-name");
|
||||||
|
|
||||||
|
if (mainTitle) {
|
||||||
|
console.log('✅ [DEBUG] Updating header for:', artist.name);
|
||||||
|
// We assume textContent is set by updateArtistHeroSection, so we just APPEND the link
|
||||||
|
// But to be safe, we can ensure name is there.
|
||||||
|
// If we run AFTER populateArtistDetailPage, textContent is already set.
|
||||||
|
|
||||||
|
// If we reset textContent here, we might lose other formatting?
|
||||||
|
// artist-detail-name usually just contains text.
|
||||||
|
mainTitle.textContent = artist.name;
|
||||||
|
|
||||||
|
if (artist.musicbrainz_id) {
|
||||||
|
console.log('✅ [DEBUG] Adding MusicBrainz link to DETAIL page header for', artist.name);
|
||||||
|
|
||||||
|
// Remove existing link if any (to prevent duplicates)
|
||||||
|
const existingMb = mainTitle.querySelector('.mb-link-btn');
|
||||||
|
if (existingMb) existingMb.remove();
|
||||||
|
|
||||||
|
const mbLink = document.createElement('a');
|
||||||
|
mbLink.className = 'mb-link-btn';
|
||||||
|
mbLink.href = `https://musicbrainz.org/artist/${artist.musicbrainz_id}`;
|
||||||
|
mbLink.target = '_blank';
|
||||||
|
mbLink.title = 'View on MusicBrainz';
|
||||||
|
// Use the specific logo requested by user
|
||||||
|
const mbLogoUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/MusicBrainz_Logo_%282016%29.svg/500px-MusicBrainz_Logo_%282016%29.svg.png";
|
||||||
|
|
||||||
|
mbLink.innerHTML = `
|
||||||
|
<img src="${mbLogoUrl}" style="height: 24px; width: auto; vertical-align: middle; display: inline-block;">
|
||||||
|
<span style="color:rgba(255,255,255,0.9); margin-left:10px; font-size: 13px; font-weight: 600; vertical-align: middle;">View on MusicBrainz</span>
|
||||||
|
`;
|
||||||
|
mbLink.style.padding = '5px 14px';
|
||||||
|
mbLink.style.border = '1px solid rgba(255,255,255,0.1)';
|
||||||
|
mbLink.style.background = 'rgba(0,0,0,0.4)';
|
||||||
|
mbLink.style.borderRadius = '20px';
|
||||||
|
mbLink.style.marginLeft = '16px';
|
||||||
|
mbLink.style.display = 'inline-flex';
|
||||||
|
mbLink.style.alignItems = 'center';
|
||||||
|
mbLink.style.textDecoration = 'none';
|
||||||
|
mbLink.style.verticalAlign = 'middle';
|
||||||
|
|
||||||
|
mainTitle.appendChild(mbLink);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25602,6 +25731,28 @@ function createReleaseCard(release) {
|
||||||
card.setAttribute("data-release-id", release.id || "");
|
card.setAttribute("data-release-id", release.id || "");
|
||||||
card.setAttribute("data-spotify-id", release.spotify_id || "");
|
card.setAttribute("data-spotify-id", release.spotify_id || "");
|
||||||
|
|
||||||
|
// DEBUG: Log release to check for MBID
|
||||||
|
// console.log(`🔍 [DEBUG] Release: ${release.title}`, release);
|
||||||
|
|
||||||
|
// Add MusicBrainz icon if available
|
||||||
|
let mbIcon = null;
|
||||||
|
if (release.musicbrainz_release_id) {
|
||||||
|
console.log(`✅ [DEBUG] Adding MB icon for release: ${release.title}`);
|
||||||
|
const mbLogoUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/MusicBrainz_Logo_%282016%29.svg/500px-MusicBrainz_Logo_%282016%29.svg.png";
|
||||||
|
mbIcon = document.createElement("div");
|
||||||
|
mbIcon.className = "mb-card-icon";
|
||||||
|
mbIcon.title = "View on MusicBrainz";
|
||||||
|
// Use image instead of text
|
||||||
|
mbIcon.innerHTML = `<img src="${mbLogoUrl}" style="width: 20px; height: auto; display: block;">`;
|
||||||
|
mbIcon.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
window.open(`https://musicbrainz.org/release/${release.musicbrainz_release_id}`, '_blank');
|
||||||
|
};
|
||||||
|
// Will append last
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create image
|
// Create image
|
||||||
const imageContainer = document.createElement("div");
|
const imageContainer = document.createElement("div");
|
||||||
if (release.image_url && release.image_url.trim() !== "") {
|
if (release.image_url && release.image_url.trim() !== "") {
|
||||||
|
|
@ -25733,6 +25884,11 @@ function createReleaseCard(release) {
|
||||||
card.appendChild(year);
|
card.appendChild(year);
|
||||||
card.appendChild(completion);
|
card.appendChild(completion);
|
||||||
|
|
||||||
|
// Add MusicBrainz icon LAST to ensure it's on top
|
||||||
|
if (release.musicbrainz_release_id && mbIcon) { // Check if mbIcon was created
|
||||||
|
card.appendChild(mbIcon);
|
||||||
|
}
|
||||||
|
|
||||||
// Add click handler for release card
|
// Add click handler for release card
|
||||||
card.addEventListener("click", async () => {
|
card.addEventListener("click", async () => {
|
||||||
console.log(`Clicked on release: ${release.title} (Owned: ${release.owned})`);
|
console.log(`Clicked on release: ${release.title} (Owned: ${release.owned})`);
|
||||||
|
|
|
||||||
|
|
@ -21715,3 +21715,74 @@ body {
|
||||||
border-right: 6px solid transparent;
|
border-right: 6px solid transparent;
|
||||||
border-bottom: 8px solid rgba(30, 30, 30, 0.98);
|
border-bottom: 8px solid rgba(30, 30, 30, 0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MusicBrainz Integration */
|
||||||
|
.release-card {
|
||||||
|
position: relative !important;
|
||||||
|
/* Ensure positioning context */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-link-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
margin-left: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-link-btn:hover {
|
||||||
|
background: rgba(186, 71, 143, 0.4);
|
||||||
|
/* MusicBrainz Purple-ish */
|
||||||
|
border-color: rgba(186, 71, 143, 0.6);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-link-btn img,
|
||||||
|
.mb-link-btn svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-card-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
/* MusicBrainz Purple - Force visible color */
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.9 !important;
|
||||||
|
/* Force visible */
|
||||||
|
transition: transform 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 100 !important;
|
||||||
|
/* Force on top */
|
||||||
|
color: white;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 800;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-card-icon:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
background: #d466a9;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue