Commit Graph

67 Commits

Author SHA1 Message Date
Psychotoxical c49f6af38b perf: reduce CPU usage and unify next-track buffering settings
- Throttle audio:progress from 100ms to 500ms; WaveformSeek and
  FsSeekbar use imperative DOM updates instead of React re-renders
- Fix all usePlayerStore() calls without selectors across pages and
  components (useShallow / individual selectors throughout)
- Remove filter:blur and transform:scale from Hero, AlbumDetail and
  FullscreenPlayer backgrounds — eliminated expensive software
  compositing layers on WebKitGTK
- Replace translate3d with 2D translate in FS mesh and portrait
  animations; remove will-change:transform from mesh blobs
- Move Hot Cache section from Storage tab to Audio tab; group Preload
  and Hot Cache under a shared 'Next Track Buffering' section with a
  mutual-exclusivity note and auto-disable logic
- Add 'off' toggle to Preload (replaces Off button with toggle switch);
  enabling either method now automatically disables the other

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:38:33 +02:00
Psychotoxical 74985fe331 fix(audio): fall back to track gain when album gain is missing
In album gain mode, tracks without albumGain tags received no
ReplayGain adjustment at all. Now falls back to trackGain before
defaulting to 0 dB — fixes inconsistent loudness across albums
where only some tracks have albumGain metadata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 01:12:06 +02:00
Psychotoxical 5f0fb5dcbd feat(audio): auto-switch to new audio output device at runtime
Adds a background device-watcher that polls the OS default output device
every 3 s via CPAL. When the device changes (Bluetooth headphones connecting,
USB DAC plugging in, etc.) the stream is reopened on the new device, the old
Sink is dropped (it was bound to the now-closed OutputStream), and
audio:device-changed is emitted to the frontend.

Frontend handler (TauriEventBridge): if playing, restarts the current track
from the saved position; if paused, clears the warm-pause flag so the next
resume uses the cold path (audio_play + seek) which creates a new Sink on
the new device.

Fixes #143 (audio through speakers after connecting Bluetooth headphones).
Also covers the intermittently reported single-channel output after long idle,
which can be caused by the OS reconfiguring the audio device in the background.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:40:12 +02:00
Psychotoxical 20bf93c344 feat(windows): taskbar thumbnail toolbar with GDI media icons
Implements ITaskbarList3::ThumbBarAddButtons for Prev/Play-Pause/Next
buttons in the Windows taskbar thumbnail preview. Icons are drawn at
runtime via GDI (no binary assets). WndProc subclass intercepts
THBN_CLICKED and emits the same media:* events as souvlaki/tray.
update_taskbar_icon command swaps Play↔Pause icon on state change.
Frontend syncs via playerStore subscribe alongside mpris_set_playback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:12:35 +02:00
Psychotoxical 0da92c2fa1 fix(radio): prevent macOS WKWebView crash + shuffle Artist Radio queue
- Remove currentTime from Zustand persist partialize: it caused 100ms
  localStorage writes that grew with the queue, crashing WKWebView SQLite
  after ~10 min of Artist Radio on macOS
- Trim old played radio tracks from queue (keep last 5) to cap localStorage
  payload size during proactive radio top-up in next()
- Add reconnect logic for internet radio stall events (max 5 retries)
- Shuffle initial Artist Radio queue via Fisher-Yates so positions 2+
  are drawn from similar-artist tracks, not predictable top-5 tracks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 13:23:48 +02:00
Psychotoxical ba670bd1e8 feat: fix UI freezes in ZIP and offline cache downloads
ZIP downloads (PlaylistDetail, Albums, NewReleases, RandomAlbums) now use
invoke('download_zip') via Rust streaming instead of fetch+blob+arrayBuffer,
eliminating JS heap saturation on large files. Progress shown in ZipDownloadOverlay.

Offline cache downloads (600+ songs) no longer freeze the UI: transient job
state moved to a separate non-persisted offlineJobStore, reducing localStorage
writes from ~1200 down to 2 for an entire download. Also adds playlist offline
toggle — clicking the cache button when already cached removes it from cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 22:33:23 +02:00
Psychotoxical 3643a78cd6 perf: drastically reduce background API requests to Navidrome (v1.34.5)
- NowPlaying polling: only active while dropdown is open + respects
  Page Visibility API — was firing every 10s unconditionally (~8.6k req/day)
