github Psychotoxical/psysonic app-v1.47.0-rc.1
Psysonic v1.47.0-rc.1

pre-releaseone hour ago

Added

Artist detail — album year sort

By @cucadmuh, PR #877

  • Albums by … on the artist page: toggle year sort (newest/oldest) within each release-type block; preference persists for the session per server.

Servers — edit existing profiles

By @Psychotoxical, PR #780

  • Pencil button opens an inline edit form prefilled with the existing profile. Card actions collapse to icon-only on narrow viewports so Edit/Delete stay reachable.

Local library index + search (preview)

By @Psychotoxical + @cucadmuh, PR #846

  • Settings → Library: local SQLite track index per server — background initial and delta sync, full resync, integrity verify, and auto-reconcile when the server reports fewer tracks than expected.
  • Live Search and Advanced Search query the local index when it is ready (fast, offline-capable).
  • Multi-server UI (by @cucadmuh): per-server exclude/include; indexing runs one server at a time so SQLite stays responsive; offline servers are retried automatically.
  • Local search results respect the sidebar music-library filter; parallel album fetch during initial sync.

Player stats — local listening history

By @cucadmuh, PR #849

  • Statistics → Player stats tab (/player-stats): year summary (listening time, clustered sessions, track plays, unique tracks, listening days, full/partial counts), GitHub-style heatmap by track plays per day, recent-days accordion, and day drill-down with per-track completion.
  • Records finalized listens to play_session in library.sqlite when the playback server's local index is enabled and ready (not preview/radio; listenedSec > 10).
  • Live UI refresh after a persisted listen; partial-index notice when only some servers are indexed.
  • i18n across 9 locales.

Playback speed — global tempo and pitch strategies

By @cucadmuh, PR #852

  • Settings → Audio and player bar: global speed 0.5–2.0× with three strategies — Speed (pitch-corrected, default), Varispeed, and Pitch shift (manual semitone offset).
  • Rust path: preserve-pitch worker (pitch_shift) for Speed / Pitch shift; varispeed via sample-rate scaling; seek-restart on strategy or enable changes.
  • Unified content timeline for seek bar, elapsed time, and seek; player bar popover anchored like volume (compact controls; full hints in Settings).
  • Orbit: passthrough 1.0× while a session is active. Not applied to radio or preview.

Track enrichment — oximedia BPM/mood, mood search, queue display

By @cucadmuh, PR #863

  • Analysis: client-side oximedia pass (60s center window) writes BPM, valence, arousal, moods JSON, and mood_tag facts to the local library index; unified playback→analysis dispatch covers stream, hot/offline files, preload, and gapless.
  • Queue: measured BPM and top mood labels when the playback server's index is enabled; analysis:enrichment-updated refreshes the UI without waiting for poll.
  • Advanced Search: virtual mood groups (joy, sadness, dance, work, romance, anger) filter via local index + mood_tag rows (migration 008).
  • Mood search requires persisted mood_tag facts; queue display may still show labels from valence/arousal fallback before tags land.

Analytics strategy + migration safety for index-key rebuild

By @cucadmuh, PR #864

  • Rebuilt server scoping around stable indexKey identifiers across Rust + frontend paths used by playback, analysis, and local index state.
  • Added per-server analysis strategy controls (lazy/aggressive), per-server parallelism tuning, queue progress visibility, and clear-analysis actions in Settings → Library.
  • Added first-launch migration orchestration (inspect/run + progress events + blocking gate) with frontend persisted-key rewrites to the new indexKey scope.
  • Reworked playback/analysis handoff paths (play, preload, stream/ranged, queue restore) so analysis dispatch and queue-priority hints use the same server scope model.
  • Hardened startup/runtime migration checks so bootstrap waits for required migration phases before normal playback/index startup.

Backup & Restore — library databases + full archive flow

By @cucadmuh, PR #864

  • Settings → System → Backup & Restore: added two archive-backed modes — Library databases (library + analysis SQLite snapshots) and Full (settings + library databases).
  • Import auto-detects backup type from file contents (.psybkp / .psylib / .psyfull) from one entry point instead of per-mode import actions.
  • Restore switches active databases via runtime store swap/restore flow and keeps previous files as .bak for recovery on failed validation.

Cover art — tier ladder, disk cache, and grid prefetch

