mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 14:55:43 +00:00
546 lines
44 KiB
TypeScript
546 lines
44 KiB
TypeScript
import { isNewer } from '@/lib/util/appUpdaterHelpers';
|
||
|
||
// Credits list rendered on the Settings → System tab. Update via PR when adding a contributor.
|
||
const CONTRIBUTOR_ENTRIES = [
|
||
{
|
||
github: 'jiezhuo',
|
||
since: '1.21',
|
||
contributions: [
|
||
'Chinese (Simplified) translation',
|
||
],
|
||
},
|
||
{
|
||
github: 'nullobject',
|
||
since: '1.22.0',
|
||
contributions: [
|
||
'Seek debounce & race condition fix (PR #7)',
|
||
'Waveform seekbar stability on position update (PR #8)',
|
||
],
|
||
},
|
||
{
|
||
github: 'trbn1',
|
||
since: '1.22.0',
|
||
contributions: [
|
||
'Replay Gain metadata propagation (PR #9)',
|
||
'songToTrack() — unified track construction across all sources',
|
||
],
|
||
},
|
||
{
|
||
github: 'nisarg-78',
|
||
since: '1.29.0',
|
||
contributions: [
|
||
'Click-to-seek in synced lyrics (PR #38)',
|
||
'Volume scroll wheel on volume slider (PR #38)',
|
||
'Lyrics line visual states: active / completed / upcoming (PR #38)',
|
||
'Queue auto-scroll to keep upcoming tracks in view; fixed re-renders from currentTime in QueuePanel (PR #115)',
|
||
],
|
||
},
|
||
{
|
||
github: 'JulianNymark',
|
||
since: '1.29.0',
|
||
contributions: [
|
||
'OGG/Vorbis container support via symphonia-format-ogg (PR #42)',
|
||
'Themed toast notifications for audio playback errors (PR #43)',
|
||
'Human-readable audio error messages (PR #44)',
|
||
],
|
||
},
|
||
{
|
||
github: 'zz5zz',
|
||
since: '1.32.0',
|
||
contributions: [
|
||
'Norwegian (Bokmål) translation (PR #101)',
|
||
'Ongoing bug & quirk reports via Discord (PRs #747, #750 et al.)',
|
||
],
|
||
},
|
||
{
|
||
github: 'cucadmuh',
|
||
since: '1.33.0',
|
||
contributions: [
|
||
'Russian translation & i18n locale split (PR #106)',
|
||
'Russian locale refinements using phrasing from ru2 (PR #113)',
|
||
'Gapless manual skip: honor user-initiated play over pre-chained track (PR #119)',
|
||
'Hot playback cache — queue prefetch (PR #123)',
|
||
'Per-server music folder filter and sidebar library picker (PR #124, PR #125)',
|
||
'Richer star ratings, skip threshold, and library filtering (PR #130)',
|
||
'Statistics: scope album and song totals to selected music library (PR #138)',
|
||
'AudioMuse-AI discovery integration for Navidrome (PR #147)',
|
||
'Hot playback cache — eviction budgeting, grace period, and live Audio settings readout (PR #153)',
|
||
'Folder Browser: keyboard navigation, context menus, now-playing path emphasis, and adaptive column layout (PR #158)',
|
||
'Infinite queue: artist-driven candidates via Top Songs + Similar Songs with random fallback (PR #163)',
|
||
'Folder Browser: per-column filter with keyboard flow and Shift+Enter queue append (PR #165)',
|
||
'Keybindings: modifier + key chords for in-app shortcuts; fixed seek ±10s units (PR #167)',
|
||
'Statistics: genre insights scoped to music library, cached Subsonic fetches, localized duration formatting (PR #144)',
|
||
'Audio output device picker: clearer ALSA labels, duplicate disambiguation, system-default mark, live refresh (PR #173)',
|
||
'Folder Browser: arrow navigation blocked when modifier keys are held (PR #174)',
|
||
'Linux audio output device picker: stable watcher (disable false enumeration-miss resets), canonicalize ALSA name drift, ghost entry for unlisted device (PR #176)',
|
||
'Opus audio playback via symphonia-adapter-libopus with bundled libopus (PR #183)',
|
||
'CLI player controls with D-Bus forwarding, shell completions for bash/zsh, library/audio-device/instant-mix CLI commands, active server switcher in header (PR #187)',
|
||
'Streaming playback stability: stream-first start, seek recovery, crossfade/gapless backup preload, hot-cache promotion (PR #200)',
|
||
'ReplayGain values in Queue tech strip (PR #196)',
|
||
'Playback source badge (offline / cache / stream) in Queue tech strip (PR #201)',
|
||
'WebKitGTK wheel scroll mode: smooth (kinetic) default with optional linear toggle (PR #207)',
|
||
'ArtistCardLocal i18n: use plural-aware artists.albumCount key instead of hardcoded German',
|
||
'NixOS / flake install guide with Cachix setup (PR #209)',
|
||
'Subsonic: align Rust HTTP UA with main WebView UA (PR #235)',
|
||
'UI: overlay scrollbars, resizer hit-test, and Linux mini-wheel (PR #255)',
|
||
'Server invites: magic-string paste, Navidrome admin share, add-user validation (PR #258)',
|
||
'Library deep links (psysonic2 scheme) — paste track/album/artist/queue (PR #261)',
|
||
'Linux: stop Wayland GTK drag proxy and PsyDnD ghost (PR #268)',
|
||
'Sidebar: long-press drag to reorder and hide nav items (PR #269)',
|
||
'Lucky Mix — instant queue from listening history, ratings, and AudioMuse similar tracks (PR #278)',
|
||
'UI perf: fix spike when medulla-perch lines up with hair-fan gestures (PR #283)',
|
||
'Navidrome smart playlists workflow in the Playlists page (PR #289)',
|
||
'Loudness normalization — LUFS / EBU R128 analysis cache + persistent waveform store (PR #315)',
|
||
'Queue resize handle: no longer blocked by main scroll hit-test (PR #324)',
|
||
'LUFS loop stabilization, serialized CPU seed work, mixed mean/peak waveform bins (PR #326)',
|
||
'Queue undo/redo — hotkeys, playback-aware restore, scroll restoration (PR #331)',
|
||
'Lucky Mix + mix rating filter (Navidrome / OpenSubsonic) (PR #332)',
|
||
'Loudness: target sync, effective pre-analysis trim, queue/settings copy (PR #333)',
|
||
'CLI logs mode with tail/follow and shell completions (PR #337)',
|
||
'Player: reserve preview row space in delay modal (PR #344)',
|
||
'Infinite queue: respect rating filter in top-up (PR #357)',
|
||
'NixOS install guide refresh for channels and CI (PR #380)',
|
||
'UI refinements — sidebar discovery indicators, adaptive header, interaction polish (PR #397)',
|
||
'Queue: drag rows outside to remove with trash ghost (PR #420)',
|
||
'Tauri: modularize audio and lib command layers (PR #422)',
|
||
'Shortcuts: action registry, dynamic CLI help, 9 new input targets + F1 help binding (PR #435)',
|
||
'Environment upgrade & hot-cache playback — replay via RAM/disk on same-track and queue-end resume, playback-source icon stays correct after resume/undo/gapless, sidebar new-releases 500-id cap merge, Windows tray double-click fix, lazy-loaded routes, rodio 0.22 migration (PR #463)',
|
||
'Audio: post-sleep stream recovery (Windows + Linux) with poll-gap-armed stall watchdog; preview seekbar freeze + anti-jump on preview end; remove card hover lift and per-card GPU compositing hints (PR #476)',
|
||
'Analysis queue control: prune stale http-backfill / cpu-seed jobs when tracks leave the playback queue, cap loudness backfill warmup to current + next 5 tracks, plus debug counters for diagnostics (PR #480)',
|
||
'CachedImage / useCachedUrl: blob URL only for matching cacheKey, layout load reset on key change; fixes broken player cover flash on track switch (#606) (PR #695)',
|
||
'OpenSubsonic albumArtists in album header; track artists in player bar, mobile, mini + songToTrack (#552) (PR #696)',
|
||
'Search: filter search3 artist rows with zero albums (report: zunoz on Psysonic Discord) (PR #697)',
|
||
'Internet Radio: portal add/edit station modal to document.body — fixes clipped modal on empty library (report: voidboywannabe on Psysonic Discord) (PR #699)',
|
||
'Now Playing: composite list keys on similar artists, album-card tracklist, and top songs — avoids duplicate React keys when Subsonic repeats ids (PR #703)',
|
||
'Search: share links in live/mobile search + queue preview modal (PR #716)',
|
||
'Multi-server: pin queue streams, cover art, links, context menu, and Now Playing to queue server (PR #717)',
|
||
'M4A/MP4 streaming: moov-at-end tail prefetch and Symphonia isomp4 probe fix (PR #737)',
|
||
'HTTP stream buffering — seekbar/timer at zero and cover overlay until playback arms (PR #737)',
|
||
'M4A playback: fix AtomIterator overread in patched isomp4 demuxer — probe gate for ranged tail prefetch, zero-hole fallback detection (PR #757)',
|
||
'Multi-server: Lucky Mix and Now Playing keep browsed server; queue metadata via apiForServer (PR #768)',
|
||
'Linux: session GDK/WebKit mitigations, in-page library scroll, Wayland text presets (PR #731)',
|
||
'In-page browse: virtual list scrollMargin + CachedImage load priority; Artists infinite-scroll batching (PR #783)',
|
||
'Lucky Mix: hand off queue to browsed server after multi-server switch (PR #785)',
|
||
'Home album rails: stable play/enqueue hover on WebKitGTK/Wayland (PR #787)',
|
||
'Local library index: multi-server settings UI, serial sync queue, music-library-scoped local search, parallel initial ingest, i18n across 9 locales (PR #846)',
|
||
'Library browse: local-vs-network text search race, All Albums/Artists catalog from index, DevTools browse-race logging (PR #847)',
|
||
'Player stats: local listening history tab with heatmap, year summary, recent days, and day drill-down (PR #849)',
|
||
'Playback speed: global 0.5–2.0× with Speed / Varispeed / Pitch shift strategies, player bar popover, Orbit passthrough (PR #852)',
|
||
'Local library index: full resync orphan sweep (IS-7) — remove server-deleted tracks after successful re-sync (PR #861)',
|
||
'Track enrichment: oximedia BPM/mood analysis, mood-group Advanced Search, queue display, unified playback analysis dispatch (PR #863)',
|
||
'Server index-key rebuild follow-up: startup-safe migration orchestration, per-server analysis strategy controls, playback/cache scope hardening, and backup/restore for library databases with blocking progress UX (PR #864)',
|
||
'Live Search: server-scoped local FTS, multi-server hit fix, and local vs search3 race merge (PR #868)',
|
||
'Cover art pipeline: tier ladder, WebP disk cache, dense-grid prefetch, Settings cover cache budget (PR #869)',
|
||
'Lossless: local index browse, Advanced Search and All Albums filters, artist/album drill-down mode, conserved sidebar page (PR #871)',
|
||
'Albums: combined browse filters (genre/year/favorites/lossless/compilations), session restore from album detail, favorites reconcile via local index (PR #876)',
|
||
'Artist detail: sort albums by year (newest/oldest) in the Albums section (PR #877)',
|
||
'Servers: optional second address per profile (LAN + public), LAN-first connect with sticky fallback, same-server verify on save, share-link picker, URL-change library remigration, magic invite v2 (PR #880)',
|
||
'Cover art: Windows thumbnails, tier fallback, PNG decode, Subsonic coverArt id resolution (PR #878)',
|
||
'Analytics: native advanced library backfill coordinator — UI stays responsive on large libraries (PR #881)',
|
||
'Analytics: library backfill scan phase/cursor persistence so advanced indexing can finish large libraries (PR #882)',
|
||
'Analytics: Opus waveform/LUFS/enrichment decode via symphonia-adapter-libopus in the analysis pipeline (PR #883)',
|
||
'Artist page: top-track thumbnails use the same album cover path and warm batch as the albums grid (PR #886)',
|
||
'Library browse navigation: session restore on back for Artists, Search/Tracks, New Releases, Random Albums; unified SearchBrowsePage (PR #936)',
|
||
'Genres: local index genre browse with Subsonic fallback, aligned counts cache, scroll restore, hold-to-shuffle play (PR #937)',
|
||
'Live Search: scoped browse on Artists, Albums, New Releases, Tracks, and Composers — header badge, ghost restore, album title FTS, session stash (PR #938)',
|
||
'Performance: idle Rust CPU — backfill coordinator park, probe overlay stability, throttled idle polls, lazy cover prefetch restore (PR #939)',
|
||
'Cover backfill: disk-free idle gate, snapshot-diff worklist, live-tunable parallelism, transient-error retries, and memoized offline & cache stats to stop idle CPU spin (PR #943)',
|
||
'Cover art: fix per-song cover over-fetch on Navidrome — only genuine per-disc artwork expands, collapsing ~520k per-track fetches to one cover per album (PR #944)',
|
||
'Performance Probe: cover pipeline covers-per-minute (cpm) throughput, the cover analogue of analysis tpm (PR #945)',
|
||
'Performance Probe: live runtime logs tab with depth switch, line cap, and ordered include/exclude word filter (PR #946)',
|
||
'Performance Probe: on-demand (ui) cover throughput alongside backfill (lib) cpm (PR #947)',
|
||
'Performance Probe: throughput (analysis tpm, cover cpm) measured over a trailing 5s window so the rate reacts promptly instead of coasting on minute-long inertia (PR #948)',
|
||
'Cover backfill: follow the smart local/public endpoint switch so off-LAN clients stop fetching covers from the unreachable local address (PR #952)',
|
||
'Audio: Symphonia 0.6 migration with libopus adapter 0.3; ranged-stream start latency fix (probe seek-gate) and a probe timeout so a stalled stream no longer hangs playback start (PR #999)',
|
||
'Offline experience — unified media layout (cache/library/favorites), localPlaybackStore, library-index Offline Library, favorites auto-sync, cached album/playlist/artist pin reconcile, mixed-server offline queue, and single mediaDir setting (PR #1008)',
|
||
'Offline browse — local-bytes catalog when server is down, integration contract (context/policy/resolvers), disconnect nav fork, Home stale-cache feed, read-only context menus, PlayerBar rating/favorite guard (PR #1017)',
|
||
'Themed startup splash before Vite loads — deferred window show, per-theme logo gradient (PR #1030)',
|
||
'Artist page: Top Tracks play when album list is empty on the page (PR #1031)',
|
||
'PsyLab Connections tab with server capability readout, Navidrome admin-role probe, and tab-bar layout fix; server-capability framework with AudioMuse detection via OpenSubsonic sonicSimilarity and sonic Instant Mix routing on Navidrome ≥0.62 (PR #1033)',
|
||
'Library DB: named slow-write op labels for macOS playback-stall diagnosis (PR #1043)',
|
||
'Settings → Servers: compact two-line cards, capability header badges, unified use/active action, delete in edit form, click-pinned version tooltip (PR #1054)',
|
||
'Navidrome Now Playing and scrobble with hot cache, offline pins, and mixed-server playback reachability (PR #1055)',
|
||
'What\'s New: remote WHATS_NEW.md from release assets, dev workspace mode, Highlights vs changelog tabs (PR #1058)',
|
||
'Local library index: multi-genre browse, filters, and counts via track_genre table and blocking backfill (PR #1059)',
|
||
'Audio: lazy-open output stream, 60s idle release (#1071), cold-start paused restore with silent engine prepare (PR #1073)',
|
||
'OpenSubsonic playbackReport — live now-playing state, gliding position bar, and immediate pause/resume on Navidrome ≥0.62 (PR #1080)',
|
||
'Playback speed follow-up — Semitones varispeed strategy, two-decimal speed label, per-strategy tooltips, and Advanced fine-step toggle (PR #1084)',
|
||
'Streamed Opus/Ogg seeking via on-demand HTTP Range fetches — seek mid-stream without a full pre-download (PR #1110)',
|
||
'AutoDJ — content-aware crossfade: waveform-driven silence trim, content-driven overlap, scenario-A self-fade, readiness gate + engine auto-crossfade suppression (PR #1122)',
|
||
'AutoDJ — smooth skip and interrupt blend: manual/out-of-queue crossfade, loud→loud ~2s advance, cold-target prep duck + deferred player-bar handoff (PR #1128)',
|
||
'Play queue sync — manual pull via connection indicator, idle auto-pull, multi-server push filter, flush-on-server-switch (PR #1131)',
|
||
'Niri compositor tiling WM detection (PR #1127)',
|
||
'Local library index: Navidrome ignored-articles artist/composer letter buckets (name_sort + server ignoredArticles), idempotent migration with safe open/swap and poisoned-lock recovery (PR #1145)',
|
||
'All Albums browse: compilation and favorites filters in slice mode — skip redundant client comp filter, route favorites through getStarred2, pre-index page scan, offline isCompilation from tracks (PR #1151)',
|
||
'Custom HTTP headers for reverse-proxy gates — per-server header editor, TS/Rust registry sync, full playback/sync/cover path coverage, log redaction (PR #1156)',
|
||
'CI: ESLint strict workflow and path-aware ci-ok merge gate (PR #1170)',
|
||
'Hi-Res transition blend rate — configurable 44.1/88.2/96 kHz resampling for crossfade, AutoDJ, and gapless when adjacent tracks differ in sample rate (PR #1171)',
|
||
'AutoDJ overlap cap — Auto (12 s content cap) or Limit (2–30 s slider, default 15 s) in track-transition settings; Orbit sync + engine override up to 30 s (PR #1173)',
|
||
'Connection recovery — shared connection status across hook instances so manual Retry clears offline sidebar gating with the header indicator (PR #1190)',
|
||
'Timeline play history — session buffer + play_session bootstrap across queue replace; pin current to top; history replay inserts in-place (PR #1204)',
|
||
'Playback — ReplayGain index prefetch, gapless playbar sync, library replayGainPeak column, live RG refresh after sync (PR #1231)',
|
||
'Artists browse — album vs track credit mode toggle, starred favorites in both modes, persisted credit mode, SQL letter-bucket filter (PR #1232)',
|
||
'Connection — ignore spurious WebKitGTK navigator.onLine offline hint on desktop; confirm via server probe; flush pending favorite/rating sync on real reachability (report: mikmik on Psysonic Discord, PR #1234)',
|
||
'Playlists — batch playlist writes past the GET URL limit (add >341 tracks), in-memory membership cache for fast dedup, offline↔playlist layering detangle (PR #1235)',
|
||
'Queue — resolve thin-state rows off the visible range so off-window items stop rendering as "…" placeholders (desktop panel, mobile drawer, fullscreen up-next) (PR #1236)',
|
||
'Artists browse — case-insensitive Cyrillic/non-ASCII name search when local index is enabled (PR #1237)',
|
||
'CLI — relative volume via signed `volume` argument (+/− percent delta); suppress WebKit NVIDIA stderr notes on CLI argv; faster Linux CLI forward before WebKit init (PR #1238)',
|
||
'Settings — start minimized to tray on cold launch; session-gated startup hide; tray icon coupling (PR #1271)',
|
||
'Multi-library filter — priority-ordered multi-select scope across browse/search/detail, sargable library_id + FTS-first SQL, rebuildable library-cluster.db identity keys, locale-aware name normalization (PR #1241)',
|
||
'Genres — full catalog via indexed SQL when All libraries is selected; no longer samples first album page on large libraries (PR #1242)',
|
||
'Offline browse — on-disk-only Artists/Albums/Tracks/Genres (pins, favorites-auto, hot-cache); reactive sidebar gates and sync-idle reload; local credit mode and genre scope (PR #1243)',
|
||
'Album detail — album-level favorite heart from album.starred_at; server-backed album rating reconcile on detail (report: HiveMind on Psysonic Discord, PR #1247)',
|
||
'Library — prune orphaned artist browse rows after server-side renames (fixes "Artist not found" ghosts) on full/delta sync + one-time startup reconcile; refresh Artists/Albums catalog on sync-idle (report: Seraphim on Psysonic Discord, PR #1253)',
|
||
'Artist detail — fall back to the local library index when getArtist 404s an album-artist id (fixes Random Albums artist links dead-ending at "Artist not found"); album browse + detail cover resolution fall back to the album\'s first track cover for rows synced without one (fixes missing Random Albums tile art) (report: tummydummy, PR #1254)',
|
||
'Library — follow getAlbum authoritatively for album artist reference so a server-side artist rename heals on resync instead of dead-ending at "Artist not found" (PR #1256)',
|
||
'Library — prune orphaned identity keys from the multi-library dedup sidecar (library-cluster.db) on rebuild so it no longer bloats with rows for removed/renamed tracks (PR #1255)',
|
||
'Servers — connect and run fully behind a custom-header gate (Cloudflare Access / Pangolin): native connect probe + REST proxy so browse/playback/covers pass, add-form failure reasons, live LAN/public badge on server switch, and LAN reclaim from a sticky public endpoint (PR #1273)',
|
||
'Per-device EQ — when System Default is selected, profiles follow the active OS default output and switch on external output changes (PR #1274)',
|
||
'Navidrome public share links — paste/search preview modal, anonymous queue playback, idle server queue-restore guard (PR #1275)',
|
||
'Queue toolbar — Navidrome public share: hide Save Playlist, copy original share URL (PR #1279)',
|
||
'Windows startup hang after #1274 — boot barrel split, stable Wasapi device IDs, legacy EQ key match (PR #1277)',
|
||
'Windows MSI bundle on dev/RC versions — numeric WiX mapping; album easter-egg import chunk (PR #1278)',
|
||
'Windows release builds — Subsonic client id no longer psysonic/undefined in Who is listening (PR #1290)',
|
||
'Track lists — optional album cover thumbnails via standard cover pipeline; queue rows use playback scope (PR #1280)',
|
||
'Internet Radio — Web Audio EQ on HTML5 streams; presets apply without reconnect (PR #1284)',
|
||
'Player bar — hideable stop button, optional album line, drag-reorderable action buttons (request: mikmik on Psysonic Discord, PR #1287)',
|
||
'Persistent shuffle mode — queue-reordering shuffle with restore, survives restart, in sync with other devices and Orbit (request: mikmik on Psysonic Discord, PR #1288)',
|
||
'Playlist and radio custom covers — preserve Navidrome pl-/ra-* getCoverArt ids (fixes blank uploaded covers; PR #1295)',
|
||
],
|
||
},
|
||
{
|
||
github: 'kilyabin',
|
||
since: '1.34.0',
|
||
contributions: [
|
||
'Russian locale improvements (PR #107, PR #120)',
|
||
'Auto-install script for Debian / RHEL (PR #121)',
|
||
'Album cover art in Discord Rich Presence via iTunes API (PR #111)',
|
||
'Tiling WM detection: hide custom TitleBar on Hyprland/Sway/i3/etc. (PR #134)',
|
||
'Russian translation: lyricsServerFirst settings strings (PR #140)',
|
||
'Russian translation refinements (PR #148)',
|
||
'Merge Random Mix & Albums into a single Build a Mix hub (PR #155)',
|
||
'Fullscreen player: software-rendering performance fixes + portrait toggle & dimming setting (PR #156)',
|
||
'Fullscreen player: stop mesh blob and portrait animations in no-compositing mode; remove seekbar box-shadow repaint (PR #175)',
|
||
'Apple Music-style scrolling lyrics with spring-physics scroll for fullscreen player and sidebar; per-style controls (PR #205)',
|
||
'Golos Text and Unbounded fonts with Cyrillic support (PR #206)',
|
||
'Fullscreen & sidebar lyrics: duration-based ease-out scroll animator replacing spring physics; bottom fade for plain lyrics (PR #214)',
|
||
'Sidebar lyrics: YouLy+ source strings render in a single line (PR #215)',
|
||
'Russian locale — 50 missing strings and ru/en phrasing cleanup (PR #1181)',
|
||
],
|
||
},
|
||
{
|
||
github: 'nisrael',
|
||
since: '1.34.0',
|
||
contributions: [
|
||
'Nightfox.nvim theme group in Open Source Classics (PR #114)',
|
||
'Switch reqwest to rustls-tls for cross-platform TLS (PR #112)',
|
||
'ICY stream metadata & AzuraCast Now Playing support (PR #146)',
|
||
],
|
||
},
|
||
{
|
||
github: 'kveld9',
|
||
since: '1.34.4',
|
||
contributions: [
|
||
'Spanish (es) translation — 964 strings (PR #159)',
|
||
'Column-header sorting for albums & playlists (PR #160)',
|
||
'Multi-select for albums, artists & playlists with bulk "Add to Playlist"; collapsible sidebar playlist section; infinite scroll on Artists page; "Remove from Playlist" in context menu (PR #168)',
|
||
'3 visual toggles: cover art background, playlist cover photo, show bitrate badge (PR #181)',
|
||
'8 community themes (AMOLED Black, Monochrome Dark, Amber Night, Phosphor Green, Midnight Blue, Rose Dark, Sepia Dark, Ice Blue) + waveform live theme update (PR #182)',
|
||
'Favorites redesign: sortable columns, genre filter, age range filter, new columns (PR #184)',
|
||
'Albums and playlist headers redesign with improved layout and theme integration (PR #186)',
|
||
'Tracklist column picker overflow fix in AlbumTrackList (PR #188)',
|
||
'Spotify CSV playlist import (PR #190)',
|
||
'Context menu for songs in SearchBrowsePage (PR #191)',
|
||
'Tracklist column picker alignment and toggle fix across Favorites and PlaylistDetail (PR #192)',
|
||
'CSV import: dynamic match threshold, cleaned title search, score display in report (PR #199)',
|
||
'Discord Rich Presence: configurable text templates for details, state and album tooltip (PR #198)',
|
||
'Click-to-toggle duration / remaining time in player bar with persisted preference (PR #212)',
|
||
'Opt-in floating player bar with themed background, accent-colored border, rounded album art, and centered volume section (PR #216)',
|
||
'Linux GPU-vendor auto-detection to configure the WebKitGTK DMA-BUF renderer (disabled on NVIDIA proprietary) (PR #217)',
|
||
'Artist page: continue playback when starting top songs (PR #220)',
|
||
'Floating player bar: scroll-padding fix (PR #221)',
|
||
'Queue Panel — position counter, tri-state duration toggle (total/remaining/ETA), persistent Now Playing collapse, animated EQ indicator (PR #419)',
|
||
'Community themes — redesign pass: removed 5 overlapping / eye-straining palettes, added 8 new dark themes (Obsidian Black, Carbon Grey, Volcanic Dark, Forest Green, Violet Haze, Copper Oxide, Sakura Night, Obsidian Gold) (PR #490)',
|
||
'Customizable queue toolbar — drag-and-drop button reordering, per-button visibility toggles, optional separator, persisted across restarts (PR #534)',
|
||
'Playlist page layout — per-element visibility toggles (Add Songs, Import CSV, Download ZIP, Cache Offline, Suggestions) under Settings → Personalisation (PR #556)',
|
||
'Player bar layout — per-control visibility toggles (Star rating, Favorite, Last.fm love, Equalizer, Mini player) under Settings → Personalisation (PR #627)',
|
||
'Queue panel — persist header duration mode (total / remaining / ETA) across app restarts (PR #625)',
|
||
'Settings: dim disabled toggle rows; Queue toolbar "Clear" → "Clear queue" rename across 9 locales (adopted from PR #558, rewritten in PR #778)',
|
||
],
|
||
},
|
||
{
|
||
github: 'peri4ko',
|
||
since: '1.43.0',
|
||
contributions: [
|
||
'WebView2 idle hooks when Tauri windows are hidden — Windows GPU and compositor mitigation (PR #273)',
|
||
],
|
||
},
|
||
{
|
||
github: 'Sayykii',
|
||
since: '1.46.0',
|
||
contributions: [
|
||
'Discord Rich Presence: cover art from your own server (Subsonic getAlbumInfo2) with three-way picker — none / server / Apple Music (PR #462)',
|
||
'Artist page: group albums by OpenSubsonic releaseTypes (Album / EP / Single / Compilation / Live / Soundtrack / Remix) with deterministic order and i18n section headers in all 9 locales (PR #471)',
|
||
],
|
||
},
|
||
{
|
||
github: 'MihaiCatalin120',
|
||
since: '1.46.0',
|
||
contributions: [
|
||
'Romanian (ro) full UI translation (PR #663)',
|
||
],
|
||
},
|
||
{
|
||
github: 'DanielWTE',
|
||
since: '1.46.0',
|
||
contributions: [
|
||
'Search: queue pasted share links from live search (PR #551)',
|
||
],
|
||
},
|
||
{
|
||
github: 'artplan1',
|
||
since: '1.46.0',
|
||
contributions: [
|
||
'Player: cap persisted queue to ±250-track window — fixes QuotaExceededError on large playlists (PR #756)',
|
||
'Playlists: virtualized tracklist for large playlists — no UI freeze on 10k+ tracks (PR #755)',
|
||
'Favorites: virtualized songs tracklist for large starred collections (PR #805)',
|
||
'Favorites: player-bar star toggle stays synced with album, playlist, and favorites tracklists (PR #1063)',
|
||
],
|
||
},
|
||
{
|
||
github: 'Psychotoxical',
|
||
since: '1.0.0',
|
||
contributions: [
|
||
'Initial app scaffold — Tauri v2 + React + Zustand + Subsonic protocol, multi-server auth (v1.0)',
|
||
'Rust/rodio audio engine replacing Howler.js (v1.2)',
|
||
'Waveform seekbar, MilkDrop visualizer, EQ bars (v1.3)',
|
||
'10-band parametric equalizer with per-theme UI (v1.5)',
|
||
'Replay Gain, Crossfade, Download Folder Modal (v1.6)',
|
||
'Last.fm scrobbling, Similar Artists, Statistics page (v1.7)',
|
||
'TooltipPortal + CustomSelect portal-based UI primitives (v1.7)',
|
||
'Keybindings system + font picker (v1.9)',
|
||
'Lyrics system with LRCLIB integration (v1.12)',
|
||
'Queue management overhaul + DnD (v1.22)',
|
||
'Advanced Search + Genre Mix overhaul (v1.23)',
|
||
'Playlist Management — create/edit/cover upload, drag reorder (v1.24)',
|
||
'Functional tray icon, Minimize to Tray, Sidebar customization (v1.25)',
|
||
'Bulk Select, Song Info modal, Recently Played (v1.26)',
|
||
'In-App Auto-Update + Configurable Home (v1.27)',
|
||
'Infinite Queue + Start Radio + single-click play (v1.28)',
|
||
'Internet Radio with fast-start, ICY metadata support (v1.29)',
|
||
'Discord Rich Presence, offline bulk download, artist images, lazy loading (v1.30)',
|
||
'AutoEQ integration, resizable tracklist columns (v1.31)',
|
||
'Genre browser with filter + FLAC seek fix (v1.20)',
|
||
'Fullscreen Player with dynamic accent color + mesh blobs (v1.34)',
|
||
'Bit-perfect hi-res playback + underrun hardening (v1.34)',
|
||
'Fullscreen lyrics overlay with FsArt crossfade (v1.34.1)',
|
||
'Offline Mode (beta) + MPRIS seek (v1.18)',
|
||
'NSIS Windows installer (v1.19)',
|
||
'WCAG contrast audits across 60+ themes (v1.17-v1.34)',
|
||
'Custom Linux title bar with now-playing display (v1.34)',
|
||
'Device Sync — fixed cross-OS naming scheme + playlist folders (v1.40)',
|
||
'macOS signing + notarization + Tauri auto-updater (v1.40)',
|
||
'Mini player — floating window, custom titlebar, queue DnD, persistent geometry, keyboard shortcut, WebView2 lifecycle fix (PR #162, v1.42.x)',
|
||
'67 themes across 8 groups (Mediaplayer, OS, Games, Movies, Series, Social Media, OSS Classics, Psysonic)',
|
||
'Admin-gated User Management tab with per-user library assignment (PR #222)',
|
||
'Comprehensive mobile UI overhaul (PR #238)',
|
||
'Runtime log levels and debug log export (PR #241)',
|
||
'ReplayGain Auto mode — picks track vs album gain from queue context (PR #242)',
|
||
'Now-Playing Info tab with artist bio, song credits, Bandsintown tour dates (PR #244)',
|
||
'Performance suite — search cover cache, DeviceSync N+1, Albums prefetch, Lyrics IDB, bundle splitting, Artists memo, Genres pagination (PRs #245-#251)',
|
||
'Artist page — user-configurable section visibility and order (PR #254)',
|
||
'Album enqueue via cover hover, context menu, multi-select toolbar (PR #256)',
|
||
'Settings refactor — thematic tab regroup, accordion sub-sections, in-page search (PR #259)',
|
||
'Navidrome admin API hardening (PR #260)',
|
||
'Now-Playing redesign as info dashboard + draggable widget cards (PRs #266, #267)',
|
||
'Sleep timer — circular ring + in-button countdown (PR #272)',
|
||
'Streaming seek UI freeze + snapback fix (PR #236)',
|
||
'Windows: tighten WebView2 idle hooks (follow-up to #273) (PR #276)',
|
||
'Audio: defer chained-track volume to gapless transition (PR #277)',
|
||
'Mini-player: portal volume popover so it cannot get clipped (PR #279)',
|
||
'Mini-player: drop saved position when its monitor is gone (PR #280)',
|
||
'Toolbar: swap Gapless / Infinite Queue icons (closes #274) (PR #284)',
|
||
'Linux audio: prefer PipeWire / Pulse aliases over raw ALSA default (PR #288)',
|
||
'Playlists: bulk-delete button in selection-mode header (PR #290)',
|
||
'Home: refresh Mainstage when active server changes (PR #291)',
|
||
'Search: enqueue on live-search click + reposition context menu on right-click (PR #298)',
|
||
'Tracks library hub page (closes #299) (PR #300)',
|
||
'Home: Discover Songs rail in Mainstage (PR #301)',
|
||
'Search: right-click context menu on artist and album rows (PR #302)',
|
||
'Unified SongRow + paginated song results in search pages (PR #303)',
|
||
'Orbit — Multi-User Listen-Together merged to main (PR #304)',
|
||
'Genres: tag-cloud refactor — log-scaled colour-tinted pills replace icon cards (PR #311)',
|
||
'imageCache: per-component object URLs to fix the blob: load flood (PR #313)',
|
||
'Queue: preserve scroll context on manual click (PR #314)',
|
||
'Seekbar: split waveform style into truewave (analysed) + pseudowave (deterministic) (PR #316)',
|
||
'Settings: restructure Normalization section for clarity (PR #317)',
|
||
'Cross-device resume: flush play-queue position on pause + all exit paths (PR #318)',
|
||
'Login: language picker on the login page (PR #328)',
|
||
'Playlists: confirm before re-adding songs already in the playlist (PR #329)',
|
||
'Discord: debug-build logging for Rich Presence IPC path (PR #330)',
|
||
'Player: persist queue panel visibility + drop dead loudness binding (PR #336)',
|
||
'Playlists: suggestion-row preview UX (PR #365)',
|
||
'macOS: Equalizer canvas blanking fix when laid-out dimensions are missing (PR #384)',
|
||
'Themes: Kanagawa, Atom One and 1984 palettes; OSS Classics regrouped by family (PR #390)',
|
||
'Rust track preview engine + player-bar preview indicator with smart stop semantics (PRs #392, #394)',
|
||
'Tray: now-playing track tooltip + i18n menu labels (PR #395)',
|
||
'Preview: audio start sync, ring animation, download timeout (PR #423)',
|
||
'Statistics: shareable Top-Albums card export (PR #425)',
|
||
'Windows: playback stutter under GPU load — MMCSS Pro Audio promotion + animation pause + reduce-animations toggle (PR #426)',
|
||
'Audio: frame-align gapless-off track-separation silence (fixes mono-channel playback after natural track end) (PR #439)',
|
||
'Settings: 3-state animation mode (Full / Reduced / Static) — replaces boolean reduce-animations toggle (PR #441)',
|
||
'Tracks: Highly Rated rail and per-card star display, with cache layer for ndListSongs (PR #443)',
|
||
'Random Mix: playlist-size picker (50/75/100/125/150) and filter-panel layout cleanup (PR #445)',
|
||
'Queue: optional "Preserve Play Next order" toggle — multiple Play Next inserts queue up behind each other instead of latest-on-top (PR #464)',
|
||
'Library: "favorites only" filter on Albums, Artists and Advanced Search — toolbar toggle reading star overrides live (PR #466)',
|
||
'Settings: keep current active server when adding a new one — no more auto-switch interrupting playback or library context (PR #475)',
|
||
'Help page: full rewrite with 45 focused entries across 10 themed sections (Getting Started / Playback & Queue / Audio Tools / Library & Discovery / Lyrics / Sharing & Social / Personalization / Power User / Offline & Sync / Integrations & Troubleshooting), in-page live search with case-insensitive substring matching and auto-expand on hits, translated to all 9 locales (PR #485)',
|
||
'Library: Browse by Composer — native-API role listing for classical libraries, library-scoped queries, composer as a first-class share entity (PR #487)',
|
||
'Home: "Because you listened" recommendation rail — Last.fm-anchored similar-artist surfacing with round-robin anchor rotation per server (PR #489)',
|
||
'Song Info: absolute file path on Navidrome via native /api/song/{id} — Subsonic only ever returned a relative path (or none on Navidrome), the native endpoint surfaces the full server-side location (PR #504)',
|
||
'Home: Lossless Albums rail + dedicated /lossless-albums page with infinite scroll and header parity (selection mode, enqueue, offline, download ZIPs), streaming load via per-fetch onProgress, sidebar entry default visible, detection via Navidrome native bit_depth-sorted song cursor with always-lossless suffix allowlist (PR #506)',
|
||
'Accessibility: OpenDyslexic font option in the Settings picker — bundled locally via @fontsource/opendyslexic, asymmetric glyph shapes for easier b/d, p/q tracking, Latin-only with translated subtitle in all 9 locales calling out the dyslexia-friendly intent and the Cyrillic/CJK fallback (PR #507)',
|
||
'Settings: tri-state Clock Format (Auto / 24h / 12h) overriding the locale default for the queue ETA and the sleep-timer preview (PR #742)',
|
||
'Servers: inline edit for existing profiles (PR #780)',
|
||
'Interface Scale: scales the entire window — sidebar, queue, player bar, modals and the fullscreen player follow the main content (PR #781)',
|
||
'Local library index (preview): SQLite per-server track store, background initial and delta sync, live and Advanced Search against the local index, integrity verify and auto-reconcile on count drop (PR #846)',
|
||
'Server index-key rebuild: safe dual-DB migration flow, per-server analysis strategy controls, and playback/index scope hardening (PR #864)',
|
||
'Settings: opt-in Linux input-focus repaint — workaround for the WebKitGTK 2.50.x text-field freeze (PR #884)',
|
||
'Discord Rich Presence: configurable activity-name template — member list shows the playing track instead of "Psysonic" (PR #885)',
|
||
'Library browse: lazy 200-row local catalog chunks, unified in-page scroll, cover priority during scroll (PR #890)',
|
||
'Performance Probe: Monitor/Toggles redesign, live CPU/RSS/thread metrics, overlay pins and sparklines, macOS snapshots (PR #890)',
|
||
'Performance Probe: opt-in thread-group CPU poll toggle and includeThreadGroups IPC fix (PR #891)',
|
||
'Queue: switchable display mode — Queue (upcoming only) vs Playlist (full list), with header toggle and settings entry (PR #922)',
|
||
'Community Theme Store: semantic-token refactor, dedicated Themes tab with day/night scheduler, install/update/uninstall, local .zip import, free-form community themes (safety floor + state-reactive styling), and per-theme download counts with popularity/newest/name sorting; 80+ palettes moved to an on-demand CDN repo (PR #1009, #1011, #1012, #1013, #1014, #1015, #1016, #1018, #1020, #1036)',
|
||
'AutoDJ as a standalone playback mode — own toolbar button and audio-settings entry, mutually exclusive with Crossfade and Gapless via a shared transition helper, combined playlist save/load submenu, and boxed playback settings sections (PR #1124)',
|
||
'Settings refactor — reusable boxed panels (SettingsGroup/SettingsToggle) rolled across every tab, Normalization and Track transitions promoted to their own Audio categories, a unified Queue Settings group on Personalisation, and the Audio Output Device category gated out on macOS (PR #1126, #1130)',
|
||
'Artist artwork from fanart.tv — opt-in fullscreen background + artist-header banner, off by default (PR #1137)',
|
||
'Per-device equalizer profiles — opt-in, remembers and restores the EQ for each audio output device (PR #1146)',
|
||
'Hungarian (Magyar) translation (PR #1149)',
|
||
'Theme scheduler — follow the OS light/dark setting as an alternative to the time-based day/night schedule (PR #1163)',
|
||
'Compact buttons — appearance toggle that switches action and toolbar buttons to icon-only across detail headers and browse views, app-wide (PR #1189)',
|
||
'Playlist sort — sort a playlist by date added (newest/oldest) or any track column from a visible sort dropdown (PR #1191)',
|
||
'Configurable artist backdrops — per-place source order + on/off (mainstage hero, artist page, fullscreen), with the mainstage hero showing the artist backdrop and prefetching upcoming ones (PR #1193)',
|
||
'Themed window title bar on macOS — follows the active theme instead of the grey system bar, with the native window buttons floating over it (PR #1199)',
|
||
'Frontend feature-folder restructure — CI-enforced layering guard, added unit/behavior-scenario/boot-smoke tests, and a compile-time frontend/backend IPC contract via tauri-specta (PR #1225)',
|
||
'Completed the tauri-specta typed-IPC cutover — CI guards for bindings freshness and full command registration (PR #1230)',
|
||
'Theme Store: per-theme changelogs shown as an expandable "What\'s new" on each card, plus installed themes with an available update pinned to the top of the list (PR #1240)',
|
||
'Settings → System: community theme authors credited in a Themes sub-section; theme card What\'s new shows the latest version only (PR #1248)',
|
||
'Fullscreen player style — selectable Minimal or Immersive view, with the artist backdrop, cover-derived accent, and rail/Apple lyrics (PR #1249)',
|
||
'Word-by-word lyrics from the server via OpenSubsonic songLyrics v2, no third-party backend needed (PR #1265)',
|
||
],
|
||
},
|
||
{
|
||
github: 'ImAsra',
|
||
since: '1.47.0',
|
||
contributions: [
|
||
'Long-press album Play to shuffle with hold progress animation (PR #888)',
|
||
'WinGet release automation — GitHub Actions workflow that opens manifest PRs on each release (PR #1077)',
|
||
'Dismissible Discord community banner (PR #1222)',
|
||
],
|
||
},
|
||
{
|
||
github: 'kbennett2000',
|
||
since: '1.48.0',
|
||
contributions: [
|
||
'Linux auto-install script — fix curl | sudo bash URL capture bug (PR #1079)',
|
||
],
|
||
},
|
||
{
|
||
github: 'Soli0222',
|
||
since: '1.49.0',
|
||
contributions: [
|
||
'Japanese (ja) full UI translation (PR #1134)',
|
||
],
|
||
},
|
||
{
|
||
github: 'Rextens',
|
||
since: '1.49.0',
|
||
contributions: [
|
||
'Polish (pl) full UI translation (PR #1185)',
|
||
],
|
||
},
|
||
{
|
||
github: 'akirichev',
|
||
since: '1.50.0',
|
||
contributions: [
|
||
'Bulgarian (bg) full UI translation (PR #1228)',
|
||
],
|
||
},
|
||
{
|
||
github: 'daquino94',
|
||
since: '1.50.0',
|
||
contributions: [
|
||
'Italian (it) full UI translation (PR #1250)',
|
||
],
|
||
},
|
||
{
|
||
github: 'norperz',
|
||
since: '1.50.0',
|
||
contributions: [
|
||
'Sync: form POST for large play queues to avoid HTTP 414 behind reverse proxies (PR #1262)',
|
||
],
|
||
},
|
||
{
|
||
github: 'AliMahmoudDev',
|
||
since: '1.50.0',
|
||
contributions: [
|
||
'Accessibility: modal dialogs announce their title to screen readers (aria-labelledby) (PR #1301)',
|
||
],
|
||
},
|
||
] as const;
|
||
|
||
// PR number of a contributor's first listed contribution, used as the
|
||
// tie-breaker when several contributors share the same `since` version.
|
||
function firstContributionPr(entry: { contributions: readonly string[] }): number {
|
||
const match = entry.contributions[0]?.match(/PR #(\d+)/);
|
||
return match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
||
}
|
||
|
||
// Rendered chronologically: ascending by the app version a contributor first
|
||
// appeared in (`since`), then by their first-contribution PR number. Sorting
|
||
// here keeps the order correct no matter where new entries land in the array.
|
||
export const CONTRIBUTORS = [...CONTRIBUTOR_ENTRIES].sort((a, b) => {
|
||
if (isNewer(a.since, b.since)) return 1;
|
||
if (isNewer(b.since, a.since)) return -1;
|
||
return firstContributionPr(a) - firstContributionPr(b);
|
||
});
|
||
|
||
export const MAINTAINERS = [
|
||
{ github: 'Psychotoxical' },
|
||
{ github: 'cucadmuh' },
|
||
] as const;
|
||
|
||
export interface ThemeContributor {
|
||
github: string;
|
||
/** Names of the themes this author has in the store registry, sorted. */
|
||
themes: string[];
|
||
}
|
||
|
||
// The org account that owns the themes repo authors the built-in / official
|
||
// themes; it is the project itself, not a community contributor, so it is kept
|
||
// out of the theme credits (its work is already covered under App credits).
|
||
const NON_CONTRIBUTOR_THEME_AUTHORS = new Set(['psysonic']);
|
||
|
||
// Community theme authors, derived from the store registry — the only source for
|
||
// them, since the app bundles no community themes. Deduplicated by author (folds
|
||
// casing drift in manifests), each carrying the theme names they authored.
|
||
// Author + theme names sorted so the Credits list is stable across fetches.
|
||
export function themeContributorsFromRegistry(
|
||
themes: readonly { author: string; name: string }[],
|
||
): ThemeContributor[] {
|
||
const byAuthor = new Map<string, ThemeContributor>();
|
||
for (const th of themes) {
|
||
const author = th.author?.trim();
|
||
if (!author) continue;
|
||
const key = author.toLowerCase();
|
||
if (NON_CONTRIBUTOR_THEME_AUTHORS.has(key)) continue;
|
||
const entry = byAuthor.get(key) ?? { github: author, themes: [] };
|
||
if (!entry.themes.includes(th.name)) entry.themes.push(th.name);
|
||
byAuthor.set(key, entry);
|
||
}
|
||
return [...byAuthor.values()]
|
||
.map(e => ({ github: e.github, themes: [...e.themes].sort((a, b) => a.localeCompare(b)) }))
|
||
.sort((a, b) => a.github.toLowerCase().localeCompare(b.github.toLowerCase()));
|
||
}
|