# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). > **πŸ›‘οΈ A note on safety investments:** Making sure Psysonic is trusted on every OS takes real money out of my pocket β€” an Apple Developer Account (now active, which is why macOS builds are signed + notarized for everyone starting with this release) and a Windows code-signing certificate (ordered, currently in validation). If you'd like to help cover those costs, you can chip in at [ko-fi.com/psychotoxic](https://ko-fi.com/psychotoxic) β€” completely voluntary, no pressure at all. Every bit helps keep Psysonic free and safe across Windows, macOS and Linux. > > **⚠️ Windows users:** This is one of the last releases with an unsigned Windows installer. Until the certificate clears validation, any SmartScreen or antivirus warning on the installer is a false positive β€” the binary itself is safe. > > **πŸŽ‰ macOS users:** Starting with **v1.40.0**, Psysonic is signed + notarized and can **update itself silently**. No more DMG downloading and dragging to Applications β€” the updater fetches the signed `.app` bundle, verifies the signature, replaces the app in place, and relaunches. Just click "Install now" when the update notification appears. > > **πŸ“¦ Version jump 1.34.x β†’ 1.40.0:** The 1.34.x patch series was bumped a lot as each small feature landed. 1.40.0 consolidates the last few weeks of work β€” macOS signing + auto-updater, the Device-Sync overhaul, theme work and contrast audits β€” into a single coherent release. The next major bump (2.0.0) is planned once Windows code-signing + Windows auto-updater are active as well. ## [1.44.0-rc.1] - 2026-04-27 > **πŸ§ͺ Release Candidate:** This is `1.44.0-rc1` β€” a release candidate for `1.44.0`. It is distributed as a draft release only and does not push via the auto-updater. Test feedback welcome before the final tag. ### Added - **Loudness Normalization β€” EBU R128 / LUFS, persistent analysis cache** *(by [@cucadmuh](https://github.com/cucadmuh) and [@Psychotoxical](https://github.com/Psychotoxical), PRs [#315](https://github.com/Psychotoxical/psysonic/pull/315) / [#317](https://github.com/Psychotoxical/psysonic/pull/317) / [#326](https://github.com/Psychotoxical/psysonic/pull/326) / [#333](https://github.com/Psychotoxical/psysonic/pull/333))*: The headline feature of this release. Psysonic now performs full **EBU R128 integrated-loudness analysis** on every track and applies a per-track gain so that quiet recordings, mastered-for-streaming albums, and "loudness war" pop tracks all play back at a consistent perceived level. Default target is **`-10 LUFS`** (a touch hotter than Spotify / Apple Music) and is configurable in Settings β†’ Audio β†’ Normalization. Cold-cache analysis costs **~35–40% on a single CPU core for ~1 minute per track**, after which the result is persisted and replays cost **0% CPU**. Settings UI is restructured into a single Normalization section with mutually-exclusive modes (Off / ReplayGain / LUFS). Follow-up PRs stabilize the LUFS computation loop, serialize CPU-heavy seed work, fix target-slider sync with the active engine, trim a stray pre-analysis silence, and clean up queue + Settings copy. Mixed mean/peak waveform bins were introduced alongside so the new analysis pass also feeds richer waveform data without a second decode. - **Orbit β€” Multi-User Listen-Together** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#304](https://github.com/Psychotoxical/psysonic/pull/304))*: The other big-ticket feature. Orbit is a real **synchronized listening session for multiple Psysonic users** β€” host starts a session, shares a magic-string link, guests paste it and instantly mirror the host's queue, current track, and playback position. Guests can suggest tracks (single-track suggestions, not full queue replacements); the host approves them with auto-approve as an opt-in. A live participant strip in the queue shows everyone connected, host-presence badge tracks the host's network state and auto-leaves stalled sessions, per-guest mute is supported, and a 9-section in-app help modal walks new users through the flow. Every interactive song surface (Tracks Hub, Albums, Playlists, Random Mix, Favorites, Artist pages, Search, Advanced Search) routes through the Orbit hook when a session is active so a single click suggests / host-enqueues instead of broadcasting a full queue. Full i18n across all 8 locales. There is no equivalent feature in Navidrome itself β€” this is the killer feature that pulls a Spotify Group Session into a self-hosted setup. Documented end-to-end in `ORBIT.md`. - **Now-Playing β€” info dashboard with draggable widget cards** *(by [@Psychotoxical](https://github.com/Psychotoxical), PRs [#266](https://github.com/Psychotoxical/psysonic/pull/266) / [#267](https://github.com/Psychotoxical/psysonic/pull/267))*: The Now-Playing page is rebuilt from a flat card list into a two-column **info dashboard** centred on context and data density rather than duplicating the fullscreen / mini players. Hero cover gets a richer info block (release-age tagline, full tech-badge row, Last.fm love, lyrics toggle, star rating, per-user playcount). Left column carries a sliding-window album tracklist (tracks 21–30 of a 30-track album when track 30 is playing β€” no more scrolling past dozens of irrelevant rows), top-songs by the same artist, and OpenSubsonic credits. Right column carries a Subsonic-bio Artist card with Last.fm fallback, a 10Γ—2 contact-sheet discography (with hover tooltips for title + year), and Bandsintown tour dates. Every card is now **draggable + resizable** with the layout persisted per user β€” users arrange the dashboard however they want and the layout sticks. All cards are `React.memo`-isolated, with module-level TTL caches for song meta, artist info, top-songs, discography, Bandsintown and Last.fm β€” same-artist track switches don't refetch. - **Tracks β€” full library hub page** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#300](https://github.com/Psychotoxical/psysonic/pull/300), closes [#299](https://github.com/Psychotoxical/psysonic/issues/299))*: A new sidebar entry between *All Albums* and *Build a Mix* that surfaces the full library at the **track level** rather than as albums. Three sections: a "Track of the moment" hero (rerollable random pick with play / enqueue), a "Random Pick" rail of 18 song cards (also rerollable, deduped against the hero), and a virtualised, paginated browse list with a search input on top. Browse uses Navidrome's native `/api/song?_sort=title&_order=ASC` for proper A–Z order (Subsonic `search3` has no sort param) with a graceful fallback to `search3` on non-Navidrome servers. New `enqueueAndPlay()` helper appends to the queue and jumps to the track instead of replacing the queue. Full 8-locale coverage including Russian Slavic plurals. - **Lucky Mix β€” instant mix from listening history and ratings** *(by [@cucadmuh](https://github.com/cucadmuh), PRs [#278](https://github.com/Psychotoxical/psysonic/pull/278) / [#332](https://github.com/Psychotoxical/psysonic/pull/332))*: A one-shot "feeling lucky" action that builds a **~50-track queue** seeded from your most-frequently-played albums + 4β˜…+ rated songs, expanded with AudioMuse `getSimilarSongs` batches, then topped up with random fill β€” all while skipping 1β˜…β€“2β˜… tracks and honouring the active library scope. Surfaces only when AudioMuse is enabled on the active Navidrome server (plain Subsonic similar-track results are too weak for this UX). Cancellable mid-build via a clickable dice control in the queue, restores the pre-build queue snapshot if assembly fails before the first track plays, and emits structured debug logs when logging mode is `debug`. Available in the sidebar (configurable visibility), mobile overlay, and the Mix landing page. The follow-up PR fixes the mix-rating filter against Navidrome's OpenSubsonic rating fields. - **Library deep links β€” `psysonic2-` share scheme** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#261](https://github.com/Psychotoxical/psysonic/pull/261))*: Building on the `psysonic1-` server-invite scheme from #258, you can now share **tracks, albums, artists and entire queues** as base64url-encoded magic strings. Pasting one anywhere in the app (Login screen, Settings, the main window β€” anywhere) auto-switches to the matching server and plays / navigates to the target. Queue links resolve track IDs in chunks of 12 and report a `played / total, skipped N` toast when the receiving server is missing some tracks. Toolbar actions in `AlbumHeader`, `ArtistDetail`, `QueuePanel`, plus context-menu entries for tracks, albums and artists. Unit-tested for encode/decode roundtrips, URL normalisation, malformed tokens and version gating. - **Sleep Timer β€” circular ring UI + in-button countdown** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#272](https://github.com/Psychotoxical/psysonic/pull/272))*: The sleep-timer / delayed-start UI is rebuilt around a **circular SVG progress ring** drawn around the play/pause button with a gradient stroke (Accent β†’ Lavender), counter-clockwise depletion synced to the armed deadline, and a soft accent glow. The Play/Pause icon inside the button is swapped for a two-line stack while a timer is armed: a small Moon (sleep) or Sunrise (delayed start) glyph on top, the live `m:ss` / `h:mm:ss` countdown below β€” no more floating pill that gets clipped by the viewport edge. The modal that arms the timer is also redesigned: mood-tinted header with accent-circle icon, soft radial accent glow, slide-up open animation, and a live "Pauses at HH:MM" / "Starts at HH:MM" preview that updates as the user hovers presets or types into the custom field. Works in PlayerBar, FullscreenPlayer and MobilePlayerView. - **Genres β€” tag-cloud refactor** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#311](https://github.com/Psychotoxical/psysonic/pull/311))*: The Genres page used to mount ~60 large Lucide-SVG cards per infinite-scroll batch, freezing the WebKitGTK renderer for several seconds on large libraries. Replaced with a **flowing tag cloud** of compact pills β€” one per genre, font size log-scaled by `albumCount`, deterministic colour from the existing CTP palette hash. No SVGs, no pagination needed, instant open even on libraries with hundreds of genres. Pills sit calm at rest (text in `--text-primary`, only the border / background carry a faint genre tint) and reveal full genre colour on hover. - **Queue β€” undo/redo with hotkeys + scroll restoration** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#331](https://github.com/Psychotoxical/psysonic/pull/331))*: **Ctrl+Z / Cmd+Z** undoes the last queue edit, **Ctrl+Shift+Z / Cmd+Shift+Z** redoes it. Snapshots capture queue order, current track, playback position, play/pause state and the queue-list scroll offset; bounded at 32 entries. When the restored snapshot still refers to the same playing track (typical reorder / remove-other-row / enqueue flows) playback is left alone β€” only queue UI state updates. When the snapshot points at a different track, the Rust audio engine is resynced to the snapshot's track + seek position + desired play/pause. Clearing the queue and automatic next/gapless advances are intentionally not recorded. The queue-list scroll position is also restored, with a one-shot suppression of the existing "scroll next track into view" effect so the list does not jump away from the restored offset. Editable fields (inputs / `contenteditable`) are correctly skipped so native undo keeps working there. The mini player forwards Ctrl+Z / Shift+Ctrl+Z to the main window via Tauri events. - **Now-Playing Info tab in queue panel** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#244](https://github.com/Psychotoxical/psysonic/pull/244))*: A third tab next to Queue + Lyrics in the right-side panel, surfacing context for the currently playing track. Three sections: an **Artist card** with bio + image from Subsonic `getArtistInfo` (clamped to 4 lines with a "Read more" toggle that only appears when the bio actually overflows); **Song info** with OpenSubsonic `contributors[]` rendered stacked (name prominent, role muted, redundant "artist" rows filtered out); **On Tour** with opt-in Bandsintown integration (off by default, in-place opt-in card with privacy info-tooltip when disabled). Bandsintown is fetched Rust-side via a `fetch_bandsintown_events` Tauri command using the whitelisted `js_app_id` (arbitrary `app_id` strings now return 403). RAM-cached per artist with inflight-promise dedup. - **Discover Songs rail on Mainstage (Home)** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#301](https://github.com/Psychotoxical/psysonic/pull/301))*: A new song-discovery rail on the Home page surfacing fresh tracks alongside the existing album rails. - **ReplayGain β€” Auto mode** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#242](https://github.com/Psychotoxical/psysonic/pull/242))*: The ReplayGain mode picker gains an **Auto** option that picks track gain or album gain based on queue context β€” album-gain when the queue is currently a contiguous album, track-gain otherwise β€” so the user no longer has to switch modes manually when jumping between full-album listening and shuffled mixes. - **Magic-string server invites + Navidrome admin share + add-user validation** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#258](https://github.com/Psychotoxical/psysonic/pull/258))*: The original `psysonic1-` magic-string scheme that #261 builds on top of. Server admins on Navidrome can generate a single-paste invite that prefills the add-server form on the recipient's side, plus inline validation on the add-user dialog so non-admins can't be saved with zero libraries. - **Settings β€” refactor: thematic tab regroup, accordion, in-page search** *(by [@Psychotoxical](https://github.com/Psychotoxical), PRs [#259](https://github.com/Psychotoxical/psysonic/pull/259) / [#263](https://github.com/Psychotoxical/psysonic/pull/263) / [#264](https://github.com/Psychotoxical/psysonic/pull/264), closes [#257](https://github.com/Psychotoxical/psysonic/issues/257))*: Settings tabs are reorganised thematically into **Servers Β· Library Β· Audio Β· Lyrics Β· Appearance Β· Personalisation Β· Integrations Β· Input Β· Storage Β· System Β· Users (admin)**. Every tab is broken into accordion sub-sections via native `
` for a calmer landing view. A magnifier icon next to the Settings header opens an **in-page search** that auto-expands accordions to reveal matches, with cross-tab fuzzy-search support, keyboard navigation, Ctrl+F binding and a results-flash animation. Last.fm / Discord / Bandsintown / Now-Playing-Share live in the new Integrations tab with a privacy banner at the top. Sidebar / Artist / Home customizers live in the new Personalisation tab. Contributors get their own card grid in System (sorted by contribution count, maintainers separated). - **Artist page β€” user-configurable visibility and section order** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#254](https://github.com/Psychotoxical/psysonic/pull/254), closes [#252](https://github.com/Psychotoxical/psysonic/issues/252) from [@bcorporaal](https://github.com/bcorporaal))*: The Artist Detail page lets the user reorder and toggle visibility of every section (Top Songs, Albums, Similar Artists, Bio, etc.). Persisted per user. Customizer lives in Settings β†’ Personalisation. - **Album cover + context menu + multi-select toolbar β€” "Enqueue"** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#256](https://github.com/Psychotoxical/psysonic/pull/256), closes [#253](https://github.com/Psychotoxical/psysonic/issues/253) from [@bcorporaal](https://github.com/bcorporaal))*: Album covers grow an "Enqueue" hover button alongside Play, the right-click context menu gains an Enqueue entry, and the multi-select toolbar gets an Enqueue action. All three append to the existing queue instead of replacing it. - **Sidebar β€” long-press drag to reorder + drop-outside to hide** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#269](https://github.com/Psychotoxical/psysonic/pull/269))*: Hold for 1s on a sidebar nav item, then drag to reorder; drop outside the sidebar to hide the item entirely (same hide-state as the Settings sidebar customizer). A trash-hint shows near the cursor when the drop would remove. Shared reorder helpers extracted into `src/utils/sidebarNavReorder.ts` and reused from the Settings customizer. - **Playlists β€” Navidrome smart playlists workflow** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#289](https://github.com/Psychotoxical/psysonic/pull/289), proposed by **bequbed** on Discord)*: Real server-side smart playlists (rule-based, auto-updated by Navidrome) are now first-class citizens in the Playlists page β€” no separate menu or page. Create / edit / delete with the same UI as regular playlists; smart playlists get a distinct icon in the list, sidebar and detail header. Edit on a smart playlist opens the rule-parameter editor (not the generic metadata modal), loads existing rules into form fields, and gracefully falls back when rules are temporarily unavailable. Rich filters: multi-genre include/exclude, year ranges, rating filters, plus arbitrary metadata. Polling + clock indicator while Navidrome materializes the playlist content. 8-locale coverage. - **Playlists β€” bulk-delete button + duplicate-add confirmation** *(by [@Psychotoxical](https://github.com/Psychotoxical), PRs [#290](https://github.com/Psychotoxical/psysonic/pull/290) / [#329](https://github.com/Psychotoxical/psysonic/pull/329))*: The Playlists page header gets a bulk-delete action while in selection mode. Adding a song that already exists in the target playlist now prompts for confirmation instead of silently adding a duplicate. - **Search β€” unified SongRow + paginated song results** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#303](https://github.com/Psychotoxical/psysonic/pull/303))*: Search results, Advanced Search and the Tracks Hub all share a single `SongRow` component. Song-result lists in search pages are now paginated with infinite scroll, replacing a single capped batch. - **Login β€” language picker** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#328](https://github.com/Psychotoxical/psysonic/pull/328))*: A language picker on the login page so first-run users in non-English locales aren't stuck with English UI before they have a server profile to persist their choice into. - **Song Info β€” copy fields via double-click** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#323](https://github.com/Psychotoxical/psysonic/pull/323))*: Double-clicking any field in the Song Info modal copies its value to the clipboard. Useful for grabbing track IDs, MusicBrainz tags, exact bitrates, etc. - **Mobile UI overhaul** *(by [@kilyabin](https://github.com/kilyabin), PR [#238](https://github.com/Psychotoxical/psysonic/pull/238))*: Comprehensive pass over the mobile / narrow-viewport layouts across the app β€” sidebar drawer, player view, queue, search, album / artist / playlist details. Many small layout fixes consolidated into a single coordinated PR. - **Logging β€” runtime log levels + debug log export** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#241](https://github.com/Psychotoxical/psysonic/pull/241))*: A new Settings control toggles between `info` and `debug` log levels at runtime (no app restart), plus a one-click export of the current debug log for bug reports. Used by Lucky Mix's structured debug pipeline (#278) and the LUFS analysis pipeline (#315). - **CLI β€” logs subcommand with tail / follow** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#337](https://github.com/Psychotoxical/psysonic/pull/337))*: The `psysonic` CLI gains a `logs` subcommand with `tail` and `--follow` flags for streaming the running app's log output, plus shell-completion entries for the new mode. ### Changed - **Performance suite β€” 7 PRs** *(by [@Psychotoxical](https://github.com/Psychotoxical), PRs [#245](https://github.com/Psychotoxical/psysonic/pull/245) / [#246](https://github.com/Psychotoxical/psysonic/pull/246) / [#247](https://github.com/Psychotoxical/psysonic/pull/247) / [#248](https://github.com/Psychotoxical/psysonic/pull/248) / [#249](https://github.com/Psychotoxical/psysonic/pull/249) / [#250](https://github.com/Psychotoxical/psysonic/pull/250) / [#251](https://github.com/Psychotoxical/psysonic/pull/251))*: A coordinated audit landed seven targeted optimisations. (1) Search results use `CachedImage` for thumbs to stop re-render download storms. (2) Device Sync parallelises `getAlbum` calls when syncing an artist source (eliminates N+1 on Navidrome). (3) Albums infinite-scroll prefetch margin bumped from 200px to 1500px so scrolling never visibly stalls on tile decode. (4) Resolved lyrics are now persisted to IndexedDB so they survive app restarts (no more re-fetching the same LRCLIB / NetEase / YouLyPlus result every launch). (5) Bundle splitting: rarely-visited pages are lazy-loaded and vendor chunks are split, shrinking the initial bundle. (6) Artists list-view filter pipeline + grouping are now memoised. (7) Genres grid is paginated with sort memoisation, fixing a 30-second cold-start freeze on libraries with many genres. - **Navidrome admin API β€” resilient calls + UI polish** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#260](https://github.com/Psychotoxical/psysonic/pull/260))*: The Navidrome admin REST client (`/api/user`, smart-playlist endpoints, etc.) is hardened against flaky upstreams. HTTP/1.1 forced (HTTP/2 sometimes deadlocks against Navidrome's own reverse proxy), TLS 1.2 minimum, automatic retry on transient errors, and a graceful UI retry surface in the admin tabs instead of bare error toasts. - **Subsonic β€” align Rust HTTP UA with main WebView UA** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#235](https://github.com/Psychotoxical/psysonic/pull/235))*: Rust-side HTTP requests (admin client, range-source, audio chain) now send the same `User-Agent` header as the main WebView's `fetch` calls, so server-side rate-limiters / WAFs don't see two different "clients" from the same Psysonic install. - **Linux β€” prefer PipeWire / Pulse over raw ALSA default** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#288](https://github.com/Psychotoxical/psysonic/pull/288))*: On Linux, the audio engine now explicitly tries the `pipewire` and then `pulse` ALSA aliases before falling back to `cpal`'s default-device resolution. On PipeWire-based distros (Debian 13, Ubuntu 22+, many GNOME setups) cpal's default sometimes resolves to a null sink β€” the stream opens cleanly, progress ticks fire, but no audio reaches the user. Choosing a device manually in Settings has always been the workaround; this makes that workaround unnecessary on first launch. Explicit user choice in Settings is still honoured. macOS / Windows code paths unchanged. - **Tauri devtools β€” disabled in production builds** *(by [@Psychotoxical](https://github.com/Psychotoxical), PRs [#307](https://github.com/Psychotoxical/psysonic/pull/307) / [#310](https://github.com/Psychotoxical/psysonic/pull/310))*: Devtools no longer auto-open in dev, and are entirely disabled in production. Use **Ctrl+Shift+I** in the dev build. - **Dependencies β€” rustls-webpki + postcss bumps** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#306](https://github.com/Psychotoxical/psysonic/pull/306))*: Routine security bumps. ### Fixed - **Streaming-seek UI freeze + progress snapback** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#236](https://github.com/Psychotoxical/psysonic/pull/236), closes [#218](https://github.com/Psychotoxical/psysonic/issues/218))*: Seeking on a streaming track could freeze the player UI for the seek duration and then snap the progress indicator back to the pre-seek position before settling. The seek path no longer blocks the render thread on the HTTP range request, and progress ticks during the pending seek window are suppressed so the playhead doesn't jitter. - **Cross-device resume β€” flush queue position on pause + all exit paths** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#318](https://github.com/Psychotoxical/psysonic/pull/318))*: Server-side play-queue position was effectively pinned at 0 on long sessions: `syncQueueToServer` only fired on track-change / seek / queue edit (with a 5s debounce), so listening to a long track for minutes without seeking and then closing the app would make Device 2 restart from byte 0. Fixed via a 15-second heartbeat from `audio:progress` while playing, an immediate flush on pause, and a shared force-quit path used by Tray Exit and the macOS red close button (both previously called `app.exit(0)` directly and bypassed the JS close handler). - **imageCache β€” per-component object URLs (no shared LRU)** *(by [@Psychotoxical](https://github.com/Psychotoxical), PRs [#313](https://github.com/Psychotoxical/psysonic/pull/313) / [#321](https://github.com/Psychotoxical/psysonic/pull/321))*: The shared blob-URL LRU cache could revoke a URL while another component still rendered against it, producing `Failed to load resource: blob:` floods. Cover URLs are now refcounted across consumers and only revoked when the last consumer unmounts. - **Queue β€” preserve scroll context on manual click** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#314](https://github.com/Psychotoxical/psysonic/pull/314))*: Clicking a track in the queue list while scrolled away from the now-playing position used to snap the list back to the current track. The queue list now preserves the user's scroll context on manual clicks. - **Mini player β€” drop saved position when its monitor is gone** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#280](https://github.com/Psychotoxical/psysonic/pull/280))*: Persisted mini-player coordinates from a disconnected monitor would re-place the mini off-screen on next launch. The persisted position is now sanity-checked against the current monitor layout and discarded if no monitor covers it. - **Mini player β€” portal volume popover so it cannot be clipped** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#279](https://github.com/Psychotoxical/psysonic/pull/279))*: The mini-player volume popover is now portal-rendered so the small mini window's `overflow` cannot clip it. - **WebView2 idle hooks on Windows** *(by [@peri4ko](https://github.com/peri4ko), PR [#273](https://github.com/Psychotoxical/psysonic/pull/273), follow-up by [@Psychotoxical](https://github.com/Psychotoxical), PR [#276](https://github.com/Psychotoxical/psysonic/pull/276))*: On Windows, hiding a window via Tauri's `win.hide()` does not stop WebView2's compositor and main-thread work β€” CSS `@keyframes`, decorative canvas loops and periodic JS timers can keep running, with `document.hidden` / `visibilitychange` not aligned with native hidden state. A new `__psyHidden` JS flag + `data-psy-native-hidden` attribute are set from injected eval before `hide()` on tray, mini, native mini-close and related paths, walking `#root` descendants to pause `animation-play-state` for `@keyframes`. CSS rules pause portaled UI globally. `MiniPlayer`, `WaveformSeek` (drops to a 400ms poll), `Hero` (skips its 10s carousel interval), `PlaybackScheduleBadge` and `usePlaybackScheduleRemaining` all skip work while hidden. Linux / macOS paths are not gated β€” same flows are safer or unchanged. Follow-up PR tightens the inject targets after review. - **Linux β€” stop Wayland GTK drag proxy and PsyDnD ghost** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#268](https://github.com/Psychotoxical/psysonic/pull/268))*: Drag operations on Wayland could leave a GTK drag proxy + PsyDnD ghost lingering on the screen until the next pointer move. Both are now torn down on drag end / cancel. - **Audio β€” defer chained-track volume to gapless transition** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#277](https://github.com/Psychotoxical/psysonic/pull/277))*: Volume changes applied during a gapless preload could affect the still-playing track instead of the upcoming one; volume is now deferred to the actual transition. - **Search β€” right-click context menu on artist + album rows** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#302](https://github.com/Psychotoxical/psysonic/pull/302))*: Artist + album rows in the search results page now respond to right-click with the full context menu, matching song rows. - **Search β€” enqueue on live-search click + reposition CM with right-click** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#298](https://github.com/Psychotoxical/psysonic/pull/298))*: Clicking a live-search result correctly enqueues + plays; right-clicking re-positions the context menu instead of opening a second one. - **Server switch β€” keep cached playback alive + Settings tab-id typo** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#262](https://github.com/Psychotoxical/psysonic/pull/262))*: Switching the active server used to tear down playback even when the same track was still cached locally. The cache is now consulted before tearing down. A typo in the Settings tab id that broke deep-linking to specific tabs is also fixed. - **Mainstage β€” refresh Home when active server changes** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#291](https://github.com/Psychotoxical/psysonic/pull/291))*: The Home page didn't refresh when the user switched servers; it now reloads its rails for the new active server. - **Queue panel resize handle no longer blocked by main scroll hit-test** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#324](https://github.com/Psychotoxical/psysonic/pull/324))*: The main page's scroll capture region overlapped the queue panel's resize handle in some viewport widths, making the handle un-grabbable. Hit-test fixed. - **Queue full album after current; filter smart playlists in add-targets** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#322](https://github.com/Psychotoxical/psysonic/pull/322))*: "Queue full album" now reliably enqueues the entire album after the currently-playing track. The "Add to playlist" target list now filters out smart playlists (you can't manually add tracks to a rule-based playlist). - **Persist queue panel visibility + drop dead loudness binding** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#336](https://github.com/Psychotoxical/psysonic/pull/336))*: Queue panel open/closed state now persists across app restarts. A dead loudness-store binding left over from the LUFS branch's pre-merge state is removed. - **Analysis cache β€” waveform logging fix** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#320](https://github.com/Psychotoxical/psysonic/pull/320))*: Logging in the analysis cache was wrong about which waveform path was active in the LUFS pipeline; corrected. - **Toolbar icons β€” swap Gapless / Infinite Queue** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#284](https://github.com/Psychotoxical/psysonic/pull/284), closes [#274](https://github.com/Psychotoxical/psysonic/issues/274))*: The Gapless and Infinite Queue toolbar icons were swapped; correct icons now match their actions. - **UI perf spike on pointer-gesture overlap** *(by [@cucadmuh](https://github.com/cucadmuh) and [@Psychotoxical](https://github.com/Psychotoxical), PRs [#281](https://github.com/Psychotoxical/psysonic/pull/281) / [#282](https://github.com/Psychotoxical/psysonic/pull/282) / [#283](https://github.com/Psychotoxical/psysonic/pull/283))*: A short-lived perf spike caused by overlapping pointer gestures was first patched in #281, reverted in #282 after a regression, and re-landed cleanly in #283. - **Subsonic β€” log pingWithCredentials failures** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#312](https://github.com/Psychotoxical/psysonic/pull/312))*: The credentials ping no longer fails silently on auth errors; failures are logged so server-side issues are diagnosable from the debug log. - **Fullscreen β€” remove WebKitGTK halo ring on mesh blobs** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#239](https://github.com/Psychotoxical/psysonic/pull/239))*: The fullscreen mesh-blob background showed a faint halo ring on WebKitGTK due to a layer compositing artefact; replaced with softer gradient stops that don't trigger the halo. - **Titlebar β€” hide traffic-light glyphs until hover** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#243](https://github.com/Psychotoxical/psysonic/pull/243))*: The macOS-style traffic-light glyphs in the custom titlebar now match real macOS behaviour: hidden at rest, only revealed on hover. - **Discord β€” debug logging for Rich Presence IPC path** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#330](https://github.com/Psychotoxical/psysonic/pull/330))*: Debug-build only logging on the Discord Rich Presence IPC pipe to make connection issues diagnosable from the debug log. - **UI β€” overlay scrollbars + resizer hit-test + Linux mini wheel** *(by [@cucadmuh](https://github.com/cucadmuh), PR [#255](https://github.com/Psychotoxical/psysonic/pull/255))*: A trio of related UI fixes: overlay-scrollbar mode now reports scrolling state correctly, the column-resizer hit-test is widened, and Linux mini-player mouse-wheel scrolling is responsive again. ### Contributors - [@cucadmuh](https://github.com/cucadmuh) β€” Loudness Normalization headline (#315), LUFS stabilisation (#326), Lucky Mix (#278), Queue undo/redo (#331), Sleep Timer ring UI (#272), Library deep links (#261), Magic-string invites (#258), CLI logs subcommand (#337), runtime log levels + debug log export (#241), smart playlists workflow (#289), several streaming + UI fixes. - [@Psychotoxical](https://github.com/Psychotoxical) β€” Orbit (#304), Now-Playing dashboard (#266 / #267), Tracks Hub (#300), Genres tag-cloud (#311), Settings refactor (#259), perf suite (#245–#251), and most of the cross-device + admin-API hardening work. - [@peri4ko](https://github.com/peri4ko) β€” Windows WebView2 idle hooks (#273). - [@kilyabin](https://github.com/kilyabin) β€” Mobile UI overhaul (#238). ## [1.43.0] - 2026-04-20 ### Added - **User Management β€” admin-gated tab in Settings** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: When the active server is Navidrome and the logged-in user is an admin, Settings gets a new "Users" tab. Lists every user with username, display name, email, last-access timestamp and assigned libraries. Add / edit / delete via Navidrome's native REST API (`/api/user`) using a Bearer token obtained from `/auth/login` β€” the Subsonic API doesn't expose this, so non-Navidrome servers don't get the tab. - **User Management β€” per-user library assignment** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#222](https://github.com/Psychotoxical/psysonic/pull/222))*: Mirrors the Navidrome web client. Non-admin users get a checkbox picker showing every library on the server; the picker is hidden for admins (Navidrome auto-grants them access to all libraries). Inline validation prevents saving a non-admin with zero libraries. - **User Management β€” last-access timestamp per user** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Each row shows when the user was last active, formatted as a localised relative time (`vor 5 Min.`, `2h ago`, etc.) using `Intl.RelativeTimeFormat`. Tooltip carries the absolute timestamp. Users who have never logged in show "Never". - **Seekable streaming + instant local playback β€” first cut** *(by [@Psychotoxical](https://github.com/Psychotoxical) and [@cucadmuh](https://github.com/cucadmuh))*: New `RangedHttpSource` + `LocalFileSource` audio backends. Seek operations on remote tracks now issue HTTP `Range` requests instead of restarting the stream from byte 0, and locally cached files start playing instantly without going through the HTTP path at all. WaveformSeek commits the seek on mouseup (not during drag), and progress ticks during a drag are ignored so the playhead doesn't jitter back and forth. **Note:** the underlying seek/buffer behaviour is not fully sorted yet β€” expect follow-up changes in the next releases as edge cases (slow proxies, partial-content retries, codec-specific quirks) get ironed out. - **Mini player β€” queue-style meta block, action toolbar, vertical volume slider** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The mini's right column gets a richer track-info block matching the queue panel's styling. A dedicated action toolbar (love / queue / context menu) sits below the transport. The horizontal volume slider is replaced by a tall vertical one on the right edge for a more compact footprint. - **Settings β€” compact spacing pass + row hover affordance** *(by [@Psychotoxical](https://github.com/Psychotoxical), PR [#223](https://github.com/Psychotoxical/psysonic/pull/223))*: Section margins, card padding and divider spacing all tightened β€” every Settings tab fits more content per viewport. Each toggle row gains a subtle accent-tinted hover background that bleeds to the card edges so the active row is visually obvious. - **Floating player bar β€” toggleable variant** *(by [@kveld9](https://github.com/kveld9), PR [#216](https://github.com/Psychotoxical/psysonic/pull/216))*: Settings β†’ Appearance β†’ "Floating player bar" turns the player bar into a floating, rounded panel that sits above the page content with a margin around all four edges. Off by default. Solid background, works with every theme. - **Floating player bar β€” liquid-glass look on macOS and Windows** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: When the floating bar is enabled, macOS and Windows users get a gentler glass-effect background (subtle blur + tint) on top of @kveld9's solid variant. Linux keeps the solid look β€” WebKitGTK's `backdrop-filter` cost is too high for an always-visible panel. A new `data-platform` attribute on `` is the generic platform-gate that other CSS can hook into. - **NVIDIA proprietary driver β€” DMA-BUF auto-disabled on Linux** *(by [@kveld9](https://github.com/kveld9), PR [#217](https://github.com/Psychotoxical/psysonic/pull/217), refactored by [@Psychotoxical](https://github.com/Psychotoxical))*: Detects the NVIDIA proprietary driver at startup and sets `WEBKIT_DISABLE_DMABUF_RENDERER=1` for the WebKitGTK process, avoiding rendering glitches that show up specifically on that combo. Confirmed via blind A/B testing β€” only the proprietary driver is targeted; Nouveau / AMD / Intel are not touched. - **Lyrics β€” cubic ease-out scroll animator** *(by [@kilyabin](https://github.com/kilyabin), PRs [#214](https://github.com/Psychotoxical/psysonic/pull/214) / [#215](https://github.com/Psychotoxical/psysonic/pull/215))*: The lyrics auto-scroll animation is replaced by a smoother cubic ease-out curve (renamed internally from `springScroll` to `easeScroll`). Active line transitions are noticeably less jerky on long line-spacing changes. - **Fullscreen lyrics β€” fade bottom edge of plain lyrics scroll viewport** *(by [@kilyabin](https://github.com/kilyabin))*: Plain (unsynced) lyrics in the fullscreen player now fade out at the bottom of the scroll viewport via a `mask-image` gradient, matching the existing fade on the synced-lyrics overlay. ### Fixed - **Mini player β€” main window minimises on open + width cap on non-tiling WMs** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Opening the mini now reliably minimises the main window (previously hit-or-miss on some WMs), and the mini's width is capped on non-tiling Linux WMs so it doesn't open larger than its intended footprint when the user's WM hands it the full screen. - **Artist page β€” Top Songs continues playback past the last track** *(by [@kveld9](https://github.com/kveld9), PR [#220](https://github.com/Psychotoxical/psysonic/pull/220))*: Playing a song from the Artist page's Top Songs row no longer stops after the row's last track β€” the queue continues into the surrounding context as intended. - **Padding fixes across several pages** *(by [@kveld9](https://github.com/kveld9), PR [#221](https://github.com/Psychotoxical/psysonic/pull/221))*: Layout polish, mostly aligning content to the page-level container padding instead of the inner card padding. - **Jayfin theme β€” WCAG AA contrast fixes for nav + primary buttons** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Hover and active states on the Jayfin theme's sidebar nav items and primary buttons now pass WCAG AA contrast against the underlying background. - **Lyrics β€” sidebar lyrics with YouLy+ source render as a single line** *(by [@kilyabin](https://github.com/kilyabin))*: Lines from the YouLyrics+ source were being split across multiple visual lines in the QueuePanel lyrics pane. Now collapse onto one line as intended. - **Settings β†’ Lyrics Sources β€” drag-and-drop survives mode toggle** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Reordering lyrics sources via drag-and-drop no longer resets when toggling the synced-vs-plain mode. - **Folder browser β€” auto-contrast text on selected row** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Selected rows in the folder browser now compute text colour from the row's background luminance, so light themes don't paint white-on-white text. - **Titlebar β€” theme-independent traffic-lights + song pill** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The macOS-style traffic-lights and the now-playing pill in the titlebar use fixed colours instead of theme tokens, so they stay legible on every theme without needing per-theme overrides. ### Reverted - **Reverted: fs-player WebKitGTK CPU-cut patch** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: An earlier perf patch in the Fullscreen Player that disabled compositing under WebKitGTK turned out to cause animation regressions in real-world use. Reverted; the original code path is back. ### Changed - **AudioMuse toggle β€” Alpha badge dropped** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The AudioMuse-AI integration has been stable for several releases; the "Alpha" tag in Settings β†’ Server is removed. ## [1.42.1] - 2026-04-19 > **🚨 Critical bug fix for Windows users.** On 1.42.0, opening the mini player on Windows could stall Tauri's event loop: the mini would appear as a blank white window, neither the main window nor the mini could be closed, and the only way out was killing the process via Task Manager. **Please update immediately if you're on Windows 1.42.0.** macOS and Linux were not affected. ### Fixed - **Mini player no longer hangs the app on Windows** *([@Psychotoxical](https://github.com/Psychotoxical))*: Creating the second WebView2 webview lazily from the `open_mini_player` invoke handler reliably froze the app on Windows β€” the mini opened blank, both windows became unresponsive, and the user had to kill the process from Task Manager. The builder + `main.minimize()` combo racing against WebView2's first paint was the trigger. The mini webview is now pre-built hidden in Tauri's `.setup()` on Windows, so the first open is a pure show/hide instead of creation + minimize. `open_mini_player` is simpler on all platforms, the minimize-main dance around show/hide is skipped on Windows, and Windows also goes back to the native window decorations (the earlier `decorations: false` mini titlebar was part of the hang surface). - **Mini player syncs immediately on first open** *([@Psychotoxical](https://github.com/Psychotoxical))*: With the mini pre-created on Windows, the mount-time `mini:ready` event could race past the main window's bridge listener and leave the mini without a snapshot when the user actually opened it. The mini now also re-emits `mini:ready` on every window focus, so opening the mini always triggers a fresh sync regardless of startup ordering. ### Added - **Optional β€œPreload mini player” setting on Linux + macOS** *([@Psychotoxical](https://github.com/Psychotoxical))*: Settings β†’ General β†’ App behaviour. Off by default. When enabled, the mini player window is built hidden at app start so the first open is instant instead of waiting a few seconds for WebKit to boot + React to hydrate + the bridge snapshot to arrive. Costs one extra WebKit process in the background permanently (~50–100 MB RAM). Windows always preloads regardless of this toggle β€” it's how we work around the hang above, not an opt-in feature there. ## [1.42.0] - 2026-04-19 > **πŸ› οΈ Note on the 1.41.0 jump:** The 1.41.0 tag exists as an internal Draft release on GitHub β€” it was used to wire up and verify the Cachix substituter pipeline and never went public. **1.42.0 is the first public release after 1.40.0** and consolidates everything that was prepared for 1.41.0 plus the work landed on top in the days since. > > **❄️ Cachix is live for NixOS users.** The `psysonic.cachix.org` substituter is now actually fed by every release. Earlier 1.40.x runs were silently skipping the cache push (see *Fixed* below), so the first user to ask for a given output paid the full compile cost. Starting with 1.42.0, `nix run github:Psychotoxical/psysonic` and the NixOS module both pull the prebuilt closure straight from Cachix β€” no local Rust + symphonia + libopus build required. ### Added - **Mini player β€” feature-complete second cut** *(Issue [#162](https://github.com/Psychotoxical/psysonic/issues/162), by [@Psychotoxical](https://github.com/Psychotoxical))*: The early-alpha mini from the internal 1.41.0 prep gets the rest of the workflow it was missing. - **Expandable queue panel** with full track list, search-style overlay scrollbar (no width-eating gutter), drag-to-reorder using the existing PsyDnD system, and a localized right-click context menu (Play now / Remove from queue / Open album / Go to artist / Favorite / Song info β€” all forwarded to the main window via Tauri events so the source-of-truth playerStore stays consistent). - **Custom in-page titlebar** on Windows + Linux with a drag region, the current track title and the queue / pin / open-main / close action icons. macOS keeps the native traffic-lights titlebar so the system look is preserved. The lower toolbar from the alpha is gone β€” its four buttons live in the titlebar now. - **Persistent geometry**: window position, expanded-queue height and queue-open state all survive an app restart. Position is written to `/mini_player_pos.json` on every move (throttled), and re-applied after each show β€” Linux WMs (Mutter/KWin) re-centre hidden windows on show, so without re-applying the position would be lost on the second open. - **User-bindable keyboard shortcut** in Settings β†’ Shortcuts (`open-mini-player`, default unbound). The same chord toggles between main and mini regardless of which window has focus. - **Layout polish**: cover shrinks 112 β†’ 84 px, the right column gets title / artist / transport in a single block, progress + toolbar take full width. - **Live theme / font / language sync**: changes in the main window propagate to an open mini via the shared localStorage `storage` event β€” no need to close + re-open the mini after rebinding a shortcut or switching themes. - **Always-on-top reliability fix**: WMs that silently ignore `set_always_on_top(true)` when the flag is "already true" (KWin, certain Mutter releases) get a forced false β†’ true cycle so the constraint is actually re-evaluated. The frontend also re-asserts the pin state on mount and on focus, so the user no longer has to click the pin button twice for it to stick. - **Player bar β€” click-to-toggle duration / remaining time** *(contributed by [@kveld9](https://github.com/kveld9), PR [#212](https://github.com/Psychotoxical/psysonic/pull/212))*: Click the time read-out in the player bar to swap between total duration (`3:45`) and remaining time (`-2:34`). Updates live, persisted to `themeStore.showRemainingTime`. A small swap icon (⇄) and hover highlight signal the interaction. - **Queue β€” ReplayGain in tech strip, expandable badge** *(Issue [#195](https://github.com/Psychotoxical/psysonic/issues/195), originally by [@cucadmuh](https://github.com/cucadmuh) in PRs [#196](https://github.com/Psychotoxical/psysonic/pull/196) / [#201](https://github.com/Psychotoxical/psysonic/pull/201) β€” UX iteration by [@Psychotoxical](https://github.com/Psychotoxical) on cucadmuh's feedback)*: Tracks with ReplayGain metadata now show a small `RG βŒ„` pill at the end of the codec/bitrate/sample-rate strip. Hover reveals the values via tooltip; click expands a second line ("ReplayGain Β· T -8.9 dB Β· A -11.0 dB Β· Peak 0.998") that is persisted across sessions. Hides itself for tracks without RG metadata. - **Changelog β€” sidebar banner + dedicated `/whats-new` page** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The auto-popup modal that nagged the user on first launch after each update is replaced by a discreet sidebar banner. Clicking it opens a full `/whats-new` page that renders the latest CHANGELOG section in app β€” no separate Markdown viewer, no broken links to GitHub. - **Favorites β€” genre column + Top Favorite Artists row** *(Issue [#87](https://github.com/Psychotoxical/psysonic/issues/87), by [@Psychotoxical](https://github.com/Psychotoxical))*: The Favorites tracklist now has a toggleable Genre column (alongside the existing Album column and multi-genre filter). A new horizontally scrolling "Top Favorite Artists" row sits between Radio Stations and Songs, aggregated from starred tracks and sorted by star count. Clicking an artist card narrows the song list to that artist. - **Compilation filter on All Albums** *(Issue [#65](https://github.com/Psychotoxical/psysonic/issues/65), by [@Psychotoxical](https://github.com/Psychotoxical))*: A tri-state toggle in the Albums page header (All / Only compilations / Hide compilations) that reads the OpenSubsonic `isCompilation` tag exposed by Navidrome 0.61+. Client-side filter, no additional server calls. Translated into all 8 supported locales. - **Sticky header on Albums, New Releases, Artists** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The header row with search/sort/genre/year controls now pins to the top while scrolling, so filters stay reachable without jumping back up. Works the same on all three browse pages. - **Device Sync β€” album artist on both panels** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Album entries in both the library (left) and on-device (right) panels now display `Album Β· Artist` inline, so sampler discs and self-titled albums are no longer guesswork. Playlists unchanged. - **NixOS β€” first-class flake install guide** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PRs [#209](https://github.com/Psychotoxical/psysonic/pull/209) / [#210](https://github.com/Psychotoxical/psysonic/pull/210))*: A new top-level `nixos-install.md` walks through adding Psysonic as a flake input, installing via `environment.systemPackages` / `home.packages`, and wiring up the public `psysonic.cachix.org` substituter so every NixOS user pulls prebuilt binaries. README links to it directly. - **README β€” AppImage in the Linux install options + Cachix badge** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The Linux install section now lists AppImage alongside `.deb`, `.rpm`, AUR and Nix flakes. A Cachix badge on the README header signals that NixOS users get prebuilt binaries. ### Changed - **Genre filter β€” portal popover** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The inline tagbox + dropdown (capped at 60 entries, ate header space when expanded) is replaced by a compact button that opens a portal-rendered popover with a search field and the full scrollable list of genres. Selected genres sort to the top. Used on Albums, New Releases, Random Albums and Favorites. - **Year filter β€” portal popover** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The From/To number inputs in the Albums header became a single button with a popover mirroring the genre filter pattern. When the filter is active, the button shows the range (e.g. `2020–2024`) in accent colour. - **Sort picker β€” portal dropdown** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The two sort buttons on Albums (`A–Z (Album)`, `A–Z (Artist)`) collapse into one dropdown button showing the current choice. Generic `SortDropdown` component, reusable for other pages. - **Device Sync β€” album/playlist meta inline** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: `BrowserRow` renders secondary info inline with a `Β·` separator in muted colour instead of a separate right-aligned column, matching the on-device panel's format. - **README β€” Arch/AUR fold-up** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The Arch / AUR install instructions are folded into the Linux install section so the README stops scrolling forever. ### Fixed - **Player bar β€” black-flash on WebKitGTK** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Linux users occasionally saw the entire player bar paint fully black for one frame when an unrelated layer elsewhere on the page invalidated. `contain: layout paint` makes the bar its own paint boundary so it can no longer be pulled into a surrounding dirty rect. No-op on platforms that don't exhibit the flash (Wayland-with-GPU, Chromium webviews on Windows / macOS). - **Player bar β€” time-toggle tooltip uses the in-app TooltipPortal** *(follow-up to PR [#212](https://github.com/Psychotoxical/psysonic/pull/212), by [@Psychotoxical](https://github.com/Psychotoxical))*: The new time-swap control was rendering the native browser `title=` tooltip (unstyled OS popup, ignored by every other control). Switched to `data-tooltip="…"` so it matches every other player-bar tooltip. - **Fullscreen player β€” lyrics menu toggle + readability** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Re-clicking the mic icon now actually closes the lyrics settings panel instead of the outside-click handler closing it and the click re-opening it β€” the trigger button is excluded from the outside-check. The panel itself is now a solid surface (no backdrop blur, near-opaque background, higher-contrast button text) so settings remain readable over the busy fullscreen background. - **i18n β€” ArtistCardLocal album count** *(contributed by [@cucadmuh](https://github.com/cucadmuh))*: Local artist cards were rendering the album count with hardcoded German (`Album` / `Alben`). Switched to the existing plural-aware `artists.albumCount` key which already covers all 8 locales including Russian Slavic plurals. - **Release CI β€” Cachix never receiving the psysonic closure** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: `cachix-action` installs its post-build hook via `NIX_USER_CONF_FILES`, but the Determinate Nix daemon that runs the actual builds reads the system nix.conf β€” so the hook never fired. Only a couple of early prep paths ever reached the cache, never the compiled `psysonic` output. The release workflow now pushes the full closure explicitly after `nix build`; Cachix dedupes against paths already present, so redundancy is cheap. ### Contributors - [@kveld9](https://github.com/kveld9) β€” click-to-toggle duration / remaining time in the player bar. - [@cucadmuh](https://github.com/cucadmuh) β€” i18n fix for ArtistCardLocal, ReplayGain UX feedback that drove the expandable badge, NixOS install guide, README polish. --- ## [1.40.0] - 2026-04-18 ### Added - **macOS β€” signed and notarized builds** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: macOS releases are now signed with a Developer ID Application certificate and notarized by Apple. Gatekeeper no longer shows the "app from unidentified developer" dialog; the DMG opens and runs with a single click on both Apple Silicon and Intel Macs. Signing + notarization happens in CI on every release. - **macOS β€” in-app auto-update** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The Tauri Updater plugin is now active on macOS. When a new release is available, clicking **Install now** in the notification modal downloads the signed `.app.tar.gz` bundle, verifies its minisign signature against the bundled public key, replaces `/Applications/Psysonic.app` in place, and relaunches the app β€” all in one click, no Gatekeeper re-approval, no manual DMG handling. The modal shows trust badges ("Notarized by Apple" + "Signature verified"), a 3-second restart countdown after install with a manual "Restart now" option, and hides redundant buttons during each download/install phase. Windows and Linux continue to use the existing "download installer / point to folder" flow until their signing pipelines are wired up. - **WebKitGTK wheel scroll mode (Linux)** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#207](https://github.com/Psychotoxical/psysonic/pull/207))*: The Linux build now defaults to WebKitGTK's native smooth (kinetic) wheel scrolling and exposes a toggle in Settings β†’ General to fall back to classic linear line-by-line scroll. Existing installs are migrated to smooth scrolling once, after which the toggle is fully user-controlled. ### Changed - **Device Sync β€” fixed naming scheme + playlist folders** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The user-configurable filename template is gone. Every sync now writes files under a single, non-negotiable scheme: - Album / artist sources: `{AlbumArtist}/{Album}/{TrackNum:02d} - {Title}.{ext}` - Playlist sources: `Playlists/{PlaylistName}/{Index:02d} - {Artist} - {Title}.{ext}` plus a self-contained `.m3u8` that references sibling filenames. **Why:** different OSes normalised separators and special characters differently, so the same library synced from macOS and then plugged into a Windows machine appeared "different" and re-downloaded every album. The fixed scheme ends that forever. **Playlist folders instead of the album tree:** playlists used to be scattered across the album structure as `.m3u8` references. For playlists with 40 artists that meant 40 new folders on the stick. Now every playlist is one self-contained folder; the `.m3u8` sits inside it and references siblings, so you can copy the whole folder anywhere. **Migration for existing sticks:** a "Reorganize existing files…" button on the Device Sync page reads the legacy template from the v1 manifest, computes per-track rename pairs, detects collisions, and executes atomic `fs::rename`s. Empty directories left behind are cleaned up automatically. Playlist tracks synced under the old scheme are left for the next sync to re-download into the new playlist folder, rather than being force-moved. **Album-Artist fallback:** libraries without an albumArtist tag fall back to the track artist β€” "Unknown Artist" is only ever a last-resort placeholder. ### Fixed - **WCAG contrast audit β€” Middle-Earth theme** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Raised `--warning`, `--border`, `--text-muted`, `--positive`, and multiple component-level overrides (connection indicators, nav section labels, lyrics status, queue duration, player time, glass-panel muted text) to AA thresholds on all background variants. The warm bronze / aged-parchment palette is preserved β€” no cool tones introduced. - **WCAG contrast audit β€” Nucleo theme** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Darkened `--warning`, `--border`, `--text-muted`, and `--positive` tokens to reach AA on the warm cream palette; added a component-level override for the column resize grip (default `--ctp-surface1` was 1.08:1 on the card background, effectively invisible) using the new `--border` token at 2px width. Brass-and-parchment aesthetic preserved. ### Contributors - **PR [#205](https://github.com/Psychotoxical/psysonic/pull/205)** β€” Apple Music-style scrolling lyrics with spring-physics scroll, by [@kilyabin](https://github.com/kilyabin). - **PR [#206](https://github.com/Psychotoxical/psysonic/pull/206)** β€” Golos Text + Unbounded fonts with Cyrillic support, by [@kilyabin](https://github.com/kilyabin). - **PR [#207](https://github.com/Psychotoxical/psysonic/pull/207)** β€” WebKitGTK wheel scroll mode toggle, by [@cucadmuh](https://github.com/cucadmuh). All three now credited in Settings β†’ About. --- ## [1.34.13] - 2026-04-17 ### Added - **YouLyPlus β€” word-by-word synced lyrics (karaoke)** *(Issue [#172](https://github.com/Psychotoxical/psysonic/issues/172), by [@Psychotoxical](https://github.com/Psychotoxical))*: Settings β†’ Lyrics now exposes a mode toggle between the existing **Standard** pipeline (Server tags + LRCLIB + Netease, configurable order) and a new **YouLyPlus** mode that fetches karaoke-style word-sync lyrics from the public `lyricsplus` aggregator (Apple Music / Spotify / Musixmatch / QQ Music). When a track has no YouLyPlus entry the app silently falls back to the Standard pipeline, so obscure titles still resolve. Active word highlighting in both the sidebar Lyrics pane and the Fullscreen Player. Five backend mirrors are tried on network failure; no API keys on the user side β€” subscription costs are borne by the lyricsplus operator. - **Static-only lyrics option** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: A new toggle renders synced lyrics as plain static text β€” no auto-scroll, no word highlighting β€” for users who prefer to read rather than follow. Works in both Standard and YouLyPlus modes. - **Discord Rich Presence β€” collapsible advanced options** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The *Fetch covers from Apple Music* toggle and the *Custom text templates* form are now tucked under a single collapsible **Advanced Discord options** header (default collapsed) that only appears when Discord Rich Presence is enabled. Reduces vertical noise in Settings β†’ General for the common case. ### Fixed - **macOS β€” spurious microphone permission prompt (real fix)** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The 1.34.12 attempt of removing `NSMicrophoneUsageDescription` did not actually suppress the prompt β€” on modern macOS, TCC fires at AudioUnit instantiation time, not at Info.plist level. Root cause: `cpal` (via `rodio`) instantiates an `AUHAL` output unit (`IOType::HalOutput`), which macOS classifies as input-capable even for playback-only apps. Psysonic now ships a vendored `cpal 0.15.3` at `src-tauri/patches/cpal-0.15.3/` wired via `[patch.crates-io]`; the patch forces `IOType::DefaultOutput` for all output streams, which never touches input and never triggers the mic dialog. **Tradeoff:** per-device output selection is a no-op on macOS β€” the stream always follows the system default (change via System Settings β†’ Sound or the menu-bar speaker icon). Matches the behaviour of Apple Music and Spotify on macOS. Settings surfaces this with an explanatory notice on macOS and hides the device picker there. --- ## [1.34.12] - 2026-04-17 ### Added - **Playback source indicator in Queue** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#201](https://github.com/Psychotoxical/psysonic/pull/201))*: The current-track tech strip in the Queue panel now shows a **source badge** indicating how the track was loaded: `stream` (live from server), `preloaded` (buffered before playback), or `cache` (served from local hot cache). Preload tracking is wired through the Rust audio engine so the badge reflects actual playback origin, not just current state. - **ReplayGain metadata in Queue tech strip** *(Issue [#195](https://github.com/Psychotoxical/psysonic/issues/195), contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#196](https://github.com/Psychotoxical/psysonic/pull/196))*: The current-track tech strip now shows track and album ReplayGain values alongside bitrate and format when the file contains gain tags. - **Discord Rich Presence enhancements** *(contributed by [@kveld9](https://github.com/kveld9), PR [#198](https://github.com/Psychotoxical/psysonic/pull/198))*: Discord Rich Presence received several improvements: dead/unused fields removed, the `{paused}` placeholder that Discord does not support was dropped, and a `timeChanged` invoke loop that fired redundantly on every progress tick was eliminated. The DRP timer is now accurate and stable. - **Context menu in Search results** *(contributed by [@kveld9](https://github.com/kveld9), PR [#191](https://github.com/Psychotoxical/psysonic/pull/191))*: Song rows in the Search panel now support the full right-click context menu (Play, Queue, Playlist, etc.) β€” previously search results were click-only with no context actions. - **Spotify CSV playlist import** *(contributed by [@kveld9](https://github.com/kveld9), PR [#190](https://github.com/Psychotoxical/psysonic/pull/190))*: Playlists exported from Spotify as CSV can now be imported directly into Psysonic. Tracks are matched by ISRC when available, with title/artist fallback. Unmatched tracks are listed in a report after import. Duplicate checking is done before writing. - **CLI completions and expanded player controls** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#187](https://github.com/Psychotoxical/psysonic/pull/187))*: The `psysonic` CLI gains shell completions for bash/fish/zsh/elvish, new subcommands for library browsing and audio device listing, a server switcher command, and an opaque play-ID scheme for stable track references. The tray icon on Linux no longer requires `libayatana-appindicator` / `libindicator` β€” it falls back gracefully when the library is absent. - **Albums and Playlists header redesign** *(contributed by [@kveld9](https://github.com/kveld9), PR [#186](https://github.com/Psychotoxical/psysonic/pull/186))*: The header sections on the Albums and Playlists pages have been redesigned for a cleaner, more consistent layout. - **Favorites page redesign** *(contributed by [@kveld9](https://github.com/kveld9), PR [#184](https://github.com/Psychotoxical/psysonic/pull/184))*: The Favorites page has been overhauled with sortable columns, a gender filter, an age range filter, and additional metadata columns. - **Split Mix navigation mode** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: A new toggle in Settings switches the Mix section between a single **Build a Mix** hub entry and **two separate sidebar entries** β€” Random Mix and Random Albums β€” for users who prefer direct access. Navigation items are now defined in `src/config/navItems.ts`; the toggle is stored as `randomNavMode` in authStore. - **Device Sync improvements** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Device Sync received several updates: a JSON manifest is now written to the device root on every sync (and read back automatically when the device is mounted); a **Cancel** button interrupts a running sync cleanly; a font picker was added to the sync page; sync status display was fixed; and the filename template builder now works correctly on all platforms. - **Radio β€” ICY StreamTitle forwarded to MPRIS** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: While playing internet radio, the current song title parsed from ICY `StreamTitle` metadata is now forwarded to MPRIS `xesam:title` on Linux so that the track name appears in desktop notification shells and media controls. - **Help page β€” expanded coverage** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Added missing help sections covering Device Sync, Internet Radio, CLI usage, Playlists, Infinite Queue, Lyrics sources, Audio device selection, Backup & Restore, and Now Playing details. - **Tracklist column reset and privacy policy** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: A reset button in the tracklist column picker restores the default column set. The Device Sync page received a cross-platform filename template fix. A privacy policy page was added documenting data usage for Last.fm, LRCLIB, NetEase, and Discord. ### Fixed - **Streaming playback stability** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#200](https://github.com/Psychotoxical/psysonic/pull/200))*: Several edge cases in the Rust audio engine around stream start, mid-track seeking, and track transitions were hardened. Cache promotion (moving a preloaded track into the hot cache) is now safer under concurrent access. Stream decoder errors during transitions no longer leave the engine in a stuck state. - **CSV import reliability** *(contributed by [@kveld9](https://github.com/kveld9), PR [#199](https://github.com/Psychotoxical/psysonic/pull/199))*: The CSV import pipeline now guards the `ISRC` field type before calling `toUpperCase`, preventing a crash on rows with numeric or null ISRC values. The playlist public/private toggle in the edit modal (accidentally removed during a post-merge fix) is restored. - **Tracklist column picker** *(contributed by [@kveld9](https://github.com/kveld9), PR [#188](https://github.com/Psychotoxical/psysonic/pull/188) and PR [#192](https://github.com/Psychotoxical/psysonic/pull/192))*: Fixed a column picker overflow where the dropdown was clipped by the tracklist container. Also fixed column toggle state and alignment issues in the picker UI. An `overflow-x: visible` regression introduced in PR #188 was subsequently reverted. - **macOS β€” spurious microphone permission prompt** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Removed `NSMicrophoneUsageDescription` from `Info.plist`. It was inherited from an earlier Tauri template but Psysonic never uses the microphone; its presence caused macOS to show a permission dialog on first launch. - **Device Sync β€” auto-import and disconnect cleanup** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The sync manifest is now automatically imported when the Device Sync page is opened if a device with a manifest is already mounted. The sync file list is cleared when the device is disconnected. - **Audio β€” streaming decoder log labels** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#201](https://github.com/Psychotoxical/psysonic/pull/201))*: Rust log lines from the streaming decoder are now tagged with the source type, making it easier to distinguish stream vs. local decode paths in debug output. - **Theme β€” Latte and GTA readability** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Improved contrast and text readability in the Catppuccin Latte and GTA themes. - **i18n β€” missing `common.play` key** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: Added the `common.play` translation key to all 8 locales; it was missing after PR #186 which introduced its usage. ### Removed - **Waveform seekbar β€” realtime waveform style** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The `realtime_waveform` CSS class and its associated style block were removed. This style was applied during live streaming and produced a low-quality rendering mode that was no longer needed after the streaming architecture improvements. --- *Thank you to everyone who contributed to this release:* *[@cucadmuh](https://github.com/cucadmuh) for the playback source indicator, ReplayGain in the tech strip, streaming stability hardening, and CLI improvements β€” four substantial PRs.* *[@kveld9](https://github.com/kveld9) for the CSV import, search context menu, Discord RP enhancements, Favorites redesign, and header redesign β€” a very productive cycle.* --- ## [1.34.11] - 2026-04-14 ### Added - **Opus audio playback** *(Issue [#180](https://github.com/Psychotoxical/psysonic/issues/180), contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#183](https://github.com/Psychotoxical/psysonic/pull/183))*: Psysonic can now decode Opus audio natively via `symphonia-adapter-libopus`, which bundles and compiles libopus from source. Previously `.opus` files were sent to the server for transcoding β€” a workaround that never worked reliably. Native decoding is now used directly; the server is no longer involved. Note: building from source requires `cmake` to be installed (see README). - **Device Sync β€” synchronise your library to USB and SD card players** *(Issue [#161](https://github.com/Psychotoxical/psysonic/issues/161), by [@Psychotoxical](https://github.com/Psychotoxical))*: A fully overhauled Device Sync page lets you copy music from your Navidrome library to any mounted USB drive or SD card. Browse albums via live search (300 ms debounce) or a random album selection. Choose a filename template (Artist/Album/Track format), pick a target folder, and review a pre-sync summary showing files to add, files to delete, and available space β€” including a warning if the device would run out of space after accounting for pending deletions. Already-synced files are detected and skipped automatically so incremental syncs are fast. - **3 visual toggles** *(contributed by [@kveld9](https://github.com/kveld9), PR [#181](https://github.com/Psychotoxical/psysonic/pull/181))*: Three new toggles in Settings β†’ Appearance: - **Cover art background** β€” enables/disables the blurred album art background in Album Detail and the Hero section. - **Playlist cover photo** β€” shows/hides the cover collage at the top of Playlist Detail pages. - **Show bitrate badge** β€” toggles the bitrate label displayed on tracks in the queue and track lists. - **8 community themes** *(contributed by [@kveld9](https://github.com/kveld9), PR [#182](https://github.com/Psychotoxical/psysonic/pull/182))*: A new **Community** theme group appears directly below Psysonic Themes in the Theme Picker, containing eight new themes: **AMOLED Black Pure** (pure black for OLED), **Monochrome Dark** (grayscale), **Amber Night** (warm golden amber), **Phosphor Green** (classic terminal green), **Midnight Blue** (deep blue), **Rose Dark** (pink/rose accents), **Sepia Dark** (warm cream sepia), and **Ice Blue** (cool cyan). Psysonic now ships with 75 themes across 9 groups. ### Fixed - **HTTPS streaming failures and server URL trailing slash** *(Issue [#178](https://github.com/Psychotoxical/psysonic/issues/178), by [@Psychotoxical](https://github.com/Psychotoxical) with fix ported from PR [#179](https://github.com/Psychotoxical/psysonic/pull/179) by [@kveld9](https://github.com/kveld9))*: Two bugs that broke HTTPS server connections are now fixed. A trailing slash in the configured server URL caused double-slash stream URLs (`//rest/stream.view`) that reverse proxies like Caddy would reject, and also caused album browsing to return 0 results. Additionally, `reqwest` now loads the OS native certificate store alongside Mozilla's root store β€” fixing HTTPS streaming failures when the server certificate is signed by a local CA (e.g. Caddy's internal CA) that is trusted in the system keychain but not in Mozilla's bundle. - **Server display in Settings** *(by [@Psychotoxical](https://github.com/Psychotoxical))*: The server list in Settings β†’ Servers now shows the URL and username on separate lines instead of a single truncated `username@url` string. Protocol prefixes (`http://`, `https://`) are stripped for cleaner display. HTTPS connections show a green lock icon. ### Changed - **Waveform seekbar β€” live theme updates** *(contributed by [@kveld9](https://github.com/kveld9), PR [#182](https://github.com/Psychotoxical/psysonic/pull/182))*: The canvas-based seekbar now listens for `data-theme` attribute changes via `MutationObserver` and redraws immediately with the new theme colours. Switching themes no longer requires an app restart to update the waveform. --- *Thank you to everyone who contributed to this release:* *[@cucadmuh](https://github.com/cucadmuh) for implementing native Opus decoding β€” a long-requested feature that finally makes `.opus` libraries fully playable.* *[@kveld9](https://github.com/kveld9) for three PRs in one release: the SSL/trailing-slash fix, visual customisation toggles, and eight new community themes with a live waveform update fix.* --- ## [1.34.10] - 2026-04-13 ### Added - **AppImage bundle for Linux** + X11/XWayland enforcement on all Linux packages: CI now builds `.AppImage` in addition to `.deb` and `.rpm`. `GDK_BACKEND=x11` and `WEBKIT_DISABLE_COMPOSITING_MODE=1` are set automatically at startup on all Linux packages β€” WebKitGTK on Wayland is unstable. Both environment variables are still overridable by setting them before launch. - **Audio output device selection** *(Issue [#169](https://github.com/Psychotoxical/psysonic/issues/169))*: Settings β†’ Audio now shows a dropdown of all available output devices. The current OS default is pinned at the top with a label; a Refresh button re-enumerates silently. A device watcher detects hot-plug events and emits `audio:device-reset` after ~9 s of consecutive misses, preventing false positives on busy ALSA devices. On Linux, technical ALSA prefixes are stripped for display (`sysdefault:CARD=U192k` β†’ `U192k`). - **Vision Dark & Vision Navy β€” colorblind-safe themes** *(Issue [#166](https://github.com/Psychotoxical/psysonic/issues/166))*: Two new themes using a Purple & Gold palette designed to be safe for Deuteranopia, Protanopia, and Tritanopia. Vision Dark pairs near-black `#0D0B12` with Gold `#FFD700` (~14.7:1 WCAG AAA); Vision Navy uses deep navy `#0A1628` + Gold (~14.5:1 WCAG AAA). Both appear under a new **Accessibility** group in the Theme Picker. These themes are a first step toward proper colorblind support and will be revised and expanded in upcoming releases β€” structural improvements such as secondary indicators and pattern/shape cues are still on the roadmap. - **Folder Browser β€” per-column filter & Shift+Enter queue append** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#165](https://github.com/Psychotoxical/psysonic/pull/165))*: Press Ctrl+F to open a filter field for the active Folder Browser column. Focus hands off cleanly between the filter input and the row list. Clearing a parent-column selection clears all right-side filters automatically. Press Shift+Enter on a filtered track list to **append** the visible tracks to the queue without replacing it. - **Keybindings β€” in-app modifier chords** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#167](https://github.com/Psychotoxical/psysonic/pull/167))*: In-app keybindings now support Ctrl/Alt/Shift+Key chords in addition to bare keys. The settings capture flow uses `buildInAppBinding`; the runtime handler uses `matchInAppBinding` and skips any chord already claimed as a global shortcut. Bare-key bindings still match without modifiers. Additionally, the seek forward/backward shortcuts now correctly interpret the configured value as seconds β€” previously the value was treated as a 0–1 progress fraction. - **Playlist management enhancements** *(contributed by [@kveld9](https://github.com/kveld9), PR [#168](https://github.com/Psychotoxical/psysonic/pull/168))*: Multi-select context-menu actions for Albums, Artists, and Playlists now include a bulk **Add to Playlist** submenu. The sidebar playlist section is now collapsible. The Artists page gains infinite scroll via `IntersectionObserver`. Submenus flip upward automatically when they would overflow the viewport bottom. A **Remove from Playlist** entry is now available in the Playlist Detail context menu. ### Fixed - **Fullscreen Player β€” animation overhead in no-compositing mode** *(contributed by [@kilyabin](https://github.com/kilyabin), PR [#175](https://github.com/Psychotoxical/psysonic/pull/175))*: In software-rendering mode (`WEBKIT_DISABLE_COMPOSITING_MODE=1`) the mesh blob pan animations are now stopped (static gradients are preserved), the portrait drift animation is stopped, and `box-shadow` is removed from the seekbar played bar. The seekbar played bar width changes on every playback tick; triggering a full shadow repaint in software mode caused significant CPU overhead. - **Folder Browser β€” arrow keys with modifier keys** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#174](https://github.com/Psychotoxical/psysonic/pull/174))*: Column and list arrow-key handling is now skipped when any modifier key is held, preventing conflicts with browser focus navigation and OS-level shortcuts. Modifier detection uses both `nativeEvent` and `getModifierState` for WebKit/WebView2 compatibility. - **Audio output device β€” Linux stability** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#176](https://github.com/Psychotoxical/psysonic/pull/176))*: Pinned ALSA/cpal device IDs now stay stable when enumeration temporarily omits the active sink or returns an equivalent name. The Linux device-watcher no longer clears the pin based solely on missing list entries β€” only macOS and Windows treat repeated absence as "device unplugged". The Settings refresh flow calls `canonicalize` and refetches the list; an i18n label is now shown when the active device is no longer in the enumerated list. - **Login β€” server URL field** *(Issue [#171](https://github.com/Psychotoxical/psysonic/issues/171))*: The placeholder text in the Add Server form was previously a hardcoded English string. It is now fully localised and clarifies that `https://` URLs are accepted. - **Offline mode β€” non-blocking banner** *(Issue [#170](https://github.com/Psychotoxical/psysonic/issues/170))*: The full-screen blocking overlay shown when Psysonic starts without a cached library is replaced with a slim banner at the top of the content area. The banner includes a direct link to Server Settings so the user can fix the connection without navigating manually. --- *Special thanks to everyone who contributed to this release:* *[@cucadmuh](https://github.com/cucadmuh) for the significant Folder Browser improvements, the modifier-chord keybindings and and the Linux audio stability fixes β€” four PRs in one release cycle, remarkable.* *[@kilyabin](https://github.com/kilyabin) for continuing to hunt down no-compositing performance issues.* *[@kveld9](https://github.com/kveld9) for the playlist management overhaul.* --- ## [1.34.9] - 2026-04-12 ### Added - **Multi-select in Playlist Detail & Favorites** *(Issue [#157](https://github.com/Psychotoxical/psysonic/issues/157))*: The same Ctrl/Cmd+Click multi-select system that was previously exclusive to album track lists is now available everywhere. Hold Ctrl (or ⌘ on macOS) to enter select mode, Shift+Click to range-select, click the header checkbox to toggle all. Selected tracks can be dragged as a group directly into the queue. A bulk action bar appears with **Add to Playlist** and **Clear selection** options. Works in Playlist Detail (main tracklist) and in the Favorites song list. - **"Open Artist" in context menu**: Song context menus now show an **Open Artist** entry directly below **Open Album**, navigating to the artist detail page. Previously only accessible via the tracklist artist link. - **"Add to Playlist" for Artists**: The context menu for artists now includes an **Add to Playlist** submenu. Psysonic fetches all albums from the artist and collects every track, then forwards them to the playlist picker β€” identical to the existing album-level submenu. - **Infinite queue β€” Instant Mix strategy** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#163](https://github.com/Psychotoxical/psysonic/pull/163))*: When Infinite Queue is enabled, Psysonic now builds the upcoming track list using the same artist-driven logic as Instant Mix. It fetches **Top Songs** and **Similar Songs** for the current track's artist, shuffles and deduplicates the pool, and only falls back to fully random songs when no artist-driven candidates are available. This results in much more coherent listening sessions that stay close to your current musical context. - **Fullscreen Player β€” appearance settings** *(contributed by [@kilyabin](https://github.com/kilyabin), PR [#156](https://github.com/Psychotoxical/psysonic/pull/156))*: Settings β†’ Appearance β†’ Fullscreen Player now offers a toggle to show/hide the artist portrait and a 0–80 % dimming slider for the background portrait. - **Build a Mix hub** *(contributed by [@kilyabin](https://github.com/kilyabin), PR [#155](https://github.com/Psychotoxical/psysonic/pull/155))*: The previous *Random Mix* and *Random Albums* sidebar entries have been merged into a single **Build a Mix** page (Wand icon) at `/random`. A landing card lets you choose between *Mix by Tracks* and *Mix by Albums*. Old routes remain fully functional. - **Spanish translation** *(contributed by [@Kveld9](https://github.com/Kveld9), PR [#159](https://github.com/Psychotoxical/psysonic/pull/159))*: Complete Spanish (es) locale with 964 translated strings. Psysonic now ships in 8 languages: English, German, French, Dutch, Chinese, Norwegian, Russian, and Spanish. - **Column-header sorting for Albums & Playlists** *(contributed by [@Kveld9](https://github.com/Kveld9), PR [#160](https://github.com/Psychotoxical/psysonic/pull/160))*: Track lists in Album Detail and Playlist Detail now support click-to-sort directly on the column headers. Three-click cycle: ascending β†’ descending β†’ natural order. Sortable columns: Title, Artist, Album, Favourite, Rating, Duration. The active column is shown bold with a β–²/β–Ό indicator. - **Folder Browser β€” keyboard navigation & context menus** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#158](https://github.com/Psychotoxical/psysonic/pull/158))*: Full keyboard navigation in the Folder Browser with arrow keys, Enter to open, and Ctrl+Enter to open the context menu. Context menus for all row types include keyboard-operable submenus and star-rating control via arrow keys. The now-playing path is visually emphasized and updates live. Adaptive column layout prioritizes right-side visibility for deep directory trees. A new configurable *Open Folder Browser* keybinding is available in Settings β†’ Keyboard. - **PLS/M3U playlist resolution for Internet Radio**: Stations configured with a `.pls` or `.m3u`/`.m3u8` URL (e.g. SomaFM, schizoid.in) are now resolved to their first direct stream URL before playback. ICY metadata fetching also auto-resolves playlist URLs. Previously these stations would fail to play or show no track info. - **Lyrics sources β€” configurable order & per-source toggle**: The old *Server First* toggle has been replaced with a full drag-to-reorder list in Settings β†’ General. Three sources β€” **Server** (embedded/OpenSubsonic), **LRCLIB**, and **Netease Cloud Music** β€” can each be individually enabled or disabled, and their priority order is fully customisable. Embedded SYLT tags from local files always win unconditionally. - **ReplayGain Pre-Gain & Fallback** *(audio)*: Two new sliders in Settings β†’ Audio β†’ ReplayGain: - **Pre-Gain** (0–+6 dB): added on top of every ReplayGain-tagged track for users who prefer a louder default. - **Fallback Gain** (βˆ’6–0 dB): applied to untagged tracks and internet radio streams, preventing volume jumps when switching between tagged and untagged content. - **Context-aware Remix button in Build a Mix**: When a genre filter is active, the Remix button now re-fetches the same genre instead of resetting to the full library pool. An *All Songs* chip is available as the first genre option to return to the global mix without leaving the page. - **AlbumTrackList multi-select & psyDnD** *(tracklist polish)*: Album track lists now support full multi-select with Ctrl/Cmd+Click, Shift+Click range selection, and drag-to-queue for multiple tracks simultaneously. The `TrackRow` component is `React.memo` with fine-grained Zustand selectors, so only the toggled row re-renders on selection change (O(1)). - **Mute/unmute restores previous volume**: The mute button in the player bar now restores the volume to its level before muting instead of always jumping to 70 %. ### Fixed - **Statistics β€” accurate counts for large libraries**: The statistics page was previously capped at 10 pages (β‰ˆ 5,000 albums), causing incorrect totals on larger libraries. The pagination loop now runs until the server returns a partial page, regardless of library size. Sort type changed to `alphabeticalByName` for stable pagination. - **Statistics β€” Artists count tooltip**: The Artists card now shows a tooltip (dotted underline, cursor: help) explaining that the count reflects album artists only β€” a Subsonic API limitation. Featured or guest artists who do not have their own album are not counted. The tooltip is localised in all 8 languages. - **Artists page β€” alphabet navigation hover effect**: The A–Z filter buttons had inline styles that prevented `:hover` CSS from applying. Buttons are now styled via `.artists-alpha-btn` CSS class with an accent-coloured hover highlight and a subtle glow ring. - **Hot Cache β€” eviction & prefetch budget**: Eviction now correctly keeps only the current and next track; prefetch fetches up to five tracks when under the size cap but always fetches the immediate next; the previous current track is given a grace period until the debounce fires; eviction runs immediately on MB limit or folder changes; the cap is re-read after each download completes. Live disk usage is now shown on the Audio settings page. - **Hot Cache + Preload β€” mutual exclusion on rehydration**: Users who had both Hot Cache and Preload enabled before the mutual-exclusion rule was introduced will have both automatically reset to off on first launch, preventing a conflicting state. - **Fullscreen Player β€” Linux compositing performance** *(contributed by [@kilyabin](https://github.com/kilyabin), PR [#156](https://github.com/Psychotoxical/psysonic/pull/156))*: A new `no_compositing_mode` Tauri command detects Linux software-rendering mode and adds an `html.no-compositing` class, which swaps GPU-only CSS effects (`backdrop-filter`, `filter`, `mask-image`) for software-friendly equivalents throughout the fullscreen player. - **Fullscreen Player β€” long lyric lines wrapping**: Long words in lyric lines now wrap correctly instead of overflowing the container. - **Russian locale** *(contributed by [@kilyabin](https://github.com/kilyabin), PR [#148](https://github.com/Psychotoxical/psysonic/pull/148))*: Numerous translation improvements across the application, replacing machine-translated or awkward phrasings with natural Russian. - **npm audit vulnerabilities**: Updated `axios` and `vite` to address reported security advisories. ### Changed - **"Remove from Queue" context menu item** now has a **Trash** icon, matching the destructive action style of other delete operations. - **Playlist Detail β€” filter-mode drag**: Rows in a filtered/sorted playlist view can now be dragged to the queue as single songs (previously dragging was disabled entirely in filter mode). - **Infinite queue deduplication**: Tracks already present in the queue are excluded from the candidate pool, preventing the same song from appearing twice in a row during Infinite Queue sessions. ### Contributors Thank you to everyone who contributed to **v1.34.9**: - [@cucadmuh](https://github.com/cucadmuh) β€” Infinite queue via Instant Mix strategy (PR [#163](https://github.com/Psychotoxical/psysonic/pull/163)), Folder Browser keyboard navigation & context menus (PR [#158](https://github.com/Psychotoxical/psysonic/pull/158)) - [@kilyabin](https://github.com/kilyabin) β€” Fullscreen Player performance & appearance settings (PR [#156](https://github.com/Psychotoxical/psysonic/pull/156)), Build a Mix hub (PR [#155](https://github.com/Psychotoxical/psysonic/pull/155)), Russian locale improvements (PR [#148](https://github.com/Psychotoxical/psysonic/pull/148)) - [@Kveld9](https://github.com/Kveld9) β€” Spanish translation (PR [#159](https://github.com/Psychotoxical/psysonic/pull/159)), Column-header sorting (PR [#160](https://github.com/Psychotoxical/psysonic/pull/160)) A huge thank you to all three of you β€” your contributions have made this one of the most feature-packed patch releases yet. Psysonic keeps getting better because of people like you. πŸ™Œ --- ## [1.34.8] - 2026-04-10 ### Added - **Netease Cloud Music Lyrics** *(opt-in)*: Netease Cloud Music can now be enabled in Settings β†’ General as a last-resort lyrics fallback. It only fires when neither the server nor LRCLIB return results β€” the existing lyrics chain is completely unaffected. Particularly useful for Asian and international music. Chinese metadata lines (作词/δ½œζ›²/ηΌ–ζ›² etc.) are automatically stripped from the LRC output. - **Navidrome AudioMuse-AI Integration** *(contributed by [@cucadmuh](https://github.com/cucadmuh), PR [#147](https://github.com/Psychotoxical/psysonic/pull/147))*: Psysonic now supports [AudioMuse-AI](https://github.com/cucadmuh/audiomuse-ai) if it is active on the Navidrome server and uses it for Random Mix, Similar Artists, and Instant Mix. No configuration required β€” Psysonic keeps its existing behavior when AudioMuse is unavailable. Also includes an Instant Mix probe, ping identity, and improved UX for AudioMuse-specific actions. - **ICY metadata & AzuraCast radio** *(contributed by [@nisrael](https://github.com/nisrael), PR [#146](https://github.com/Psychotoxical/psysonic/pull/146))*: Internet radio now displays live track metadata from ICY streams. AzuraCast stations are supported with extended now-playing information. - **Automatic audio device switching**: Psysonic now detects newly connected or changed audio output devices and switches to them automatically β€” no app restart required. ### Fixed - **Multi-artist tracks**: Tracks with multiple artists (OpenSubsonic `artists[]` field, e.g. semicolon-separated entries) now display each artist individually. Artists with their own profile page are clickable links; artists without one appear as plain text. Separated by `Β·`. - **Gapless + Preload Gate**: The gapless chain and the preload gate now run on separate paths. Previously both could fire simultaneously, causing a brief black flash on track change. - **Replay Gain β€” missing album gain**: When no album gain tag is present, Psysonic now correctly falls back to track gain instead of skipping gain correction entirely. - **Statistics β€” music library scope**: Genre insights now respect the currently selected music library. Fetch results are cached to avoid redundant server requests. Playback durations are displayed in localized units. - **Russian locale**: "Most Played" in the sidebar, home page, and page title now uses Β«ΠŸΠΎΠΏΡƒΠ»ΡΡ€Π½ΠΎΠ΅Β». ### Changed - **"Reset to defaults" buttons** in Settings β†’ Input are now styled as warning buttons (red border). - **Lyrics button** removed from the player bar (redundant with the queue panel tab). - **Icons**: Advanced search now uses the `TextSearch` icon; artist bio button now uses `Highlighter`. - **Album chip** in the album detail header is now opaque across all themes. - **Hot Cache and Hi-Res Audio**: Alpha badges removed β€” both features are production-ready. - **CPU optimisations**: Next-track buffering and preload settings have been consolidated into a unified control. ### Theme Fixes - **Middle Earth**: Removed vertical stripe pattern from sidebar; improved queue artist contrast on hover; fixed album detail artist colour, bio text, and "Read more" link readability; "Next Tracks" divider label is now lighter. - **Toy Tale**: Fixed sidebar section labels (System/Library), queue tab buttons (Lyrics/Queue), inactive artist text, and "Next Tracks" divider label β€” all were too dark to read. - **Tetrastack**: Raised all purple and blue palette values (`#a020f0` β†’ `#c070ff`, `#0060f0` β†’ `#4090ff`); raised `--text-muted` from `#3a3a6a` to `#7878b8` β€” affected settings descriptions, artist names in tracklists, and queue labels. - **Horde & Alliance**: Removed repeating horizontal line pattern from sidebar. ### Contributors Thank you to everyone who contributed to this release: - [@cucadmuh](https://github.com/cucadmuh) β€” AudioMuse-AI Navidrome integration (PR [#147](https://github.com/Psychotoxical/psysonic/pull/147)) - [@sorensiimSalling](https://github.com/sorensiimSalling) β€” ICY metadata & AzuraCast radio support (PR [#146](https://github.com/Psychotoxical/psysonic/pull/146)) You make Psysonic better. πŸ™Œ --- ## [1.34.7] - 2026-04-09 ### Added - **Windows β€” Taskbar Thumbnail Toolbar**: Prev / Play-Pause / Next media buttons now appear in the Windows taskbar thumbnail preview (the popup that appears when hovering over the taskbar icon). Buttons emit the same `media:*` events as the tray menu and souvlaki. The Play/Pause icon swaps in real-time as playback state changes. - **Windows β€” High-quality taskbar icons**: The taskbar thumbnail toolbar icons are now loaded from embedded `.ico` assets (`play.ico`, `pause.ico`, `prev.ico`, `next.ico`) via `CreateIconFromResourceEx`, replacing the previous monochrome GDI drawing code. All four icons are properly cleaned up on window destruction. - **Professional update modal**: The in-app updater now shows a polished modal with the full release changelog, a **Skip this version** option, and an OS-aware direct download button (`.dmg` on macOS, `.exe` on Windows, `.deb`/`.rpm` on Linux) as a fallback if the auto-update fails. The modal is fully localised in all 7 supported languages. - **Self-hosted fonts β€” no internet required**: All 10 UI fonts are now shipped as WOFF2 files bundled into the app via `@fontsource-variable` npm packages. The previous Google Fonts CDN dependency has been removed entirely β€” Psysonic now renders correctly with no internet connection and without any external requests on startup. - **Help β€” 11 new FAQ entries**: The Help page covers previously undocumented features across Ratings (how to rate songs/albums/artists, removing a rating, Skip-to-1β˜…, rating filter for mixes), Folder Browser, Theme Scheduler, UI Scale, Seekbar styles, AutoEQ, Replay Gain, Hot Cache, and offline playlist caching. All 7 locales updated. ### Fixed - **Embedded lyrics (MP3 & FLAC)**: A new `get_embedded_lyrics` Tauri command reads lyrics tags directly from local files β€” `SYLT`/`USLT` frames for MP3 (via the `id3` crate) and `SYNCEDLYRICS`/`LYRICS` tags for FLAC (via `lofty`). Additionally: the LRC parser now correctly handles timestamps without fractional seconds (e.g. `[01:23]`), and the Subsonic structured-lyrics parser now accepts both `synced` and `issynced` field names for compatibility with different server versions. - **Linux β€” player bar disappearing at high zoom / small window sizes**: All `grid-template-rows` definitions now use `minmax(0, 1fr)` instead of bare `1fr`, and the `min-height: 720px` constraint on the app shell has been removed. The player bar no longer gets pushed off-screen when the window is small or the UI scale is above 100 %. - **Windows β€” "Open folder" in Settings crashing**: The Settings page uses a Rust `open_folder` command instead of the Tauri `shell:open` API, which was blocked by the capability scope on Windows for local paths. - **macOS β€” Artist Radio crashing WKWebView after ~10 minutes**: Storing `currentTime` in the persisted Zustand state caused up to ~1,200 synchronous `localStorage.setItem` calls per radio session, eventually crashing the WKWebView SQLite backend. `currentTime` has been removed from the persist partializer. Old played radio tracks are also now trimmed from the queue (keeping the last 5) to cap the localStorage payload during queue top-up. - **Artist Radio β€” predictable track order**: The initial Artist Radio queue is now shuffled via Fisher-Yates, so positions 2+ draw from similar-artist tracks in a random order rather than always playing the server's top-5 tracks in sequence. - **Internet Radio β€” stall / buffering recovery**: Stall events on the HTML5 `