By @cucadmuh, PR #869

  • Album and artist grids load sharper repeat visits from a dedicated WebP disk cache (separate from the general image cache budget in Settings → Storage).
  • Dense lists prefetch smaller tiers first, then steady-state grid quality capped at 512px for scroll performance; detail and player chrome still resolve up to 800px on demand.
  • One-time upgrade clears legacy multi-size IndexedDB cover keys; offline-first when the server is unreachable.

Lossless — local index browse, filters, and drill-down

By @cucadmuh, PR #871

  • Local index: library_list_lossless_albums queries indexed tracks by lossless suffix allowlist; /lossless-albums and Home rail use SQLite when the library index is ready, with Navidrome bit_depth walk as fallback.
  • Advanced Search: lossless is true on tracks, albums, and artists (local + network); artist/album links open detail with ?lossless=1 and a lossless-mode banner.
  • All Albums: lossless toggle (local index only — plain lossless, year, and genre combinations).
  • Sidebar: dedicated Lossless page route conserved; nav entry hidden by default and removed from visibility settings.

Albums — combined browse filters and session restore

By @cucadmuh, PR #876

  • Albums toolbar: sort, genre (with counts), year range, favorites, lossless, and compilations combine in one browse query when the local index is ready; returning from album detail restores the same filter state.
  • Favorites list reconciles from the server into the local index (no stub album rows); genre/year/lossless/compilation filters apply on the indexed catalog.
  • Year spinners use catalog min/max from the local index; compilation filter uses indexed OpenSubsonic flags (resync refreshes track metadata).

Servers — second optional address per profile (LAN + public)

By @Psychotoxical, PR #880

  • Settings → Servers: a profile can carry a second address — typically a LAN counterpart of a public URL or vice versa. The app probes LAN-first and uses whichever endpoint actually answers, so the same profile is fast at home and reachable away from home without manual switching. Single-address profiles behave exactly as before.
  • Same-server verify on save when both addresses are filled — mismatched or unreachable pairs are blocked with a clear message; two-LAN combinations are caught client-side before any network call.
  • Share links (Orbit invites, library / queue shares, magic invite v2) embed the public address by default so off-LAN guests can reach the host; a Use local address in share links checkbox flips that for LAN-only groups. Pasted invites match either address.
  • Editing the primary URL to a new host triggers a confirm-modal-gated data move: library + analysis databases, cover-cache files on disk, and player queue all re-tag to the new identifier in one go. Changing only the second address or httphttps skips the migration entirely.

Album play — hold to shuffle

By @ImAsra, PR #888

  • Hold an album Play button (~1 s) for a filling wave animation, then the album starts in shuffled order; a short click still plays in track order.
  • Album cards, hero, Because-you-like rail, and Most Played; tooltip in all locales.

Performance Probe — monitor UI, overlay pins, and live metrics

By @cucadmuh, PR #890

  • Ctrl+Shift+D modal: Monitor tab (metric cards with pin-to-overlay) and Toggles tab (tree of probe flags/phases).
  • Live CPU/memory polling: process CPU, RSS by group, thread CPU groups (Linux /proc); macOS process CPU + RSS via sysinfo.
  • HUD overlay: FPS always on top; pinned live metrics with 1-minute sparklines; Analysis/Cover pipeline blocks; corner + opacity controls.
  • Cover pipeline stats in the probe (per-server cache, ensure/peek queues).

Performance Probe — thread-group CPU toggle

By @cucadmuh, PR #891

  • Monitor live poll: explicit opt-in checkbox for Linux /proc thread-group CPU (off by default); fixes camelCase IPC so thread rows populate instead of staying on “Collecting…”.

Queue — choose between Queue and Playlist view

By @Psychotoxical, PR #922

  • Settings → Personalisation → Queue Display Mode: Queue shows only upcoming tracks — the current one stays in the header and leaves the list once played; Playlist keeps the full queue with the current track highlighted at the top. A small icon in the queue header flips the mode, and the title follows it.
  • New default is Queue — switch to Playlist in settings if you prefer the full list with the playing track shown in place.

Genre detail — play or shuffle a whole genre

By @Psychotoxical, PR #926

  • Play, Shuffle and Add-to-queue buttons on a genre page start the whole genre in one click — in order or randomized — instead of adding each album by hand. Suggested by Apollosport.

Changed

CI — hot-path coverage gates block merges

By @cucadmuh, PR #921

  • Frontend and Rust coverage jobs no longer carry continue-on-error; listed hot-path files must stay at ≥70% line coverage or the PR fails.

Linux — session GDK, WebKitGTK mitigations, and Wayland text

By @cucadmuh, PR #731

  • Nix / AUR default installs follow the session GDK backend instead of pinning GDK_BACKEND=x11; startup applies webkit2gtk-nvidia-quirk only (skip with PSYSONIC_WEBKIT_GPU_ACCEL). nix run .#psysonic-x11-legacy keeps the old explicit X11 launcher.
  • AppImage stays on X11/XWayland: unlike the .deb / .rpm / Nix packages it still pins GDK_BACKEND=x11 (set by the bundle's AppRun hook), so it doubles as the legacy channel. Use .deb, .rpm, AUR, or the Nix default for a native-Wayland launch.
  • NVIDIA + forced X11 on a Wayland user session no longer greys out the webview — the quirk uses the DMABUF renderer path instead of Wayland explicit-sync disable.
  • Wayland + GPU compositing: clearer UI text via on-demand hardware acceleration on main and mini webviews; Settings → System adds Wayland text rendering presets (Balanced / Sharp / GPU / Minimal). Opt out with PSYSONIC_SKIP_WAYLAND_FONT_TUNING.

Library browse — in-page overlay scroll

By @cucadmuh, PR #731

  • Artists, Albums, Composers, Lossless albums, and New releases scroll inside the route on a locked in-page viewport — toolbars stay sticky, virtual grids use the matching scroll root.
  • Sidebar hover and album/artist card covers no longer jitter on WebKitGTK + Wayland during pointer moves.

Settings + Queue polish

By @kveld9 + @Psychotoxical, adopted from PR #558, rewritten in PR #778

  • Settings toggle rows dim non-toggle content to 0.6 opacity when their switch is off; mutex-disabled rows (Crossfade/Gapless) unchanged.
  • Queue toolbar ClearClear queue across all 9 locales.

Development — parallel tauri dev alongside release

By @cucadmuh, PR #866

  • Debug builds skip tauri-plugin-single-instance so ./dev.sh can run next to an installed release while sharing the same app data directory.
  • Debug-only chrome: window title Psysonic (Dev), red sidebar brand, monochrome custom titlebar buttons, mobile DEV badge, horizontally flipped tray icon.
  • Debug builds do not register OS global shortcuts, MPRIS/media keys, or Windows taskbar media controls — release keeps system-wide input when both are open.

Interface Scale — covers the whole window

By @Psychotoxical, PR #781

  • Settings → Appearance → Interface Scale now scales sidebar, queue, player bar, modals/portals and the fullscreen player alongside the main content — same behaviour as browser Ctrl+/−.

Radio — card control polish

By @Psychotoxical, reported by zunoz on Discord, PR #786

  • Repeat is disabled while a radio stream plays.
  • Deleting the playing station fades out instead of cutting hard.
  • Play / Stop tooltip on the cover-overlay button; stop uses a Square icon.

Library browse — local index race and catalog paths

By @cucadmuh, PR #847

  • Artists, Composers, Tracks, and Search Results text search races local FTS against network search3; a ready index still serves hits when remote is down.
  • All Albums paginated browse and genre filter, plus Artists catalog browse-all, read from the local index when ready (network fallback unchanged).
  • DevTools: [psysonic][library] browse-race … lines for race winner, timings, hit counts, and fallback reason.

Lyrics — sources can be turned off entirely

By @Psychotoxical, suggested by sddania, PR #855

  • YouLyPlus is now an independent toggle instead of an either/or with the standard sources, so lyrics can be switched off completely — turn off YouLyPlus and every source under Settings → Lyrics. With nothing selected, no lyrics are fetched or shown and the queue lyric tab says so. Fresh installs start with all sources off.

Queue — smoother scrolling for very long queues

By @Psychotoxical, PR #857

  • The queue panel now renders only the rows in view, so very long queues (e.g. hours of Artist Radio) stay smooth instead of bogging down the interface.

Queue — section dividers kept when restoring from the local index

By @Psychotoxical, PR #858

  • When the queue is rebuilt from the local library index on startup, the Radio and Auto-added section dividers are now preserved. Groundwork toward keeping very large queues fast and light.

Queue — on-demand track loading for very large queues

By @Psychotoxical, PR #859

  • Continued groundwork for multi-thousand-track queues: track details are resolved on demand through a shared cache rather than all being held at once. No change to how the queue looks or behaves.

Queue — panel now reads through the shared track cache

By @Psychotoxical, PR #860

  • The queue panel sources its row details through the on-demand track cache, another step toward keeping multi-thousand-track queues light on memory. No visible change.

Backup UX — blocking progress gate for long operations

By @cucadmuh, PR #864

  • Backup/export and restore operations now show a global blocking status modal after file selection, so the app no longer looks frozen while archive and SQLite work runs.

Discord Rich Presence — track title in the member list

By @Psychotoxical, PR #885

  • The Discord member list and the collapsed presence card now show the playing track next to the music icon instead of the static "Psysonic" label — matches how comparable players appear in the user list.
  • New User list line (name) template under Settings → Integrations → Discord Rich Presence, default {title}. Same placeholders as the other templates: {title}, {artist}, {album}. Leaving it empty restores the previous "Psysonic" display.

Library browse — chunked local catalogs and unified in-page scroll

By @cucadmuh, PR #890

  • Albums / Artists: local index loaded in 200-row SQL chunks instead of a single ~50k-row fetch; filters preserved.
  • All Albums: client-slice infinite scroll on the local index (Artists-style).
  • Shared in-page scroll hooks and sentinel UI across browse routes; album SQL pagination prioritized over cover ensures.

Fixed

CI — npmDepsHash on app-v* tags

By @cucadmuh, PR #927

  • Channel publish now refreshes nix/upstream-sources.json and flake.lock on the channel branch before cutting app-v* tags, so Nix builds from release tags no longer fail with stale npmDepsHash (e.g. after promote finalizes package-lock.json version).

Queue — Infinite Queue and Smart Radio top-ups no longer show / 0:00

By @Psychotoxical, PR #930

  • Tracks added automatically by Infinite Queue or by Smart Radio could render as / 0:00 instead of their real title and duration when the queue was filled without a queue-replacing playback (single-track enqueue from a song row, search result, etc).
  • Same root cause as PR #892 — just on the auto-add paths the earlier fix did not cover. The owning server is now pinned before each auto-top-up so the resolver cache sees the fresh tracks.

Advanced Search — centered button label

By @Psychotoxical, PR #925

  • The Search button's label is now centered. Buttons wider than their text (the Search button has a fixed minimum width) previously rendered the label left-aligned.

Radio — track info in OS media controls

By @Psychotoxical, reported by agriffit79 on GitHub, PR #924

  • The Linux media overlay (MPRIS) now shows the current radio track and artist instead of just "Psysonic", and updates as the stream changes songs. Internet radio plays through the WebView audio element, which exposes its own OS media player — that player is now fed the live ICY/AzuraCast metadata. Streams that send no metadata still fall back to the station name.

Analytics — Opus waveform and loudness analysis

By @cucadmuh, PR #883

  • Opus tracks: waveform, LUFS, and enrichment analysis now use the same symphonia-adapter-libopus registry as playback — previously Symphonia could demux Ogg Opus but failed at decoder creation, leaving .opus libraries without analysis data.

In-page browse — virtual scroll and cover-art priority

By @cucadmuh, PR #783

  • In-page browse: virtual artist/album/composer grids and lists no longer lose all rows after deep scroll — scrollMargin now targets the in-page overlay viewport, not the locked main route scrollport.
  • Cover art on browse pages: CachedImage priority scoring follows the real scrolling pane so visible thumbnails win network fetch slots; Artists infinite scroll loads one page per batch instead of re-entrantly queueing many pages during a fast fling.

Lucky Mix after server switch

By @cucadmuh, PR #785

  • Starting a mix on the browsed server no longer spams cross-server enqueue errors — unpinned or foreign queues hand off cleanly before batch enqueue.

Radio — paused streams stay paused

By @Psychotoxical, reported by drelabre on GitHub, PR #786

  • Pausing a radio stream no longer auto-resumes after about a minute on macOS.

Mainstage — album rail hover controls

By @cucadmuh, PR #787

  • Home horizontal album rails (Discover, etc.): play/enqueue overlay no longer flickers on WebKitGTK + Wayland GPU; cover zoom stays smooth like the All Albums grid.
  • Album grids and song rails: overlay pointer-events so the dim layer does not steal hover from the card.

Album view — bulk add to playlist selection

By @Psychotoxical, PR #844

  • Bulk "Add to playlist" no longer clears the track selection without opening the playlist picker.

Playlists — column sorting keeps the viewport in place

By @Psychotoxical, PR #848

  • Sorting a playlist column no longer snaps the viewport down to the list when scrolled to the top.

Settings — local library index exclude/include feedback

By @cucadmuh, PR #850

  • Settings → Library: Exclude from sync and Include again show immediate busy labels and block repeat clicks while bind/unbind runs; exclude cancels an in-flight sync first.

Statistics — player stats tab without local index

By @cucadmuh, PR #851

  • Statistics → Player stats tab stays visible when the local library index is off; an info notice explains that player statistics require the index and links to Settings → Library.

Playlists & Favorites — column picker on short lists

By @Psychotoxical, PR #853

  • On a one-song playlist (or short favorites list) the column menu was clipped behind the list, added a stray scrollbar, and could hide the row when scrolled. The picker now sits outside the scroll area, so it opens fully on lists of any length.

Browse all tracks — sticky header no longer overlapped

By @Psychotoxical, PR #854

  • Scrolling the full tracks list painted rows over the sticky column header. Browse now flows in the page like the search results, so the header stays put; it shares one list view with Search and Advanced Search.

Local library index — full resync removes server-deleted tracks

By @cucadmuh, PR #861

  • Settings → Library → Full resync now soft-deletes local rows that no longer exist on the server after a successful re-sync (mark-and-sweep via resync_gen), so Ready (N tracks) no longer stays inflated when tracks were removed on Navidrome/Subsonic. Delta tombstone reconcile is unchanged.

Server index-key migration — unknown/legacy data handling

By @cucadmuh, PR #864

  • Legacy destructive migration paths were replaced with a dual-DB import/switch flow that keeps old DBs as source until verification passes.
  • Rows belonging to removed servers are explicitly skipped/purged from the active migrated DB scope instead of being silently carried forward.
  • Legacy sqlite artifacts from old paths are now cleaned up after successful path migration (including WAL/SHM sidecars) to prevent stale old-version leftovers.

Now Playing — stray zero metadata badges

By @cucadmuh, PR #865

  • Hero track-info badges no longer render literal 0 when numeric metadata fields (bit depth, bitrate, sample rate, year, rating) are missing and arrive as zero from the server.

Analysis — failed tracks no longer block completion forever

By @cucadmuh, PR #867

  • Settings → Library: problematic tracks that cannot complete loudness/enrichment are now persisted as failed and excluded from endless retry loops after restart.
  • Added failed-track controls in Analytics strategy (count, inspect list with title/path, export, and rescan) so users can review and requeue only when they want.
  • Aggressive analysis idle checks now run as a cheap startup + 5-minute recheck against live-track count instead of heavy frequent polling.

Live Search — multi-server local index hits

By @cucadmuh, PR #868

  • Live Search with a local index no longer returns empty or wrong-server hits on multi-server libraries — FTS is scoped to the active server instead of global bm25 across all indexed tracks.
  • Local artist/album rows dedupe correctly (one performer no longer fills the whole dropdown); Advanced Search text queries use the same server scope fix.

Queue — mixed-server routing and quota-safe persist

By @Psychotoxical + @cucadmuh, PR #872

  • Mixed-server queues with the same track ID on different servers now stay on their original server through track switches, undo, and radio top-ups.
  • Persisted queue is quota-safe — a full local storage no longer blocks playback on very large queues.

Analytics — aggressive scan no longer eats memory on big libraries

By @Psychotoxical, PR #873

  • Settings → Library → Analytics → Aggressive on multi-server or 100k+ track libraries no longer climbs in memory until the system swaps (Linux) or runs out (Windows OOM mid-scan): the HTTP download stage now waits for Symphonia decode + loudness to catch up instead of buffering tracks faster than they can be processed.
  • Now-playing prefetch still bypasses the cap, so starting a track during a background scan stays instant.

Home — Discover Songs cover art with local index

By @Psychotoxical + @cucadmuh, PR #874

  • Mainstage → Discover Songs no longer shows disc placeholders when the local library index returns tracks without coverArt but with a valid albumId — cover resolution matches the Rust backfill rule (COALESCE(cover_art_id, album_id)).
  • Discover Songs row gets dedicated mainstage cover prefetch and warmup so song cards are not crowded out by album rails on cold caches.

Cover art — Windows thumbnails, PNG decode, and Subsonic cover ids

By @cucadmuh, PR #878

  • Small cover surfaces on Windows (player bar, queue, artist top tracks) no longer stay empty while large album art loads — tier ladder disk lookup, valid asset:// URLs only, and broader Tauri asset scope.
  • Fixes a startup/UI freeze when disk paths were applied via rememberGridDiskSrc (notify loop); seeds cache without waking subscribers.
  • Resolves Subsonic coverArt when it equals the track id — prefers albumId and warmed album-grid art on playback and artist pages.
  • No broken-image flash on cover surfaces while disk tiers warm (placeholder until a loadable URL exists).
  • Rust cover pipeline decodes PNG bytes from the server (previously JPEG/WebP only); failed decode no longer leaves albums stuck with .fetch-failed.

Analytics — advanced library backfill without webview jank

By @cucadmuh, PR #881

  • Settings → Library → Analytics → Advanced on large libraries no longer stalls the whole UI (~4 FPS): catalog scheduling runs in a native background worker like cover backfill, not a webview polling loop.
  • Partially analyzed tracks (hash + BPM but missing waveform/loudness) are picked up via a targeted second scan with a reset cursor so the library is not skipped mid-pass.
  • Performance Probe analysis stats still update during background backfill; waveform/enrichment refresh events stay quiet for low-priority work.

Analytics — advanced backfill scan no longer replays the first chunk

By @cucadmuh, PR #882

  • Settings → Library → Analytics → Advanced on large libraries no longer stalls mid-pass when most early tracks are already analyzed: the native coordinator keeps hash/BPM gap scan phase and cursor across ticks instead of restarting from the first ids every cycle.

Settings — Linux text-input freeze workaround

By @Psychotoxical, PR #884

  • Settings → System → Behavior (Linux only): optional toggle for users on WebKitGTK 2.50.x where text fields freeze when clicked (issues #342, #782) — turning it on forces the input to repaint on focus. Default off; enabling it adds a brief flicker on search icons.

Artist page — top track thumbnails

By @cucadmuh, PR #886

  • Top Tracks on the artist page now load cover art through the same album id + coverArt path and disk warm batch as the albums grid below — fixes slow or missing 32px thumbs that used a separate sparse resolver.
  • Warm/peek uses the album-grid tier (not 32px), top-track rows ensure at high priority, and the page registers the same dense prefetch as All Albums.

Covers — load on Windows when the server URL has a :port

By @Psychotoxical, PR #889

  • Album, now-playing, mainstage, and lightbox covers no longer stay blank on Windows when the active server URL has a :port (typical Navidrome LAN setup on :4533). The colon used to land in a Windows filesystem segment, so the OS rejected the whole cache path with ERROR_INVALID_NAME and every cover load failed silently.
  • Existing cache buckets on disk are wiped once on the next launch (layout-stamp bump) and rebuild lazily as users browse. Library, offline, and hot caches are untouched.

Library browse & covers — scroll stability and cover loading

By @cucadmuh, PR #890

  • In-page infinite scroll stabilized; cover memory caches capped; covers keep loading during active grid scroll.
  • New Releases and Lossless grids use the correct in-page scroll root; cover ensure invoke pump no longer sticks; viewport priority tiers for ensure/peek.
  • Performance Probe overlay: fixed blank page from unstable sparkline history; synchronized poll clock and bar/sparkline tick jitter.

Queue — new tracks no longer render as blank placeholders

By @Psychotoxical, PR #892

  • Adding tracks to the queue from Advanced Search results, song rows, or song cards right after launch could show every new entry as / 0:00 instead of the real title and duration, until something else triggered a queue-replacing playback.
  • Root cause: the queue's owning server was not pinned yet, so the resolver cache skipped seeding the incoming tracks. Add-to-queue mutations now pin the active server up-front.

Don't miss a new psysonic release

NewReleases is sending notifications on new releases.