diff --git a/js/api.js b/js/api.js index 2a14ea2..ab66413 100644 --- a/js/api.js +++ b/js/api.js @@ -219,12 +219,12 @@ export class LosslessAPI { } } - async searchTracks(query) { + async searchTracks(query, options = {}) { const cached = await this.cache.get('search_tracks', query); if (cached) return cached; try { - const response = await this.fetchWithRetry(`/search/?s=${encodeURIComponent(query)}`); + const response = await this.fetchWithRetry(`/search/?s=${encodeURIComponent(query)}`, options); const data = await response.json(); const normalized = this.normalizeSearchResponse(data, 'tracks'); const result = { @@ -235,17 +235,18 @@ export class LosslessAPI { await this.cache.set('search_tracks', query, result); return result; } catch (error) { + if (error.name === 'AbortError') throw error; console.error('Track search failed:', error); return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 }; } } - async searchArtists(query) { + async searchArtists(query, options = {}) { const cached = await this.cache.get('search_artists', query); if (cached) return cached; try { - const response = await this.fetchWithRetry(`/search/?a=${encodeURIComponent(query)}`); + const response = await this.fetchWithRetry(`/search/?a=${encodeURIComponent(query)}`, options); const data = await response.json(); const normalized = this.normalizeSearchResponse(data, 'artists'); const result = { @@ -256,17 +257,18 @@ export class LosslessAPI { await this.cache.set('search_artists', query, result); return result; } catch (error) { + if (error.name === 'AbortError') throw error; console.error('Artist search failed:', error); return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 }; } } - async searchAlbums(query) { + async searchAlbums(query, options = {}) { const cached = await this.cache.get('search_albums', query); if (cached) return cached; try { - const response = await this.fetchWithRetry(`/search/?al=${encodeURIComponent(query)}`); + const response = await this.fetchWithRetry(`/search/?al=${encodeURIComponent(query)}`, options); const data = await response.json(); const normalized = this.normalizeSearchResponse(data, 'albums'); const result = { @@ -277,17 +279,18 @@ export class LosslessAPI { await this.cache.set('search_albums', query, result); return result; } catch (error) { + if (error.name === 'AbortError') throw error; console.error('Album search failed:', error); return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 }; } } - async searchPlaylists(query) { + async searchPlaylists(query, options = {}) { const cached = await this.cache.get('search_playlists', query); if (cached) return cached; try { - const response = await this.fetchWithRetry(`/search/?p=${encodeURIComponent(query)}`); + const response = await this.fetchWithRetry(`/search/?p=${encodeURIComponent(query)}`, options); const data = await response.json(); const normalized = this.normalizeSearchResponse(data, 'playlists'); const result = { @@ -298,6 +301,7 @@ export class LosslessAPI { await this.cache.set('search_playlists', query, result); return result; } catch (error) { + if (error.name === 'AbortError') throw error; console.error('Playlist search failed:', error); return { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 }; } diff --git a/js/events.js b/js/events.js index e11cd6a..4a82f2a 100644 --- a/js/events.js +++ b/js/events.js @@ -33,12 +33,14 @@ export function initializePlayerEvents(player, audioPlayer, scrobbler) { } playPauseBtn.innerHTML = SVG_PAUSE; player.updateMediaSessionPlaybackState(); + player.updateMediaSessionPositionState(); updateTabTitle(player); }); audioPlayer.addEventListener('pause', () => { playPauseBtn.innerHTML = SVG_PLAY; player.updateMediaSessionPlaybackState(); + player.updateMediaSessionPositionState(); }); audioPlayer.addEventListener('ended', () => { diff --git a/js/player.js b/js/player.js index 8f17b4f..932947b 100644 --- a/js/player.js +++ b/js/player.js @@ -143,6 +143,9 @@ export class Player { if (this.preloadAbortController.signal.aborted) break; this.preloadCache.set(track.id, streamUrl); + + // Warm connection/cache + fetch(streamUrl, { method: 'HEAD', signal: this.preloadAbortController.signal }).catch(() => {}); } catch (error) { if (error.name !== 'AbortError') { console.debug('Failed to get stream URL for preload:', trackTitle); @@ -250,6 +253,7 @@ export class Player { if (this.audio.paused) { this.audio.play().catch(e => { + if (e.name === 'NotAllowedError' || e.name === 'AbortError') return; console.error("Play failed, reloading track:", e); if (this.currentTrack) { this.playTrackFromQueue(); diff --git a/js/ui.js b/js/ui.js index 603f1f4..ad39f6e 100644 --- a/js/ui.js +++ b/js/ui.js @@ -6,6 +6,7 @@ export class UIRenderer { constructor(api) { this.api = api; this.currentTrack = null; + this.searchAbortController = null; } setCurrentTrack(track) { @@ -404,12 +405,18 @@ export class UIRenderer { albumsContainer.innerHTML = this.createSkeletonCards(6, false); playlistsContainer.innerHTML = this.createSkeletonCards(6, false); + if (this.searchAbortController) { + this.searchAbortController.abort(); + } + this.searchAbortController = new AbortController(); + const signal = this.searchAbortController.signal; + try { const [tracksResult, artistsResult, albumsResult, playlistsResult] = await Promise.all([ - this.api.searchTracks(query), - this.api.searchArtists(query), - this.api.searchAlbums(query), - this.api.searchPlaylists(query) + this.api.searchTracks(query, { signal }), + this.api.searchArtists(query, { signal }), + this.api.searchAlbums(query, { signal }), + this.api.searchPlaylists(query, { signal }) ]); let finalTracks = tracksResult.items; @@ -463,6 +470,7 @@ export class UIRenderer { : createPlaceholder('No playlists found.'); } catch (error) { + if (error.name === 'AbortError') return; console.error("Search failed:", error); const errorMsg = createPlaceholder(`Error during search. ${error.message}`); tracksContainer.innerHTML = errorMsg; diff --git a/js/utils.js b/js/utils.js index 12aa916..112c925 100644 --- a/js/utils.js +++ b/js/utils.js @@ -26,8 +26,8 @@ export const QUALITY_TOKENS = { export const RATE_LIMIT_ERROR_MESSAGE = 'Too Many Requests. Please wait a moment and try again.'; -export const SVG_PLAY = ''; -export const SVG_PAUSE = ''; +export const SVG_PLAY = ''; +export const SVG_PAUSE = ''; export const SVG_VOLUME = ''; export const SVG_MUTE = '';