diff --git a/js/api.js b/js/api.js index 10dc0e8..f02c6cb 100644 --- a/js/api.js +++ b/js/api.js @@ -971,6 +971,26 @@ export class LosslessAPI { throw new Error('Track metadata not found'); } + async getTrackRecommendations(id) { + const cached = await this.cache.get('recommendations', id); + if (cached) return cached; + + try { + const response = await this.fetchWithRetry(`/recommendations/?id=${id}`, { type: 'api' }); + const json = await response.json(); + const data = json.data || json; + + const items = data.items || []; + const tracks = items.map((item) => this.prepareTrack(item.track || item)); + + await this.cache.set('recommendations', id, tracks); + return tracks; + } catch (error) { + console.error('Failed to fetch recommendations:', error); + return []; + } + } + async getTrack(id, quality = 'HI_RES_LOSSLESS') { const cacheKey = `${id}_${quality}`; const cached = await this.cache.get('track', cacheKey); diff --git a/js/events.js b/js/events.js index 44b9f82..ae22ad8 100644 --- a/js/events.js +++ b/js/events.js @@ -1655,17 +1655,35 @@ export function initializeTrackInteractions(player, api, mainContent, contextMen !e.target.closest('.remove-from-playlist-btn') && !e.target.closest('.artist-link') ) { - const parentList = trackItem.closest('.track-list'); - const allTrackElements = Array.from(parentList.querySelectorAll('.track-item')); - const trackList = allTrackElements.map((el) => trackDataStore.get(el)).filter(Boolean); + const clickedTrackId = trackItem.dataset.trackId; + const isSearch = window.location.pathname.startsWith('/search/'); - if (trackList.length > 0) { - const clickedTrackId = trackItem.dataset.trackId; - const startIndex = trackList.findIndex((t) => t.id == clickedTrackId); + if (isSearch) { + const clickedTrack = trackDataStore.get(trackItem); + if (clickedTrack) { + player.setQueue([clickedTrack], 0); + document.getElementById('shuffle-btn').classList.remove('active'); + player.playTrackFromQueue(); - player.setQueue(trackList, startIndex); - document.getElementById('shuffle-btn').classList.remove('active'); - player.playTrackFromQueue(); + api.getTrackRecommendations(clickedTrack.id).then((recs) => { + if (recs && recs.length > 0) { + player.addToQueue(recs); + showNotification(`Added ${recs.length} recommendations to queue`); + } + }); + } + } else { + const parentList = trackItem.closest('.track-list'); + const allTrackElements = Array.from(parentList.querySelectorAll('.track-item')); + const trackList = allTrackElements.map((el) => trackDataStore.get(el)).filter(Boolean); + + if (trackList.length > 0) { + const startIndex = trackList.findIndex((t) => t.id == clickedTrackId); + + player.setQueue(trackList, startIndex); + document.getElementById('shuffle-btn').classList.remove('active'); + player.playTrackFromQueue(); + } } } diff --git a/js/music-api.js b/js/music-api.js index f5fbf4e..0e0d55c 100644 --- a/js/music-api.js +++ b/js/music-api.js @@ -98,6 +98,16 @@ export class MusicAPI { return this.tidalAPI.getMix(id); } + async getTrackRecommendations(id) { + const p = this.getProviderFromId(id) || this.getCurrentProvider(); + const api = this.getAPI(p); + const cleanId = this.stripProviderPrefix(id); + if (typeof api.getTrackRecommendations === 'function') { + return api.getTrackRecommendations(cleanId); + } + return []; + } + // Stream methods async getStreamUrl(id, quality, provider = null) { const p = provider || this.getProviderFromId(id) || this.getCurrentProvider();