Skip to content

Conversation

@biase-d
Copy link

@biase-d biase-d commented Nov 18, 2025

Hi, Thanks for the awesome project. I have made a few changes hope they will be useful

  • Player State Persistence: The player's state is no longer wiped out on navigation, The current queue, active track, and playback progress are now saved to Local Storage. This prevents the user from losing their listening session upon a page refresh.

  • Hybrid Caching Strategy: Implements a sophisticated dual-cache system in the service worker. The core application shell is stored in a versioned cache that updates with new deployments, while a separate, persistent cache is used for data (like audio streams and API responses). This ensures media is not re-downloaded unnecessarily after an update

  • Reliable API Routing: Integrates the API health-checking system directly into the fetch logic. Requests are now intelligently routed to healthy, responsive endpoints, ensuring the application remains functional even if some API servers are down

  • Compact Audio Player: The audio player now collapses into a space-saving compact mode on smaller viewports, preventing it from taking up excessive screen real estate

  • Auto-Hiding Header on Mobile: To maximize content visibility, the main header now gracefully slides out of view when scrolling down and reappears when scrolling up on smaller screen devices

  • Fixes: Corrects the URLs for the favicon and site.webmanifest files to ensure they load correctly and the PWA is properly configured

@uimaxbai
Copy link
Owner

Hey! Thanks for the PR. I'm gonna do something I've never done before and get Copilot to check it - maybe the results will be good. It's mainly because I've done a quick config resolution that may have caused issues.

Copilot finished reviewing on behalf of uimaxbai November 20, 2025 20:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request introduces significant enhancements focused on player state persistence, service worker caching improvements, API health monitoring, and mobile UI optimizations. The changes aim to provide a more resilient and user-friendly experience, particularly for mobile users and those experiencing API connectivity issues.

Key Changes:

  • Implements localStorage-based player state persistence to maintain playback queue and progress across page refreshes
  • Introduces a dual-cache strategy in the service worker with separate versioned caches for app assets and persistent data
  • Adds an API health-checking system that monitors endpoint availability and routes requests to healthy servers

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
src/service-worker.ts Implements hybrid caching with separate APP_CACHE and DATA_CACHE, adds proxy URL caching for audio streams
src/lib/stores/player.ts Adds player state persistence using localStorage, saves queue, track, and playback position
src/lib/stores/apiHealth.ts New store that periodically checks API endpoint health and maintains list of healthy targets
src/lib/config.ts Integrates health checking into API routing, routes requests to healthy endpoints based on health store
src/routes/+layout.svelte Adds auto-hiding header on mobile with scroll detection, fixes manifest/favicon paths, initializes health checks
src/lib/components/AudioPlayer.svelte Implements collapsible mini-player mode for mobile viewports with compact/expanded states
src/lib/components/SearchInterface.svelte Refactors click event handling to prevent navigation when clicking artist/album links
src/routes/+page.svelte Changes track selection behavior to insert into queue instead of replacing it
static/site.webmanifest Removes trailing comma syntax error
Comments suppressed due to low confidence (1)