- Connection check interval: 30s → 120s (4× reduction)
- Queue sync debounce: 1.5s → 5s (prevents burst on rapid skip)
- Rating prefetch: add 7-minute in-memory TTL cache for artist/album
  ratings — repeated random album loads no longer re-fetch known ratings

Fixes server log flooding reported by users behind reverse proxies (Traefik).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 18:04:03 +02:00
Psychotoxical 5848c621fd feat(folder-browser): Miller columns directory navigation
New page /folders with macOS Finder-style column layout:
- getMusicDirectory / getMusicIndexes / getMusicFolders Subsonic API methods
- Root level uses getIndexes.view (music folder IDs are not getMusicDirectory IDs)
- Columns fill available width with flex: 1, min-width: 200px
- Auto-scroll to rightmost column on expand
- Cancelled-flag prevents stale state on fast navigation
- Clicking a track plays it in the context of the current column's tracks
- FolderOpen/Folder/Music icons, accent-colored selection, ellipsis truncation
- Hidden in sidebar by default (user can enable in Settings)
- i18n: EN DE FR NL NB ZH RU

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 14:40:29 +02:00
Psychotoxical e64d151079 feat(settings): theme scheduler, UI scale, CustomSelect scroll fix
- Time-based theme scheduler: auto-switches between day/night theme
  based on configurable times; setInterval re-checks every minute
- UI scale slider (80–150%) via CSS zoom on document root; preset
  buttons aligned to actual slider positions
- CustomSelect: scroll listener keeps dropdown anchored on scroll
- All features i18n in 7 languages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:23:24 +02:00
Psychotoxical 737b057d4a feat(seekbar+ratings): 5 new seekbar styles and entity/mix rating system (PR #130)
Seekbar styles (5 new, by Psychotoxical):
- Neon Glow: transparent track, played section as multi-layer glowing neon tube
- Pulse Wave: flat line with animated gaussian sine pulse at playhead (rAF)
- Particle Trail: particles spawn at playhead and drift with glow (rAF)
- Liquid Fill: glass tube fills with liquid and animated wave surface (rAF)
- Retro Tape: two spinning reels connected by tape, shrink/grow with progress (rAF)

Ratings system (PR #130 by cucadmuh):
- Shared StarRating component with pulse/clear animations, disabled/locked states
- Entity ratings for albums and artists via OpenSubsonic probe (setEntityRatingSupport)
- Skip→1★: after N consecutive manual skips, set unrated track to 1★ (persisted per server)
- Mix rating filter: exclude low-rated songs/albums/artists from RandomMix, RandomAlbums, Hero, Home
- Batch refill in fetchRandomMixSongsUntilFull to fill list despite strict filters
- Prefetch artist/album ratings via getArtist/getAlbum for incomplete list payloads
- Settings: new Ratings section for skip threshold and per-axis mix filter stars
- i18n: all 7 languages (en, de, fr, nl, zh, nb, ru)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:29:21 +02:00
Maxim Isaev 0c2ac13fed merge: integrate origin/main into feat/improve-rating-ux
Resolve conflicts in RandomAlbums (keep mix rating filter with batch ZIP/offline
selection from upstream) and Settings (SeekbarPreview + mix filter constants).
2026-04-08 05:04:12 +03:00
Maxim Isaev 1624880c2a feat(ratings): mix cutoff filter for random flows and home, i18n
Apply the same per-axis star cutoff to random mix (multi-batch fetch until
full), random albums, Hero, and Home hero/discover rows. Prefetch artist and
album ratings via Subsonic when list payloads omit them. Exclude items when
0 < rating ≤ threshold; 0 or missing counts as unrated.

Settings: rating filter description interpolates sidebar menu labels; Russian
strings for custom title bar; short axis labels aligned across locales.
2026-04-08 04:57:26 +03:00
Maxim Isaev 8a1d942128 feat(ratings): skip-to-1★ threshold and mix min-rating filter
Add Ratings section under General: manual skip count before setting 1★,
and per-axis minimum stars for random mixes/albums (song, album, artist).
StarRating shows five stars with selection capped at three; shared max in authStore.
Queue filtering via passesMixMinRatings; OpenSubsonic-style entity rating fields on types.
2026-04-08 02:39:25 +03:00
Psychotoxical 829936a78d feat(seekbar): configurable seekbar styles with animated preview
Add five seekbar styles selectable in Settings → Appearance:
Waveform, Line & Dot, Bar, Thick Bar, and Segmented. Each option
shows an animated canvas preview using requestAnimationFrame.

Style is persisted in authStore (seekbarStyle, default: waveform).
Translations added for all 7 languages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 01:39:08 +02:00
Maxim Isaev 705c80ef07 feat(ratings): Subsonic entity ratings and StarRating UX
Probe OpenSubsonic after connect; persist album/artist ratings via setRating when supported. Add shared StarRating (repeat same star to clear, pulse and shrink animations, hover freeze after clear). Widen tracklist duration column and bump narrow saved widths in localStorage. Use StarRating in AlbumHeader, track lists, and playlist rows.
2026-04-08 01:09:17 +03:00
Psychotoxical 45fa606ae1 feat(titlebar): make custom title bar optional via Settings toggle
- New authStore field useCustomTitlebar (default: true)
- set_window_decorations Tauri command toggles native decorations at runtime
- Settings toggle visible only on Linux (no restart required)
- i18n keys added to EN + DE

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 21:47:29 +02:00
Psychotoxical a606d1edd6 feat(themes): add Dracula theme to Open Source Classics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 20:35:48 +02:00
Psychotoxical 9ef8566d64 fix(discord): add Apple Music cover opt-in, show Paused state
- iTunes Search API calls are now gated behind enableAppleMusicCoversDiscord
  (default: false). No external requests to Apple are made unless the user
  explicitly enables the new opt-in toggle in Settings.
- When the toggle is flipped, Discord presence is immediately re-sent for
  the current track (coversSettingChanged bypasses the early-return guard).
- When paused, activity type switches to Playing (no auto-timer) and state
  text shows "Paused" instead of the artist name.
- New authStore field + setter; new Settings toggle shown only when Discord
  RPC is enabled; i18n keys added to all 7 languages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 20:33:16 +02:00
Psychotoxical 4b8b6ae797 feat(most-played): add dedicated Most Played page (#86)
New sidebar item (TrendingUp icon, /most-played route) with:

- Top Artists section: derived by grouping frequent albums by artistId
  and summing playCount — no extra API calls needed. Shows up to 10
  artists as a responsive grid with avatar, rank and total play count.
- Top Albums section: paginated list using getAlbumList('frequent'),
  50 albums per batch with a "Load more" button. Each row shows rank,
  cover thumbnail (44px), album name, artist (navigates to artist
  page), year and play count.
- Sort toggle (most plays first / fewest plays first).

Also adds playCount?: number to SubsonicAlbum — Navidrome returns this
field in getAlbumList2?type=frequent but it was missing from the type.

Closes #86

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 20:00:07 +02:00
Psychotoxical 70f0641439 feat(subsonic): per-server music folder filter and sidebar picker (#125)
Adds library scoping per server: users can select one Navidrome music
folder or 'All' from a new sidebar dropdown. Choice is persisted per
server ID in localStorage. A musicLibraryFilterVersion counter triggers
refetches across browsing pages when the scope changes.

- getMusicFolders() + libraryFilterParams() in subsonic.ts
- New fields in authStore: musicFolders, musicLibraryFilterByServer,
  musicLibraryFilterVersion, setMusicFolders, setMusicLibraryFilter
- Sidebar dropdown via createPortal; collapses to icon in narrow mode
- API scoping applied to: getAlbumList2, getRandomSongs, getArtists,
  getStarred2, search3
- Full i18n coverage (en, de, fr, nl, zh, nb, ru)

Review fixes applied (co-authored):
- Sidebar: title={} → data-tooltip + data-tooltip-pos='right'
- authStore: setMusicFolders merged into single set() call
- Locales: removed dead libraryScopeHint key from all 7 files
- Genres.tsx: removed musicLibraryFilterVersion dep (getGenres unscoped)

Co-authored-by: cucadmuh <cucadmuh@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:54:57 +02:00
Psychotoxical c779e8f587 fix(pr-review): address review findings for music folder filter
- Sidebar.tsx: title={} → data-tooltip + data-tooltip-pos="right" on
  scope subtitle (CLAUDE.md rule: never use native title= for tooltips)

- authStore.ts: merge two set() calls in setMusicFolders into one atomic
  update — avoids double render on folder list arrival

- locales (all 7): remove dead libraryScopeHint key (defined but never
  rendered in any component)

- Genres.tsx: remove musicLibraryFilterVersion from effect deps —
  getGenres is intentionally not folder-scoped, re-fetching on filter
  change was a no-op waste

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:53:44 +02:00
Maxim Isaev f08619fb3d feat(subsonic): per-server music folder filter and sidebar picker
Apply musicFolderId across Subsonic requests, bump a filter version so library views reload, and add a fixed-position sidebar dropdown (with capped height when there are many folders).
2026-04-07 13:59:59 +03:00
Psychotoxical 44287a7ceb feat(audio): bit-perfect hi-res playback + underrun hardening (opt-in alpha)
Safe mode (default): all audio outputs at 44.1 kHz, rodio resamples
internally. Maximum stability out of the box.

Hi-Res mode (alpha toggle): stream opens at the file's native sample rate
(e.g. 88.2/96/192 kHz) for bit-perfect output.

Rust (audio.rs):
- open_stream_for_rate(): CPAL queries device supported configs, finds
  exact rate match or falls back to highest available / device default.
- create_engine() thread: ThreadPriority::Max (silently ignored without
  CAP_SYS_NICE), loop handles rate-switch requests, PIPEWIRE_LATENCY
  scales with rate (8192 frames for >48 kHz ≈ 93 ms quantum), keeps
  PULSE_LATENCY_MSEC in sync.
- audio_play(): hi_res_enabled param gates effective_rate (44100 vs
  native); stream re-opens only when needed; 150 ms PipeWire settle +
  500 ms sink pre-fill (pause→append→sleep→play) for hi-res tracks.
- audio_chain_preload(): hi_res_enabled param; rate-mismatch bail skipped
  in safe mode so gapless chains always work at 44.1 kHz.
- SizedDecoder MSS buffer: 4 MB (was 512 KB) to reduce Symphonia read
  overhead on high-bitrate hi-res files.
- thread-priority crate added to Cargo.toml.

Frontend:
- authStore: enableHiRes (bool, default false) + setEnableHiRes.
- playerStore: hiResEnabled passed to all audio_play /
  audio_chain_preload invoke calls.
- Settings → Audio tab: "Native Hi-Res Playback" toggle with Alpha
  badge and stability warning, i18n for all 7 languages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 12:20:07 +02:00
cucadmuh 0f3033d84e feat: add hot playback cache (queue prefetch, alpha)
Ephemeral on-disk cache for upcoming queue tracks. Adds Rust commands (download/delete/purge), hotCacheStore with LRU eviction, serial prefetch worker, playback gate, and Settings UI. Disabled by default.

- fix: boundary check before file delete in delete_hot_cache_track
- fix: remove stale languageRu2 key from ru.ts
- fix: restore accidentally removed lyricsServerFirst/fsLyricsToggle/lyricsSource* strings in ru.ts
2026-04-07 10:52:26 +02:00
Psychotoxical d73f348339 feat: v1.34.1 — fullscreen lyrics overlay, FsArt crossfade, lyrics toggle, contributor updates
- FullscreenPlayer: synced lyrics overlay (5-line rail, mask fade, click-to-seek)
- FullscreenPlayer: FsArt crossfade component with onLoad pattern, 300px art, next-track pre-fetch
- FullscreenPlayer: MicVocal lyrics toggle button next to heart (persisted in authStore)
- FullscreenPlayer: idle system — only close button auto-hides, cluster+seekbar always visible
- LyricsPane: refactored to use shared useLyrics hook (no double-fetch with FS overlay)
- authStore: showFullscreenLyrics field (default true)
- i18n: fsLyricsToggle key added to all 7 locales; ru2.ts removed (merged into ru.ts)
- Settings: Contributors updated (nisrael, cucadmuh, kilyabin); logout moved to Server tab as btn-danger
- theme.css: btn-danger style added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:00:33 +02:00
Psychotoxical 1197c1f916 feat: album cover art in Discord Rich Presence via iTunes Search API
Fetches album artwork from the iTunes Search API and passes the URL
directly to Discord's large_image field. Subsonic cover URLs require
auth and can't be used by Discord directly.

- 3-strategy search: exact quoted → relaxed → track title fallback
- 1-hour in-memory cache per artist|album key
- iTunes search runs in tokio::task::spawn_blocking (reqwest::blocking
  must not run on the Tokio async executor)
- artwork_cache wrapped in Arc<Mutex<...>> for cross-thread sharing
- Fallback to the pre-uploaded "psysonic" asset when no match found
- Cargo.toml: blocking feature alongside rustls-tls

Co-Authored-By: kilyabin <kilyabin@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:30:22 +02:00
Nils Israel 64e0948904 feat: integrate nightfox.nvim themes into Open Source Classics
* feat: integrate nightfox.nvim themes into Open Source Classics

Agent-Logs-Url: https://github.com/nisrael/psysonic/sessions/410b9047-62de-4c0b-a6bc-1dc6f8247164

Co-authored-by: nisrael <66925+nisrael@users.noreply.github.com>

* i18n: add Nightfox to theme FAQ in de + zh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nisrael <66925+nisrael@users.noreply.github.com>
Co-authored-by: Psychotoxical <dev@psysonic.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:09:39 +02:00
Psychotoxical 8cd4cdcd64 feat: v1.33.0 — Fullscreen Player redesign, Norwegian, configurable preload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 02:00:48 +02:00
Psychotoxical 9be0d8dfa9 feat: v1.32.0 — The Big Easter Update
Internet Radio full release (HTML5 engine, card UI, RadioDirectoryModal,
cover upload), Backup/Restore, Albums year filter, Statistics Library
Insights (playtime/genres/formats), Playlist cover upload, resizable
tracklist columns for Playlists & Favorites, crossfade fine control,
Settings Storage tab redesign, various fixes and UI polish.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 19:34:26 +02:00
Psychotoxical c873880a26 feat: v1.31.0 — AutoEQ, resizable tracklist columns, Discord Listening type, layout fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 02:04:14 +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 7263d93d42 feat: v1.29.0 — Radio fast-start, seek fix, OGG, error toasts, contributors
Incorporates PRs #38 (nisarg-78), #42 #43 #44 (JulianNymark) with fixes:

Audio / Playback
- Artist Radio starts immediately from fast getTopSongs (local library);
  getSimilarSongs2 (Last.fm) enriches queue in background — no more wait
- Fix seek audio glitch: EqualPowerFadeIn only resets to zero-gain on
  seeks to track start (<100 ms); all other seeks resume at unity gain
- OGG/Vorbis container support via symphonia-format-ogg (PR #42)
- Human-readable audio error messages in SizedDecoder (PR #44)
- Audio playback errors shown as themed toast notifications (PR #43)

Queue / Radio
- Infinite Queue: proactive load of 5 tracks (was 25) when ≤2 remain
- Radio: proactive reload at ≤2 remaining tracks, independent of
  Infinite Queue setting — radio no longer stops at last track
- Fix: clicking Start Radio multiple times no longer stacks duplicates
- Fix: Start Radio on artist keeps current song playing
- Manual tracks always appear before Radio, Radio before Auto-added
- Queue separators: "— Radio —" and "— Auto —" dividers
- Fix: radio proactive load now works even when songs lack artistId
  (uses currentRadioArtistId module var as fallback)

UI / UX
- Click synced lyrics lines to seek (PR #38)
- Volume scroll wheel on volume slider (PR #38)
- Lyrics: active / completed / upcoming visual states (PR #38)
- Shared toast utility (src/utils/toast.ts) replaces inline toast fn
- Auto-updater: relaunch_after_update Rust command exits first to
  release single-instance lock before spawning new process

About / Credits
- nisarg-78 and JulianNymark added to contributors (since v1.29.0)
- netherguy4 added as Special Thanks for feature ideas and feedback
- i18n: aboutSpecialThanksLabel in all 5 languages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 22:17:55 +02:00
Psychotoxical 95283d792b feat: v1.28.0 — Infinite Queue, Start Radio, Single-click Play, Performance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:40:39 +02:00
Psychotoxical 55e7cb835b feat: v1.27.0 — In-App Auto-Update, Configurable Home, Icon Consistency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 02:14:57 +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 560349819f feat: v1.25.0 — Tray Icon, Minimize to Tray, Sidebar Customization
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:46:41 +02:00
Psychotoxical c67d606f89 feat: v1.24.0 — Playlist Management, native sample rate playback
- Full playlist feature: overview grid, detail page with hero collage,
  tracklist DnD, song search, suggestions, context menu submenu
- Audio: disable all app-level resampling — every track plays at its
  native sample rate (target_rate always 0 in audio_play + chain_next)
- Fix: playlist hero bg flicker (memoize buildCoverArtUrl calls)
- Fix: input focus double-border (search-input → .input class)
- Polish: redesigned playlist search panel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:14:15 +02:00
Psychotoxical 3d03b8d5a1 merge: PR #9 — Replay Gain fix (trbn1) + conflict resolution
Merges songToTrack() helper and audio_update_replay_gain Tauri command
from @trbn1 without losing v1.22.0 DnD/queue-management work:

- All track construction sites use songToTrack() for correct replay gain
- psy-drag system (mouse-event DnD) preserved across all components
- enqueueAt() position-aware insertion preserved in QueuePanel
- updatePlaylist / smart-save / active-playlist tracking preserved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 22:07:29 +02:00
Psychotoxical d6f6e6466c feat: v1.22.0 — Queue Management, DnD Overhaul, Seek & Waveform Fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:14:59 +02:00
trbn 95cdbc7fc7 fix: replay gain not applying to tracks
Replay gain was not working because track objects were created manually without
including replay gain metadata from the Subsonic API response.

Changes:
- Add songToTrack() helper function to properly map SubsonicSong to Track with
  replayGainTrackDb, replayGainAlbumDb, and replayGainPeak fields
- Add audio_update_replay_gain Tauri command for dynamic volume recalculation
  when replay gain settings change mid-playback
- Add updateReplayGainForCurrentTrack() to recalculate volume when toggling
  replay gain setting
- Fetch fresh track data on cold resume (app relaunch) to ensure replay gain
  values are current from server
- Update all files that create track objects to use songToTrack()

Fixes issue where toggling replay gain ON/OFF or changing between track/album
mode had no effect on currently playing or newly played tracks.
2026-03-30 19:11:33 +02:00
Psychotoxical 42863877f6 fix: close seek-flash race window after debounce expires
PR #7 blocked stale Rust progress ticks during the 100 ms debounce
window but lifted the guard exactly when invoke('audio_seek') went out.
The engine can still emit 1–2 ticks with the old position before the
seek takes effect.  Add seekTarget: once the debounce fires, record the
requested time and keep ignoring progress until current_time is within
2 s of that target, then clear it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:28:48 +02:00
Joshua Bassett bb56269cd1 fix: prevent waveform seekbar flashing back to old position on seek
When clicking/dragging the waveform seekbar, the playback position would
briefly flash back to the previous position before snapping to the new
one.

The seek function optimistically updates the store with the target
position immediately but debounces the actual audio_seek IPC call by
100ms. During that window, audio:progress events from the Rust backend
continue to arrive carrying the old playback position, overwriting the
optimistic update and causing the visual flash.

The fix skips incoming audio:progress events while a seek debounce is
pending, since the store already holds the correct target position. Once
the debounce fires and audio_seek is sent to the backend, progress
events resume normally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:59:55 +11:00
Psychotoxical 29a4363dca feat: v1.21.0 — What's New Modal, 3 New Themes (Beta), Artist Column, Powerslave Blue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 21:48:40 +02:00
Psychotoxical e1d27798eb feat: v1.20.0 — FLAC Seek Fix, Genres, Genre Filter, Chinese, W10 Theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 18:04:01 +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 936e548f40 feat: v1.18.0 — Offline Mode (Beta), MPRIS Seek, 2 New Themes, Perf Fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 17:17:09 +01:00
Psychotoxical 6bdd6f3a59 feat: v1.17.0 — Media Keys, 3 New Themes, Perf Fixes, Contrast Audit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:30:13 +01:00
Psychotoxical d62bffd082 feat: v1.16.0 — 15 New Themes, W98 Overhaul, Aqua Quartz Polish
New themes: Aqua Quartz (Mac OS X Aqua, skeuomorphic), Spider-Tech,
T-800, B-Runner, Hill Valley 85, TetraStack, Turtle Power, Insta,
ReadIt, The Book (new Social Media group), W3.1, Jayfin (Jellyfin).

W98 rebuilt from scratch: authentic #d4d0c8 warm-gray, full 4-layer
3D bevel on all panels/buttons (raised/sunken on press), title bar
gradient on song name, navy progress fill, 16px styled scrollbar.

Aqua Quartz: all button variants now jelly-styled, blue Source List
sidebar with white pill nav, aluminium pinstripe background.

Theme Picker: groups and themes sorted alphabetically, Mediaplayer
group renamed, Pandora/Order of the Phoenix/Imperial Sith removed.

Fix: AlbumDetail genre propagation, W98 accordion active state,
Aqua Quartz sidebar labels, W98 connection indicator contrast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 21:54:57 +01:00
Psychotoxical 3effad0830 feat: v1.15.0 — Genre Strip, Lyrics Accent, Sidebar Button fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 18:47:32 +01:00
Psychotoxical 361e9cfdb3 feat: v1.14.0 — Critical Buffer Fix, Gapless/Crossfade stable, UX polish
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:00:55 +01:00