diff --git a/index.html b/index.html index ef3f00f..1478e8e 100644 --- a/index.html +++ b/index.html @@ -226,6 +226,10 @@

Albums

+
diff --git a/js/api.js b/js/api.js index ab66413..4ba9fa3 100644 --- a/js/api.js +++ b/js/api.js @@ -484,15 +484,42 @@ export class LosslessAPI { entries.forEach(entry => scan(entry)); - const albums = Array.from(albumMap.values()).sort((a, b) => + // Attempt to find more albums/EPs via search since the direct feed might be limited + try { + const searchResults = await this.searchAlbums(artist.name); + if (searchResults && searchResults.items) { + const numericArtistId = Number(artistId); + + for (const item of searchResults.items) { + const itemArtistId = item.artist?.id; + const matchesArtist = itemArtistId === numericArtistId || + (Array.isArray(item.artists) && item.artists.some(a => a.id === numericArtistId)); + + if (matchesArtist && !albumMap.has(item.id)) { + albumMap.set(item.id, item); + } + } + } + } catch (e) { + console.warn('Failed to fetch additional albums via search:', e); + } + + const allReleases = Array.from(albumMap.values()).sort((a, b) => new Date(b.releaseDate || 0) - new Date(a.releaseDate || 0) ); + const eps = allReleases.filter(a => + a.type === 'EP' || + a.type === 'SINGLE' || + (a.numberOfTracks < 7 && !a.type) + ); + const albums = allReleases.filter(a => !eps.includes(a)); + const tracks = Array.from(trackMap.values()) .sort((a, b) => (b.popularity || 0) - (a.popularity || 0)) - .slice(0, 10); + .slice(0, 15); - const result = { ...artist, albums, tracks }; + const result = { ...artist, albums, eps, tracks }; await this.cache.set('artist', artistId, result); return result; diff --git a/js/app.js b/js/app.js index e577baf..e086e50 100644 --- a/js/app.js +++ b/js/app.js @@ -418,8 +418,9 @@ document.addEventListener('DOMContentLoaded', async () => { try { const artist = await api.getArtist(artistId); - if (!artist.albums || artist.albums.length === 0) { - throw new Error("No albums found for this artist"); + const allReleases = [...(artist.albums || []), ...(artist.eps || [])]; + if (allReleases.length === 0) { + throw new Error("No albums or EPs found for this artist"); } const trackSet = new Set(); @@ -427,7 +428,7 @@ document.addEventListener('DOMContentLoaded', async () => { const chunks = []; const chunkSize = 3; - const albums = artist.albums; + const albums = allReleases; for (let i = 0; i < albums.length; i += chunkSize) { chunks.push(albums.slice(i, i + chunkSize)); diff --git a/js/downloads.js b/js/downloads.js index c652c33..46a0041 100644 --- a/js/downloads.js +++ b/js/downloads.js @@ -387,12 +387,13 @@ export async function downloadDiscography(artist, api, quality, lyricsManager = const template = localStorage.getItem('zip-folder-template') || '{albumTitle} - {albumArtist} - monochrome.tf'; const rootFolder = `${sanitizeForFilename(artist.name)} discography - monochrome.tf`; - const totalAlbums = artist.albums.length; + const allReleases = [...(artist.albums || []), ...(artist.eps || [])]; + const totalAlbums = allReleases.length; const notification = createBulkDownloadNotification('discography', artist.name, totalAlbums); try { - for (let albumIndex = 0; albumIndex < artist.albums.length; albumIndex++) { - const album = artist.albums[albumIndex]; + for (let albumIndex = 0; albumIndex < allReleases.length; albumIndex++) { + const album = allReleases[albumIndex]; updateBulkDownloadProgress(notification, albumIndex, totalAlbums, album.title); diff --git a/js/ui.js b/js/ui.js index f5ae910..e39e7e6 100644 --- a/js/ui.js +++ b/js/ui.js @@ -140,6 +140,17 @@ export class UIRenderer { yearDisplay = `${date.getFullYear()}`; } } + + let typeLabel = ''; + if (album.type === 'EP') { + typeLabel = ' • EP'; + } else if (album.type === 'SINGLE') { + typeLabel = ' • Single'; + } else if (!album.type && album.numberOfTracks) { + if (album.numberOfTracks <= 3) typeLabel = ' • Single'; + else if (album.numberOfTracks <= 6) typeLabel = ' • EP'; + } + return `
@@ -147,7 +158,7 @@ export class UIRenderer {

${album.title} ${explicitBadge}

${album.artist?.name ?? ''}

-

${yearDisplay}

+

${yearDisplay}${typeLabel}

`; } @@ -557,43 +568,57 @@ export class UIRenderer { document.title = `${album.title} - ${album.artist.name} - Monochrome`; - // "More from Artist" Section + // "More from Artist" Sections try { - // Remove any existing "More from" section if re-rendering - const existingMoreSection = document.getElementById('album-more-from-artist'); - if (existingMoreSection) existingMoreSection.remove(); + // Remove any existing "More from" sections if re-rendering + document.querySelectorAll('.album-more-section').forEach(el => el.remove()); + document.getElementById('album-more-from-artist')?.remove(); // Legacy cleanup - const moreSection = document.createElement('section'); - moreSection.id = 'album-more-from-artist'; - moreSection.className = 'content-section'; - moreSection.style.marginTop = '3rem'; - moreSection.innerHTML = ` + // Create placeholder section while loading + const placeholderSection = document.createElement('section'); + placeholderSection.className = 'content-section album-more-section'; + placeholderSection.style.marginTop = '3rem'; + placeholderSection.innerHTML = `

More from ${album.artist.name}

-
+
${this.createSkeletonCards(6, false)}
`; - document.getElementById('page-album').appendChild(moreSection); + document.getElementById('page-album').appendChild(placeholderSection); const artistData = await this.api.getArtist(album.artist.id); - // Filter out current album and duplicates - const otherAlbums = artistData.albums - .filter(a => a.id != album.id) - .filter((a, index, self) => - index === self.findIndex((t) => t.title === a.title) // Dedup by title - ) - .slice(0, 12); // Limit to 12 - - const moreContainer = document.getElementById('album-more-albums'); - if (otherAlbums.length > 0) { - moreContainer.innerHTML = otherAlbums.map(a => this.createAlbumCardHTML(a)).join(''); - } else { - moreSection.remove(); // Remove section if no other albums - } + // Remove placeholder + placeholderSection.remove(); + + const renderSection = (title, items) => { + const filtered = (items || []) + .filter(a => a.id != album.id) + .filter((a, index, self) => + index === self.findIndex((t) => t.title === a.title) // Dedup by title + ) + .slice(0, 12); + + if (filtered.length === 0) return; + + const section = document.createElement('section'); + section.className = 'content-section album-more-section'; + section.style.marginTop = '3rem'; + section.innerHTML = ` +

${title}

+
+ ${filtered.map(a => this.createAlbumCardHTML(a)).join('')} +
+ `; + document.getElementById('page-album').appendChild(section); + }; + + renderSection(`More albums from ${album.artist.name}`, artistData.albums); + renderSection(`EPs and Singles from ${album.artist.name}`, artistData.eps); + } catch (err) { console.warn('Failed to load "More from artist":', err); - document.getElementById('album-more-from-artist')?.remove(); + document.querySelectorAll('.album-more-section').forEach(el => el.remove()); } } catch (error) { @@ -672,6 +697,8 @@ async renderPlaylistPage(playlistId) { const metaEl = document.getElementById('artist-detail-meta'); const tracksContainer = document.getElementById('artist-detail-tracks'); const albumsContainer = document.getElementById('artist-detail-albums'); + const epsContainer = document.getElementById('artist-detail-eps'); + const epsSection = document.getElementById('artist-section-eps'); const dlBtn = document.getElementById('download-discography-btn'); if (dlBtn) dlBtn.innerHTML = `${SVG_DOWNLOAD}Download Discography`; @@ -681,6 +708,8 @@ async renderPlaylistPage(playlistId) { metaEl.innerHTML = '
'; tracksContainer.innerHTML = this.createSkeletonTracks(5, true); albumsContainer.innerHTML = this.createSkeletonCards(6, false); + if (epsContainer) epsContainer.innerHTML = this.createSkeletonCards(6, false); + if (epsSection) epsSection.style.display = 'none'; try { const artist = await this.api.getArtist(artistId); @@ -702,9 +731,21 @@ async renderPlaylistPage(playlistId) { `; this.renderListWithTracks(tracksContainer, artist.tracks, true); - albumsContainer.innerHTML = artist.albums.map(album => - this.createAlbumCardHTML(album) - ).join(''); + + // Render Albums + albumsContainer.innerHTML = artist.albums.length + ? artist.albums.map(album => this.createAlbumCardHTML(album)).join('') + : createPlaceholder('No albums found.'); + + // Render EPs and Singles + if (epsContainer && epsSection) { + if (artist.eps && artist.eps.length > 0) { + epsContainer.innerHTML = artist.eps.map(album => this.createAlbumCardHTML(album)).join(''); + epsSection.style.display = 'block'; + } else { + epsSection.style.display = 'none'; + } + } recentActivityManager.addArtist(artist);