src/lib/components/AudioPlayer.svelte:1402

  • Malformed HTML: Closing </p> tag without a corresponding opening tag. The artist link and quality information starting at line 1383 should be wrapped in a <p> tag. Add <p class="text-xs text-gray-500"> before the <a> tag on line 1383 to properly open this paragraph element, or remove the closing </p> tag on line 1402 if these elements should not be in a paragraph.
									<a
										href={`/artist/${$playerStore.currentTrack.artist.id}`}
										class="truncate text-sm text-gray-400 hover:text-blue-400 hover:underline inline-block"
										data-sveltekit-preload-data
									>
										{formatArtists($playerStore.currentTrack.artists)}
									</a>
									<span class="mx-1" aria-hidden="true">•</span>
									<span>{formatQualityLabel(currentPlaybackQuality ?? undefined)}</span>
									{#if currentPlaybackQuality && $playerStore.currentTrack.audioQuality && currentPlaybackQuality !== $playerStore.currentTrack.audioQuality}
										<span class="mx-1 text-gray-600" aria-hidden="true">•</span>
										<span class="text-gray-500">
											({formatQualityLabel($playerStore.currentTrack.audioQuality)} available)
										</span>
									{/if}
									{#if sampleRateLabel}
										<span class="mx-1 text-gray-600" aria-hidden="true">•</span>
										<span>{sampleRateLabel}</span>
									{/if}
								</p>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


// Only handle same-origin requests
if (url.origin !== self.location.origin) {
if (url.origin !== sw.location.origin && !url.pathname.startsWith('/api/proxy')) {
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic error: The condition url.origin !== sw.location.origin && !url.pathname.startsWith('/api/proxy') is incorrect for cross-origin requests. This checks if the pathname starts with '/api/proxy' on the request URL's origin, but for cross-origin requests, you should be checking the path on the service worker's origin. A proxied request would have a URL like https://yoursite.com/api/proxy?url=..., so the check should be url.origin !== sw.location.origin (without the pathname check), or if you want to handle proxy URLs specially, check the origin first: if (url.origin === sw.location.origin || url.pathname.startsWith('/api/proxy')).

Suggested change
if (url.origin !== sw.location.origin && !url.pathname.startsWith('/api/proxy')) {
if (!(url.origin === sw.location.origin || url.pathname.startsWith('/api/proxy'))) {

Copilot uses AI. Check for mistakes.
const cache = await caches.open(cacheName);
const cachedResponse = await cache.match(request);
// Fallback to offline page if both network and cache fail
return cachedResponse || (await cache.match('/offline.html'))!;
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-null assertion without guarantee: Using the non-null assertion operator ! on await cache.match('/offline.html') assumes the offline page is always in the cache. However, if caching the assets fails during installation, this could return undefined and cause a runtime error. Consider handling the case where the offline page might not be cached: return cachedResponse || (await cache.match('/offline.html')) || new Response('Offline', { status: 503 }).

Suggested change
return cachedResponse || (await cache.match('/offline.html'))!;
return cachedResponse || (await cache.match('/offline.html')) || new Response('Offline', { status: 503 });

Copilot uses AI. Check for mistakes.
document.removeEventListener('click', handleDocumentClick);
window.removeEventListener('scroll', handleScroll);
mediaQuery.removeEventListener('change', updateIsMobile);
unsubscribe();
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory leak: The subscription unsubPerf created on line 414 is not cleaned up in the return function. Add unsubPerf() to the cleanup function to prevent memory leaks.

Suggested change
unsubscribe();
unsubscribe();
unsubPerf();

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +81
if (browser) {
store.subscribe((state) => {
saveState(state);
});
}
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Potential infinite loop: The store subscription on lines 78-80 will trigger saveState on every state change. However, since the subscription is set up during store creation (before the store is fully initialized), this could cause issues. More importantly, if there's ever a scenario where loading state triggers a state update that triggers a save, this could cause performance issues. Consider debouncing the saveState calls or using a flag to prevent saving during initialization.

Copilot uses AI. Check for mistakes.
}
// Make sure the player height is updated when the view changes
$effect(() => {
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Over-eager effect trigger: The $effect on lines 97-99 will run whenever any reactive dependency changes, which could be frequently. Since the comment says it should update when the view changes, it should specifically track isExpanded: $effect(() => { isExpanded; notifyContainerHeight(); }) or use a more explicit effect that only runs when isExpanded changes. This will prevent unnecessary calls to notifyContainerHeight().

Suggested change
$effect(() => {
$effect(() => {
isExpanded;

Copilot uses AI. Check for mistakes.
>
{$playerStore.currentTrack.album.title}
{formatArtists($playerStore.currentTrack.artists)}
</a>
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Removed album information: The expanded player view previously displayed the album title with a link to the album page (visible in other components like SearchInterface and queue items). This information has been removed in the refactored player, reducing the metadata available to users. Consider adding back the album link, similar to how it appears in the queue panel: <a href={/album/${$playerStore.currentTrack.album.id}} class="hover:text-blue-400 hover:underline" data-sveltekit-preload-data>{$playerStore.currentTrack.album.title}</a>.

Suggested change
</a>
</a>
{#if $playerStore.currentTrack.album}
<span class="mx-1" aria-hidden="true">•</span>
<a
href={`/album/${$playerStore.currentTrack.album.id}`}
class="truncate text-sm text-gray-400 hover:text-blue-400 hover:underline inline-block"
data-sveltekit-preload-data
>
{$playerStore.currentTrack.album.title}
</a>
{/if}

Copilot uses AI. Check for mistakes.
Comment on lines +1613 to +1619
<a
href={`/artist/${queuedTrack.artist.id}`}
class="truncate text-xs text-gray-400 hover:text-blue-400 hover:underline inline-block"
data-sveltekit-preload-data
>
{formatArtists(queuedTrack.artists)}
</a>
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing event propagation handling: The artist link on line 1613 will trigger playFromQueue(index) when clicked because it's inside a clickable container. The onclick={(e) => e.stopPropagation()} handler was removed from the link, but no alternative solution was implemented. Consider using event delegation in the parent's onclick handler to check if an anchor was clicked: onclick={(event) => { if (!(event.target as HTMLElement).closest('a')) { playFromQueue(index); } }}.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@uimaxbai
Copy link
Owner

uimaxbai commented Nov 20, 2025

Harsh, isn't it... I'll have to wait until I have some spare time so I can properly make sure that these aren't errors until I can merge. The tabs and spaces stuff I'll have to change to spaces myself. Hang tight!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants