Skip to content

Performance pass, local files, playback fixes - #45

Merged
noFAYZ merged 16 commits into
mainfrom
dev
Aug 7, 2026
Merged

Performance pass, local files, playback fixes#45
noFAYZ merged 16 commits into
mainfrom
dev

Conversation

@noFAYZ

@noFAYZ noFAYZ commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Perf

  • Per-effect render switches under Settings > Potato PC > Manage; Potato PC is now the opaque palette plus all switches off
  • Honour prefers-reduced-motion in CSS; mini player window gets the theme/effect/motion settings it never had
  • TiltCard only composites the hovered card; custom scrollbar writes thumb geometry to the DOM instead of re-rendering Layout per scroll event
  • Gate always-on timers (mini-player sync, Last.fm, session heartbeat, media session, sleep timer, seek bar)
  • Volume drag no longer writes 4 log files and a durable settings write per pointer move
  • Large blur filters replaced with gradients where the result is identical

Playback

  • Don't advance the queue when a stream dies early; reload from a fresh URL instead
  • Refuse a preloaded deck whose download failed, so nothing swaps into a few seconds of audio
  • Fix: partial savePlaybackSettings was resetting crossfade, playback rate and gapless

Local files

  • Scan reads real tags (title, artist, album, duration) and embedded cover art
  • Tag editor supports cover art: add, replace, remove
  • Folder button on the local playlist page; per-playlist cover images
  • Fix: removing a song from a folder-backed local playlist did nothing

macOS

  • Set MPNowPlayingInfoCenter.playbackState so media keys reach the app; register togglePlayPause and the Control Center scrubber

UI

  • Library page redesign, artist page "show all songs", header play button, Home destination stamps
  • Fix: right-clicking a Made for you card played it

Not verified by ear or on macOS/Linux hardware.

noFAYZ and others added 16 commits August 2, 2026 16:12
The mini player autoplayed on load, and when the browser refused it fell back
to starting on the first pointerdown or keydown anywhere on the document — so
clicking a download link played music from a control the visitor may not have
noticed.

Now silent until the play button is pressed. preload dropped to metadata, so
nothing but the duration is fetched until someone asks to hear it.
- Per-effect render switches (Settings > Potato PC > Manage), one `data-fx-off` attribute driving CSS
- Potato PC is now the opaque palette plus all switches off; its duplicate `!important` block is gone
- Honour `prefers-reduced-motion` in CSS; add `useReduceMotion` since Motion's own hook ignores MotionConfig
- Mini player window now gets the theme, effect and motion settings it never had
- TiltCard only composites the hovered card (was `will-change` + preserve-3d + glare on every album card)
- Custom scrollbar writes thumb geometry to the DOM instead of re-rendering Layout on every scroll event
- `useHoverCapable` is one module-level matchMedia, not one per card
- content-visibility on AlbumCard, `decoding="async"` on artwork, drop no-op backdrop filters
- Volume drag no longer writes 4 log files and a durable settings write per pointer move
- Fix: partial `savePlaybackSettings` was resetting crossfade, playback rate and gapless
- Gate always-on timers (mini-player sync, Last.fm, session heartbeat, media session, sleep timer, seek bar)
- Add a 400 artwork bucket: 176-200px cards at 2x were rounding up to 544 (1.18MB vs 0.64MB each)
- Add `memory.sample` logging so renderer RAM can be split into heap, DOM, images and subframes

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Loading and onboarding glows were 420px discs under `blur-[120px]`: a compositor layer plus an intermediate texture ~3x the radius on each side. Now radial gradients, drawn in the raster pass.
- Lyrics backdrop blur 70px -> 32px. It is a ~2600x1470 layer; the 120px source is already upscaled ~20x, so the filter was adding ~3.5 source pixels of softness for most of the cost. 32px matches Layout's tuning at the same upscale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A preloaded deck can end up holding only its first chunk: googlevideo
refuses individual byte ranges, `fill_media_buffer` falls back to a
whole-file fetch, and gives up if that is refused too. Nothing invalidated
the deck, so the gapless swap handed over to a few seconds of audio and the
engine reported "ended" — indistinguishable from a finish, so the queue
advanced. Symptom: every track plays for a few seconds and skips.

- `isPrematureEnd` compares position against duration, allowing for crossfade
- On a premature end the track reloads from a fresh stream URL and resumes at
  the failure point instead of advancing; one retry, then it moves on

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Root cause behind the previous commit. A deck is reported loaded as soon as
the decoder reads the container header out of the head chunk, while the rest
of the file is still arriving. When `fill_media_buffer` then gives up, the
deck still holds a real source with a real duration and nothing looks wrong
until it is played and stops after a few seconds.

- Deck carries an optional `DeckHealth` probe over its `MediaBuffer`
- `HasStandby` and `Transition` refuse and clear an unhealthy standby, so the
  caller falls through to a normal load and resolves a fresh stream URL
- Offline and local files pass `None`; their bytes cannot fail mid-playback

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… the dead URL

The guard shipped in 5a028ac never triggered. `ended` clears the engine's
track id before the callback runs, so `getDuration()` answered 0 and every
premature end read as a finish.

The retry also reused the warmed stream slot, which holds the same signed URL
the failed deck was built from — the log shows both attempts fetching the
identical `expire=` URL and both getting 403.

- Snapshot position and duration at `ended`, before the id is cleared
- Discard the warmed stream on a premature end and on a refused transition, so
  the reload re-resolves instead of replaying a dead URL

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Set MPNowPlayingInfoCenter.playbackState. macOS picks the Now Playing app
  from it, and without it the keys went elsewhere and no handler ever fired.
  This is why next/previous failed too, not just play/pause.
- Register togglePlayPauseCommand, which is what the Play/Pause key, the Touch
  Bar and headphone buttons actually send. The frontend already handled the
  "playPause" payload.
- Register changePlaybackPositionCommand for the Control Center scrubber; the
  frontend already handled the seekTo payload but nothing emitted it.

Not compiled or tested: needs a macOS toolchain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The carousel preventDefaults pointerdown, so PickCard hand-rolls tap
detection from pointerdown/pointerup. Both fire for the right button, so a
context-menu press counted as a tap.

- PickCard: only the primary button starts or completes a tap
- CylinderCarousel: only the primary button starts a drag, so a right-click no
  longer captures the pointer and leaves it stuck mid-drag

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Icon 20px -> 64px, absolute at the left edge, cropped by the card
- Drop the rounded chip, its ring and its tinted background
- Drop the hover blur glow; the stamp brightens on hover instead, which is one
  fewer blurred compositor layer per card

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- strokeWidth 2.25 at 48px. 1.5 is tuned for a 20px glyph; unscaled it reads as
  wire at display size.
- 48px, cropped left only. 64px in a ~60px card clipped on three sides, which
  reads as an accident rather than a decision.
- Live counts get foreground weight and tabular-nums; static hints stay muted.
  They were rendered identically, so the count read as description and the
  download percentage jittered as it climbed.
- Drop the hover ring; the background shift and the stamp carry it, and the
  system separates surfaces by contrast rather than outlines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
zuno Ready Ready Preview Aug 7, 2026 11:51am

@noFAYZ
noFAYZ merged commit 51af073 into main Aug 7, 2026
9 of 10 checks passed
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.

1 participant