diff --git a/js/api.js b/js/api.js index a2e52df..bcbad22 100644 --- a/js/api.js +++ b/js/api.js @@ -862,11 +862,11 @@ export class LosslessAPI { const trackMap = new Map(); const videoMap = new Map(); - const isTrack = (v) => v?.id && v.duration && v.album; + const isTrack = (v) => v?.id && v.duration; const isAlbum = (v) => v?.id && 'numberOfTracks' in v; const isVideo = (v) => v?.id && v.type === 'VIDEO'; - const scan = (value, visited = new Set()) => { + const scan = (value, visited) => { if (!value || typeof value !== 'object' || visited.has(value)) return; visited.add(value); @@ -877,13 +877,17 @@ export class LosslessAPI { const item = value.item || value; if (isAlbum(item)) albumMap.set(item.id, this.prepareAlbum(item)); - if (isTrack(item)) trackMap.set(item.id, this.prepareTrack(item)); + if (isTrack(item) && !isAlbum(item) && !isVideo(item)) { + trackMap.set(item.id, this.prepareTrack(item)); + } if (isVideo(item)) videoMap.set(item.id, this.prepareVideo(item)); Object.values(value).forEach((nested) => scan(nested, visited)); }; - entries.forEach((entry) => scan(entry)); + const visited = new Set(); + entries.forEach((entry) => scan(entry, visited)); + scan(primaryData, visited); if (!options.lightweight) { try { @@ -1097,7 +1101,7 @@ export class LosslessAPI { results.forEach((tracks) => { if (tracks.length > 0) { recommendedTracks.push(...tracks); - seenTrackIds.add(...tracks.map((t) => t.id)); + tracks.forEach((t) => seenTrackIds.add(t.id)); } }); diff --git a/js/events.js b/js/events.js index ee8f987..7927cbf 100644 --- a/js/events.js +++ b/js/events.js @@ -61,7 +61,7 @@ export function initializePlayerEvents(player, audioPlayer, scrobbler, ui) { const prevBtn = document.getElementById('prev-btn'); const shuffleBtn = document.getElementById('shuffle-btn'); const repeatBtn = document.getElementById('repeat-btn'); - const homeStartRadioBtn = document.getElementById('home-start-radio-btn'); + const homeStartRadioBtn = document.getElementById('home-start-infinite-radio-btn'); const sleepTimerBtnDesktop = document.getElementById('sleep-timer-btn-desktop'); const volumeBar = document.getElementById('volume-bar'); @@ -778,13 +778,13 @@ export async function handleTrackAction( if (!item) return; // Actions not allowed for unavailable tracks - const forbiddenForUnavailable = ['add-to-queue', 'play-next', 'track-mix', 'download', 'start-radio']; + const forbiddenForUnavailable = ['add-to-queue', 'play-next', 'track-mix', 'download', 'start-radio', 'start-infinite-radio']; if (item.isUnavailable && forbiddenForUnavailable.includes(action)) { showNotification('This track is unavailable.'); return; } - if (action === 'start-radio') { + if (action === 'start-radio' || action === 'start-infinite-radio') { let tracks = []; if (type === 'track') { tracks = [item]; diff --git a/js/player.js b/js/player.js index e7a7a5a..79d1700 100644 --- a/js/player.js +++ b/js/player.js @@ -909,6 +909,9 @@ export class Player { } } else { this.radioSeeds = Array.isArray(seeds) ? seeds : [seeds]; + this.wipeQueue(); + this.setQueue(this.radioSeeds, 0, true); + this.playAtIndex(0); } const currentQueue = this.getCurrentQueue(); @@ -949,21 +952,23 @@ export class Player { if (recommendations && recommendations.length > 0) { const currentQueueIds = new Set(this.getCurrentQueue().map((t) => t.id)); - const [favorites, userPlaylists] = await Promise.all([ + const [favorites, userPlaylists, history] = await Promise.all([ db.getFavorites('track'), db.getAll('user_playlists'), + db.getHistory(), ]); - + const knownTrackIds = new Set([ - ...favorites.map(t => t.id), - ...userPlaylists.flatMap(p => (p.tracks || []).map(t => t.id)) + ...favorites.map((t) => t.id), + ...userPlaylists.flatMap((p) => (p.tracks || []).map((t) => t.id)), + ...history.map((t) => t.id), ]); const newTracks = recommendations.filter((t) => { if (currentQueueIds.has(t.id)) return false; - + if (knownTrackIds.has(t.id)) { - return Math.random() < 0.2; + return Math.random() < 0.05; } return true;