PlaylistCard: per-artist system variants (songs_like_artist) all share one variant tag but exist as one playlist per seed artist; routing their play handler through systemShuffle(variant) hit the wrong playlist (or 404). Detect via seed_artist_id != null and fall through to getPlaylist(playlist.id) for those; still tag the queue with the variant so source attribution stays correct. smoothPosition.svelte.ts: new useSmoothPosition() hook mirrors Android's rememberSmoothPositionMs. player.position only updates ~4 Hz (HTML audio timeupdate), so the seek thumb stepped visibly; $effect resets on each canonical tick / seek / track-change and a rAF loop extrapolates at playback rate between ticks. Wired into both PlayerBar.svelte (mini + expanded seek rows) and now-playing/+page.svelte. Seek input handler still reads the raw range value (not smoothed.value) so user drags stay authoritative.
This commit is contained in:
@@ -14,11 +14,17 @@
|
||||
import { formatDuration } from '$lib/media/duration';
|
||||
import { FALLBACK_COVER, coverUrl } from '$lib/media/covers';
|
||||
import { dominantColorFromUrl, rgbToCssString } from '$lib/media/dominantColor';
|
||||
import { useSmoothPosition } from '$lib/player/smoothPosition.svelte';
|
||||
import LikeButton from './LikeButton.svelte';
|
||||
import TrackMenu from './TrackMenu.svelte';
|
||||
|
||||
const current = $derived(player.current);
|
||||
|
||||
// Per-frame interpolated playhead so the scrubber bar moves
|
||||
// smoothly between server ticks instead of stepping every ~250ms.
|
||||
// Reset on track/play-state change via the helper's $effect.
|
||||
const smoothed = useSmoothPosition();
|
||||
|
||||
const skipPrevDisabled = $derived(player.index === 0 && player.position < 3);
|
||||
const skipNextDisabled = $derived(
|
||||
player.index === player.queue.length - 1 && player.repeat === 'off'
|
||||
@@ -226,10 +232,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom row: full-width seek slider + time labels -->
|
||||
<!-- Bottom row: full-width seek slider + time labels.
|
||||
Reads the smoothed position so the slider thumb glides
|
||||
between server ticks at playback rate. -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-9 shrink-0 text-right text-[10px] tabular-nums text-text-secondary">
|
||||
{formatDuration(player.position)}
|
||||
{formatDuration(smoothed.value)}
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
@@ -237,7 +245,7 @@
|
||||
min="0"
|
||||
max={player.duration || 0}
|
||||
step="0.1"
|
||||
value={player.position}
|
||||
value={smoothed.value}
|
||||
oninput={onSeekInput}
|
||||
class="flex-1 accent-accent"
|
||||
/>
|
||||
@@ -346,10 +354,11 @@
|
||||
<SkipForward size={20} strokeWidth={1.5} fill="currentColor" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Seek row -->
|
||||
<!-- Seek row — reads smoothed position so the thumb glides
|
||||
between server ticks. See useSmoothPosition. -->
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-12 shrink-0 text-right text-xs tabular-nums text-text-secondary">
|
||||
{formatDuration(player.position)}
|
||||
{formatDuration(smoothed.value)}
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
@@ -357,7 +366,7 @@
|
||||
min="0"
|
||||
max={player.duration || 0}
|
||||
step="0.1"
|
||||
value={player.position}
|
||||
value={smoothed.value}
|
||||
oninput={onSeekInput}
|
||||
class="flex-1 accent-accent"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user