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 = '';