Commit Graph

12 Commits

Author SHA1 Message Date
cucadmuh ecdbe0cf2a fix(player): stale cover blob and load state on track change (#695)
* fix(player): align cached cover URL with cacheKey on track change

Prevents a one-frame stale blob src (and broken image in the player bar)
when switching tracks; reset CachedImage load state in useLayoutEffect.

* docs: changelog + credits for cover-art track-switch fix (PR #695)
2026-05-14 21:38:56 +03:00
cucadmuh d48ea819c1 fix: stabilize preview seekbar, post-sleep audio recovery, and card hover behavior (#476)
* fix(player): freeze main seekbar during track preview

Preview pauses the main sink in Rust while isPlaying stays true in the
store, so WaveformSeek's interpolation rAF must not advance progress.

* fix(audio): recover output after sleep and stalled streams

Add platform-specific post-sleep recovery hooks for Windows and Linux, and add a watchdog that reopens the output stream when playback is active but sample progress stalls, so audio can recover without restarting the app.

* fix(ui): remove card hover lift and smooth artwork zoom

Remove vertical hover translation from album and artist cards, and move image fade transition out of inline styles so cover zoom uses CSS timing consistently.

* fix(player): prevent seekbar jump after preview ends

Reset interpolation anchor timing when preview freeze state changes so the main seekbar does not momentarily jump forward before resyncing.

* fix(audio): reduce false watchdog recoveries and add diagnostics

Arm stalled-output recovery only after long poll gaps that suggest sleep/resume, and add detailed watcher logs for arm/clear/trigger paths to diagnose unintended stream reopens.

* chore(ui): drop card GPU hints and clarify macOS sleep scope

Remove translateZ and will-change hints from album and artist cover images to avoid per-card compositing overhead on software-composited Linux paths, and document why post-sleep recovery hooks currently target only Windows and Linux.

* docs(audio): document intentional Win32 callback pointer lifetime

Add inline rationale for the two Box::into_raw pointers in Windows suspend/resume registration so future maintenance does not treat the process-lifetime pointers as accidental leaks.

* docs(changelog): summarize playback stability updates for PR #476

Add a high-level changelog entry for preview seekbar fixes, sleep/wake audio recovery hooks and watchdog diagnostics, and card-hover stability adjustments from PR #476.

* docs(contributors): add cucadmuh entry for PR #476

Logs the post-sleep audio recovery, preview-seekbar fixes and card
hover stability work in the Settings → System → Contributors list.
2026-05-06 13:28:38 +03:00
cucadmuh c3d37546cf Feat/search improvements (#470)
* feat(covers): race sibling downscale vs fetch, search thumb priorities

Run getCoverArt and client downscale in parallel when another size of the
same cover is cached; first successful result wins and aborts the other path.
Await both branches so inflight bookkeeping does not detach early.

Extend the cover cache size roster so provisional siblings resolve for sizes
used in the UI (e.g. 400/600/800, 48/96).

CachedImage: fetchQueueBias for live/mobile search (artist thumbnails ahead of
albums in fetch-slot ordering); configurable observeRootMargin with a wider
default to prepare priority slightly before elements enter view.

Mobile search adds round artist-thumb styling; add shared cover blob downscale
helper.

* perf(image-cache): batch sibling IDB reads and guard cover size registry

Use one read transaction when probing IndexedDB for sibling cover keys.
Extract COVER_ART_REGISTERED_SIZES and add Vitest coverage so every literal
coverArtCacheKey(_, size) in src stays aligned with sibling invalidation.
Honor AbortSignal during JPEG encode in downscaleCoverBlob.
2026-05-06 01:26:30 +03:00
cucadmuh 9d30285ff1 Perf/UI cover cache mainstage (#468)
* Enhance CachedImage and ArtistDetail components with improved image caching and priority handling

- Refactor CachedImage to utilize a priority system for image loading based on viewport visibility, improving performance during scrolling.
- Update useCachedUrl to accept an optional getPriority function for better cache management.
- Optimize ArtistDetail and Artists components by using useMemo for cover art URLs, reducing redundant calculations and improving rendering efficiency.
- Adjust image loading logic in CachedImage to ensure smoother transitions and avoid unnecessary fetch requests.

* perf(ui): unblock IDB cover art, stabilize mainstage rails and virtual lists

Let IndexedDB reads bypass the network concurrency slot so cached thumbnails
paint without queueing behind remote fetches; debounce disk eviction during
heavy scrolling.

Fix mainstage horizontal rails: dedupe album/song ids for React keys, widen
artwork budget overscan, avoid resetting the budget on list append, and raise
Home initial artwork budgets. CachedImage treats already-decoded images as
loaded; rail cards load cover images eagerly.

Refresh dynamic color extraction and extend virtual scrolling / scroll roots on
Albums, Artists, Playlists, and related surfaces.


Remove local agent-only commit instructions from the repository tree.

* perf(virtual): viewport-based overscan for main scroll lists

Drive TanStack Virtual overscan from measured scroll height so each list
renders about one screen of extra rows above and below the viewport for
snappier scrolling on Albums, Artists (list mode), and Tracks virtual song list.

Introduce useResizeClientHeight helpers (ID + ref) for ResizeObserver-based
clientHeight tracking.

* docs(changelog): note PR #468 UI cover cache, rails, and virtual lists

Add a coarse summary under 1.46.0 Changed for cover-art pipeline,
mainstage rails, viewport-based overscan, and library/chrome polish.
2026-05-06 00:15:58 +03:00
Frank Stellmacher c4a283b809 fix(imageCache): share refcounted blob URLs across consumers (Windows perf) (#321)
The previous design handed every <img> its own URL.createObjectURL for the
same cached Blob. WebKitGTK shrugged it off, but Chromium/WebView2 keys
its decoded-image cache by URL — so identical thumbnails were re-decoded
once per instance. On Windows this made cover/artist grids painfully slow
even when blobs were warm in memory.

Refactor the URL layer to be refcounted and shared:

- New acquireUrl(cacheKey) / releaseUrl(cacheKey) API. First acquire
  creates the URL; subsequent acquires return the same string and bump
  the refcount. Revoke is deferred 500 ms after the count hits zero so
  in-flight decodes finish cleanly.
- useCachedUrl uses a lazy useState initializer: when the blob is hot,
  the very first <img src> is already the blob URL. No fetchUrl→blobUrl
  swap, no decode thrash, no race against the LRU.
- CachedImage passes fallbackToFetch=false: previously the <img>
  briefly carried the raw server URL while the blob resolved, which
  triggered an HTTP fetch that the browser then aborted when src
  flipped to blob: — visible in DevTools as a flood of "Pending / 0 B"
  requests. Memory hits remain instant via the synchronous acquire
  path; cold paths now do a single fetch via getCachedBlob.
- invalidateCacheKey / clearImageCache now also purge URL entries.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 14:17:12 +02:00
Psychotoxical ab1b1dcffa fix(imageCache): give every cached <img> its own object URL
The previous design kept a single global Map<cacheKey, objectURL> with an
LRU cap of 150 and aggressively revoked the oldest URL on overflow. On
libraries with more than 150 cached covers (artist + album grids quickly
exceed that), an in-use URL would get revoked because a different
consumer pushed it out of the cache, producing the "Failed to load
resource: blob:..." flood that several users have reported.

Refactor the cache to be blob-centric:

- Public API is now getCachedBlob() returning the Blob itself.
- In-memory LRU now holds Blobs (cap 200), not URLs. Map-entry eviction
  drops the strong reference and lets the GC free the Blob once no
  consumer (object URL, <img>, <canvas>) still holds it. No revoke choreography needed.
- useCachedUrl creates its own URL.createObjectURL on blob arrival and
  revokes it on cleanup with a 500 ms grace delay so the DOM <img> has
  time to finish decoding the URL we just took away.
- All existing callers keep their string-returning API; no call-site
  changes outside the hook itself.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 01:32:17 +02:00
Psychotoxical 0119e27f6d feat(covers): logo fallback for broken cover art images
CachedImage now shows /logo-psysonic.png when an image fails to load
instead of a broken icon. The fallback switches to object-fit: contain
with a card background so the logo stays proportional inside the container.

If the caller provides a custom onError (e.g. to hide the element), that
handler is used instead. A separate fallbackSrc state prevents React from
re-writing the broken URL on re-renders. The DOM-level onerror = null
guard prevents any infinite error loop.

extractCoverColors skips color extraction when the logo fallback is
active, falling back to the theme accent color instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:25:25 +02:00
Psychotoxical c365140870 feat: v1.30.0 — Discord RPC, offline bulk download, artist images, lazy loading, crossfade fix
- Discord Rich Presence (opt-in) — requested by @Bewenben (#49)
- Bulk offline download for playlists and artist discographies — requested by @Apollosport (#54)
- Offline Library filter tabs: All / Albums / Playlists / Discographies with artist grouping
- Artist images on Artists overview (opt-in, off by default) — reported by @Apollosport (#53)
- Image lazy loading via IntersectionObserver (300px margin) across all pages
- Fix: crossfade no longer triggers on manual track skip — reported by @netherguy4 (#35)
- Fix: playlist offline cache now stored as single entry (not per-album)
- Fix: image cache AbortController no longer blocks IDB writes
- Update toast: experimental auto-update hint + GH download link always visible (Win/Mac)
- Queue tech strip: genre removed
- Facebook theme: contrast, opaque badge/back button, queue tab labels
- "Save discography offline" label (was "Download discography")
- Fix: clearing empty playlists via updatePlaylist.view (Axios empty array workaround)
- starredOverrides propagated to AlbumDetail, Favorites, RandomMix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 14:53:41 +02:00
Psychotoxical 434ee0ecf1 feat: v1.26.0 — Bulk Select, Song Info, Favorite Button, Recently Played
### Added
- Favorite/Star button in player bar (requested by @halfkey)
- Bulk multi-select for album tracklist and playlist detail (add to playlist, remove from playlist)
- Song Info modal via right-click context menu (metadata: format, bitrate, sample rate, bit depth, channels, file size, path, replay gain)
- Recently Played section on Home page
- "Show activity in Now Playing" opt-in toggle in Settings → Behavior

### Fixed
- Queue cover art not updating on track change (useCachedUrl/CachedImage reset on cacheKey change)
- FullscreenPlayer background flickering (FsBg preloads image before layer transition)
- Playlist card delete confirmation visual (size expansion + pulse animation)
- Gruvbox Light Soft: back button and badge invisible against light background

### Changed
- buildStreamUrl: removed unused suffix parameter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:57:57 +02:00
Psychotoxical b6fb66c46d feat: v1.19.0 — NSIS Installer, Tray Removed, Storage Warning, Theme Polish
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 23:56:35 +01:00
Psychotoxical af18aef42a fix: random albums performance, image cache memory leak, i18n fix (v1.4.1)
- Remove auto-refresh timer from Random Albums (caused 100ms progress
  interval + 30 concurrent fetches every 30s, eventually freezing the app)
- Limit concurrent image fetches to 5 (was unbounded)
- Cap in-memory object URL cache at 150 entries with revokeObjectURL eviction
- Add cancellation flag to useCachedUrl to prevent setState on unmounted components
- Fix hardcoded "Neueste" page title in New Releases (now uses i18n)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 19:09:31 +01:00
Psychotoxical f8c45efd2b feat: IndexedDB image caching, initial-avatars, and discovery improvements (v1.0.5) 2026-03-12 22:28:25 +01:00