feat(library): genre sliders, artist links, and navigation utils
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 3m49s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 19s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Successful in 18m24s

- music landing: diverse per-genre album sliders (online counts /
  offline wide-probe fallback) and home-screen library shortcuts
- add ArtistLinks component and shared navigation/genreDiversity utils
- player/playback-mode refinements across Rust and frontend
This commit is contained in:
2026-06-25 19:18:06 +02:00
parent 62874564ff
commit 1836615dc0
38 changed files with 1009 additions and 207 deletions
+8 -38
View File
@@ -7,6 +7,7 @@
import { auth, isAuthenticated, isLoading as isAuthLoading, currentUser } from "$lib/stores/auth";
import { library } from "$lib/stores/library";
import { currentMedia, isPlaying, playbackPosition, playbackDuration, shouldShowAudioMiniPlayer } from "$lib/stores/player";
import { isShuffle, repeatMode, hasNext as hasNextStore, hasPrevious as hasPreviousStore } from "$lib/stores/queue";
import { useScrollGuard } from "$lib/composables/useScrollGuard";
import Search from "$lib/components/Search.svelte";
import MiniPlayer from "$lib/components/player/MiniPlayer.svelte";
@@ -23,16 +24,15 @@
let showFullPlayer = $state(false);
let showOverflowMenu = $state(false);
let showSleepTimerModal = $state(false);
let shuffle = $state(false);
let repeat = $state<"off" | "all" | "one">("off");
let hasNext = $state(false);
let hasPrevious = $state(false);
// Queue status (shuffle, repeat, hasNext, hasPrevious) is event-driven via the
// queue store, which listens for queue_changed events from the backend. This
// updates instantly when toggling shuffle/repeat (no polling lag).
const shuffle = $derived($isShuffle);
const repeat = $derived($repeatMode);
const hasNext = $derived($hasNextStore);
const hasPrevious = $derived($hasPreviousStore);
let isAndroid = $state(false);
let pollInterval: ReturnType<typeof setInterval> | null = null;
let failedAttempts = 0;
const MAX_SILENT_FAILURES = 3; // Don't log errors for first 3 attempts
onMount(() => {
// Detect platform
try {
@@ -42,17 +42,7 @@
console.error("Platform detection failed:", err);
}
// Poll for queue status (shuffle, repeat, hasNext, hasPrevious)
// Position/duration come from player events (pushed every 250ms)
// Add a small delay before starting polling to ensure player is ready on Android
const timeoutId = setTimeout(() => {
updateQueueStatus(); // Initial update
pollInterval = setInterval(updateQueueStatus, 1000);
}, 100);
return () => {
clearTimeout(timeoutId);
if (pollInterval) clearInterval(pollInterval);
scrollGuard.cleanup();
};
});
@@ -64,26 +54,6 @@
}
});
async function updateQueueStatus() {
try {
const queue = await commands.playerGetQueue();
// Reset failure counter on success
failedAttempts = 0;
hasNext = queue.hasNext;
hasPrevious = queue.hasPrevious;
shuffle = queue.shuffle;
repeat = queue.repeat as "off" | "all" | "one";
} catch (e) {
failedAttempts++;
// Only log errors after initial attempts (player may still be initializing)
if (failedAttempts > MAX_SILENT_FAILURES) {
console.error("[Queue Status] Error:", e);
}
}
}
async function handleLogout() {
await auth.logout();
library.reset();