diff --git a/index.html b/index.html index b89d71c..8e9c471 100644 --- a/index.html +++ b/index.html @@ -2420,6 +2420,21 @@ +
+
+
+ Shuffle Editor's Picks + Randomize the order of editor's picks on each load +
+ +
+
+
diff --git a/js/settings.js b/js/settings.js index 1ebabdb..210673f 100644 --- a/js/settings.js +++ b/js/settings.js @@ -1306,6 +1306,14 @@ export function initializeSettings(scrobbler, player, api, ui) { }); } + const shuffleEditorsPicksToggle = document.getElementById('shuffle-editors-picks-toggle'); + if (shuffleEditorsPicksToggle) { + shuffleEditorsPicksToggle.checked = homePageSettings.shouldShuffleEditorsPicks(); + shuffleEditorsPicksToggle.addEventListener('change', (e) => { + homePageSettings.setShuffleEditorsPicks(e.target.checked); + }); + } + // Sidebar Section Toggles const sidebarShowHomeToggle = document.getElementById('sidebar-show-home-toggle'); if (sidebarShowHomeToggle) { diff --git a/js/storage.js b/js/storage.js index 6feda33..f3d8938 100644 --- a/js/storage.js +++ b/js/storage.js @@ -1187,6 +1187,21 @@ export const homePageSettings = { setShowEditorsPicks(enabled) { localStorage.setItem(this.SHOW_EDITORS_PICKS_KEY, enabled ? 'true' : 'false'); }, + + SHUFFLE_EDITORS_PICKS_KEY: 'home-shuffle-editors-picks', + + shouldShuffleEditorsPicks() { + try { + const val = localStorage.getItem(this.SHUFFLE_EDITORS_PICKS_KEY); + return val === null ? true : val === 'true'; + } catch { + return true; + } + }, + + setShuffleEditorsPicks(enabled) { + localStorage.setItem(this.SHUFFLE_EDITORS_PICKS_KEY, enabled ? 'true' : 'false'); + }, }; export const sidebarSectionSettings = { diff --git a/js/ui.js b/js/ui.js index b2ac404..43c90b6 100644 --- a/js/ui.js +++ b/js/ui.js @@ -1585,36 +1585,89 @@ export class UIRenderer { const response = await fetch('/editors-picks.json'); if (!response.ok) throw new Error("Failed to load editor's picks"); - const items = await response.json(); + let items = await response.json(); if (!Array.isArray(items) || items.length === 0) { picksContainer.innerHTML = createPlaceholder("No editor's picks available."); return; } - // Fetch details for each item + // Shuffle items if enabled + if (homePageSettings.shouldShuffleEditorsPicks()) { + items = [...items].sort(() => Math.random() - 0.5); + } + + // Use cached metadata or fetch details for each item const cardsHTML = []; const itemsToStore = []; for (const item of items.slice(0, 12)) { try { if (item.type === 'album') { - const result = await this.api.getAlbum(item.id); - if (result && result.album) { - cardsHTML.push(this.createAlbumCardHTML(result.album)); - itemsToStore.push({ el: null, data: result.album, type: 'album' }); + // Check if we have cached metadata + if (item.title && item.artist) { + // Use cached data directly + const album = { + id: item.id, + title: item.title, + artist: item.artist, + releaseDate: item.releaseDate, + cover: item.cover, + explicit: item.explicit, + audioQuality: item.audioQuality, + mediaMetadata: item.mediaMetadata, + type: 'ALBUM', + }; + cardsHTML.push(this.createAlbumCardHTML(album)); + itemsToStore.push({ el: null, data: album, type: 'album' }); + } else { + // Fall back to API call for legacy format + const result = await this.api.getAlbum(item.id); + if (result && result.album) { + cardsHTML.push(this.createAlbumCardHTML(result.album)); + itemsToStore.push({ el: null, data: result.album, type: 'album' }); + } } } else if (item.type === 'artist') { - const artist = await this.api.getArtist(item.id); - if (artist) { + if (item.name && item.picture) { + // Use cached data directly + const artist = { + id: item.id, + name: item.name, + picture: item.picture, + }; cardsHTML.push(this.createArtistCardHTML(artist)); itemsToStore.push({ el: null, data: artist, type: 'artist' }); + } else { + // Fall back to API call + const artist = await this.api.getArtist(item.id); + if (artist) { + cardsHTML.push(this.createArtistCardHTML(artist)); + itemsToStore.push({ el: null, data: artist, type: 'artist' }); + } } } else if (item.type === 'track') { - const track = await this.api.getTrackMetadata(item.id); - if (track) { + if (item.title && item.album) { + // Use cached data directly + const track = { + id: item.id, + title: item.title, + artist: item.artist, + album: item.album, + explicit: item.explicit, + audioQuality: item.audioQuality, + mediaMetadata: item.mediaMetadata, + duration: item.duration, + }; cardsHTML.push(this.createTrackCardHTML(track)); itemsToStore.push({ el: null, data: track, type: 'track' }); + } else { + // Fall back to API call + const track = await this.api.getTrackMetadata(item.id); + if (track) { + cardsHTML.push(this.createTrackCardHTML(track)); + itemsToStore.push({ el: null, data: track, type: 'track' }); + } } } } catch (e) { diff --git a/public/editors-picks.json b/public/editors-picks.json index 187ad9a..20ca3d5 100644 --- a/public/editors-picks.json +++ b/public/editors-picks.json @@ -1,11 +1,112 @@ [ - { "type": "album", "id": "4527433" }, - { "type": "album", "id": "413189044" }, - { "type": "album", "id": "488297983" }, - { "type": "album", "id": "90502209" }, - { "type": "album", "id": "447277344" }, - { "type": "album", "id": "490209783" }, - { "type": "track", "id": "279082605" }, - { "type": "album", "id": "380211863" }, - { "type": "album", "id": "4644639003" } + { + "type": "album", + "id": 4527433, + "title": "Flockaveli", + "artist": { "id": 3654061, "name": "Waka Flocka Flame" }, + "releaseDate": "2010-10-05", + "cover": "05702b51-45cf-4157-b9ed-dd7ca7e7b7b3", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS"] } + }, + { + "type": "album", + "id": 90502209, + "title": "NASIR", + "artist": { "id": 1003, "name": "Nas" }, + "releaseDate": "2018-06-15", + "cover": "503ea6b2-0829-438e-8e4e-9a988154b3bc", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS", "HIRES_LOSSLESS"] } + }, + { + "type": "album", + "id": 413189044, + "title": "Jump Out", + "artist": { "id": 27836827, "name": "OsamaSon" }, + "releaseDate": "2025-01-24", + "cover": "ec4a4ef2-69fe-4d3c-aaba-05dc2d546e84", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS", "HIRES_LOSSLESS"] } + }, + { + "type": "album", + "id": 209061256, + "title": "Super Tecmo Bo", + "artist": { "id": 4839917, "name": "Boldy James" }, + "releaseDate": "2021-12-17", + "cover": "f58ca804-1da7-4953-a26d-2b3258310db5", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS"] } + }, + { + "type": "album", + "id": 488297983, + "title": "So Much Country 'Till We Get There", + "artist": { "id": 51427239, "name": "Westside Cowboy" }, + "releaseDate": "2026-01-23", + "cover": "dd9d4f23-1517-4a76-8e3f-31e341dcd525", + "explicit": false, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS", "HIRES_LOSSLESS"] } + }, + { + "type": "album", + "id": 447277344, + "title": "REST IN BASS", + "artist": { "id": 50418386, "name": "Che" }, + "releaseDate": "2025-07-18", + "cover": "d1397066-72ed-4481-b508-77f7c7a03073", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS"] } + }, + { + "type": "album", + "id": 490209783, + "title": "My Ghosts Go Ghost", + "artist": { "id": 39920098, "name": "By Storm" }, + "releaseDate": "2026-01-30", + "cover": "bc470b88-3583-4853-976e-593855672322", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS", "HIRES_LOSSLESS"] } + }, + { + "type": "album", + "id": 279082597, + "title": "Noktifer's Symphony", + "artist": { "id": 22055243, "name": "axxturel" }, + "releaseDate": "2020-11-17", + "cover": "a886acd8-b915-4a77-9c1f-eecf7fa6091c", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS"] } + }, + { + "type": "album", + "id": 380211863, + "title": "Sayso Says", + "artist": { "id": 50418386, "name": "Che" }, + "releaseDate": "2024-08-30", + "cover": "917c7e0f-3ebb-471e-9c88-fcdf6a9f21a5", + "explicit": true, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS"] } + }, + { + "type": "album", + "id": 464463900, + "title": "A-Rhythm Absolute", + "artist": { "id": 30049790, "name": "Sunday Mourners" }, + "releaseDate": "2026-01-16", + "cover": "d04e2ed8-f6d7-43c0-bcb2-fb99d3de0d5d", + "explicit": false, + "audioQuality": "LOSSLESS", + "mediaMetadata": { "tags": ["LOSSLESS", "HIRES_LOSSLESS"] } + } ]