Existing users keep the rail/classic experience they're used to;
the new Apple Music-like style is opt-in via the FS popover and
the Sidebar Lyrics Style setting in Appearance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds YouLyPlus karaoke lyrics + static-only lyrics toggle, collapses
the advanced Discord options under one header, and ships the real
macOS microphone-prompt fix (cpal patch forcing DefaultOutput on all
output streams).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Supersedes PR #203's out-of-tree upstream-src layout now that the Nix
build lives in the canonical repo. The flake treats `self` as the
source of truth — nothing in flake.nix or psysonic.nix needs a manual
version bump per release.
flake.nix
- `inputs.upstream-src` dropped; `src = self` throughout
- Dev shell retains the full WebKitGTK / GStreamer / AppIndicator
closure needed for tauri:dev on NixOS (Linux x86_64 + aarch64)
nix/psysonic.nix
- Version read from package.json (upstreamMeta.version removed)
- `srcClean` filter excludes node_modules / dist / target / .git /
result / .flatpak-builder
- `cargoDeps = rustPlatform.importCargoLock` with empty
`outputHashes` — sufficient for our local `[patch.crates-io]`
path overrides (cpal-0.15.3, symphonia-format-isomp4)
- Wraps the installed binary with LD_LIBRARY_PATH + GST_PLUGIN_PATH
+ GIO_EXTRA_MODULES + GDK_BACKEND=x11 + WebKit disable flags
nix/upstream-sources.json
- Reduced to `{ npmDepsHash }`. Placeholder for the initial commit;
the verify-nix job rewrites it on every release from the actual
`prefetch-npm-deps` output.
.github/workflows/release.yml
- New `verify-nix` job gated on `create-release` (same as the
Tauri builds). Checks out `main`, installs Nix, recomputes
`npmDepsHash`, refreshes `flake.lock`, runs
`nix build .#psysonic --no-link` as the sanity check, then
commits lock + hash updates back to `main` when they changed.
- The old standalone `upstream-release-nix.yml` is intentionally
not ported; its scheduled re-poll of the upstream release is
redundant now that the source lives here.
Tarball publishing and Cachix upload remain explicitly out of scope —
those ship in a follow-up workflow tied to the cache setup.
"Fetch covers from Apple Music" and "Custom text templates" are now both
tucked into a single collapsible "Advanced Discord options" block (default
collapsed) that only appears when Discord Rich Presence is enabled.
Reduces vertical noise in Settings → General for the common case where
users just flip Rich Presence on and leave the defaults.
Uses the same chevron-rotate pattern as the font picker and contributors
list. Adds `settings.discordOptions` key in all 8 locales.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two independent user-facing fixes rolled together.
── Lyrics: YouLyPlus provider (issue #172) ──
Adds word-by-word synced (karaoke) lyrics as an alternative lyrics mode.
Backed by the public lyricsplus aggregator (Apple Music / Spotify /
Musixmatch / QQ Music) — no API keys on our side, subscription costs are
borne by the backend operator. Five mirrors are tried on network failure.
Settings → Lyrics now exposes a mode radio (Standard vs YouLyPlus) plus
a static-only toggle. YouLyPlus misses silently fall back to the existing
server + LRCLIB + Netease pipeline so obscure tracks still resolve. The
drag-to-reorder source list is hidden in YouLyPlus mode since the
external aggregator manages its own source priority.
Rendering: per-word spans on each line, active word highlighted with
accent-tinted glow. Subscribed imperatively via usePlayerStore.subscribe
so 500 ms progress ticks do not re-render the whole lyrics block. The
Fullscreen Player reuses the same pattern; the 5-line rail layout is
unchanged. white-space: pre on .fs-lyric-word preserves the trailing
spaces the API embeds in each syllabus token (flex context would
otherwise collapse them).
i18n: new keys in all 8 locales (de, en, fr, nl, nb, ru, es, zh).
── macOS mic-prompt suppression ──
Ships a vendored cpal 0.15.3 at patches/cpal-0.15.3/ wired via
[patch.crates-io]. The only change is in src/host/coreaudio/macos/mod.rs:
audio_unit_from_device now unconditionally uses IOType::DefaultOutput for
output streams instead of conditionally choosing HalOutput.
AUHAL (HalOutput) is classified as input-capable by macOS TCC and
triggers the microphone-permission dialog at first launch / after each
update even for playback-only apps. Previous attempts (removing
NSMicrophoneUsageDescription, setting com.apple.security.device.audio-input
false in Entitlements.plist) did not suppress the prompt because TCC
fires at AudioUnit instantiation, not at plist level. The upstream fix
in cpal PR #1070 / cpal 0.17 only helps when the pinned device equals
the system default; always-DefaultOutput covers all cases.
Tradeoff: per-device output selection is a no-op on macOS — the stream
always follows the system default. Settings.tsx surfaces this via
audioOutputDeviceMacNotice (hides the CustomSelect on macOS) and skips
audio_list_devices + device-watcher effects there. Matches the behaviour
of Apple Music / Spotify on macOS.
Also removes the now-obsolete com.apple.security.device.audio-input
entitlement (sandbox is disabled anyway, so it was ignored).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Latte: fix server dropdown dark background (--bg-card instead of hardcoded #1e1e2e), add back-button white text over album cover overlay, fix NowPlaying white-on-white text (artist/album, badges, bio, tracklist, star/heart icons use semantic vars).
GTA: bump --text-muted from #484848 to #707070 and --text-primary from #e8e8e8 to #d8d8d8 — sidebar section labels, Settings descriptions and album detail info were nearly invisible on the near-black background.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cpal/CoreAudio triggers macOS TCC mic check during output device enumeration.
Without NSMicrophoneUsageDescription, macOS silently denies input access instead
of showing a dialog — output playback is unaffected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add source badges for offline, cache, and stream playback in the queue tech line, including localized tooltips across shipped locales.
Track Rust preload-ready events by stream track id and latch the selected source per track, with dev logs to debug preload/source mismatches during next-track handoff.
Add resilient stream-first track playback with reconnect/range recovery, seek fallback handling for non-seekable starts, and promotion of completed streamed bytes into hot cache.
Also add crossfade/gapless backup preload windows to keep automatic transitions smooth on imperfect networks.
Downloaded and fully decoded the entire audio file on every track
change — too CPU/bandwidth intensive to keep. Removes SeekbarStyle
variant, Rust command, waveform_decode function, all frontend canvas
code, seekbarRealtime_waveform i18n keys across all 8 locales, and
unused urlencoding dependency from Cargo.toml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Condensed 27 feature bullets to 14 key highlights, replaced exact
theme count with "wide selection", added CLI Control entry, and
removed the completed roadmap section (redundant with features list).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add "Reset to defaults" button to column picker in AlbumTrackList, PlaylistDetail, Favorites (all 8 locales)
- Fix device sync cross-OS status detection: store filenameTemplate in manifest, use it for path computation on import
- Redesign filename template UI: preset buttons (Standard/Multi-Disc/Alt. Folder), clickable token chips ({artist}, {album}, etc.), clear button
- Fix suggestions section in PlaylistDetail missing album column rendering
- Add PRIVACY.md documenting all third-party integrations (Last.fm, LRCLIB, Apple Music, Discord); link from README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Read psysonic-sync.json automatically when DeviceSync page opens and
drive is already connected (no manual folder re-select needed)
- Always replace sources from manifest when choosing a folder, so
switching between sticks loads the correct album list
- Hide source list and status badges when drive is disconnected
- Reset import flag on disconnect so re-plugging triggers a fresh import
- Fix unused `mut` warning in cancel_device_sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Write psysonic-sync.json to device after sync/deletions; auto-import
on folder select when localStorage is empty (cross-platform handoff)
- Add cancel button during active sync: AtomicBool flag per job,
tasks bail after acquiring semaphore, cancelled state in UI
- Fix sync status staying "pending": normalize template path separators
to OS separator (Windows: '/' → '\') so compute_sync_paths and
list_device_dir_files produce matching strings for Set comparison
- Add Geist and JetBrains Mono as variable fonts; font picker collapsible
- Fix device mount detection on Windows: removable drive letters (E:\)
were incorrectly skipped alongside system roots; strip \?\ prefix
from canonicalized paths before mount-point comparison
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract ALL_NAV_ITEMS to src/config/navItems.ts and add randomMix/randomAlbums
nav items. A new toggle in Settings switches between a single "Build a Mix" hub
and separate "Random Mix" / "Random Albums" sidebar entries (randomNavMode in
authStore). Fixes reorder crash caused by hidden items being overwritten with
undefined during the merge step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap tauri_identifier, single_instance_bus_name and single_instance_object_path
with #[cfg(target_os = "linux")] since they are only reachable via D-Bus paths.
Move the argv collection into the linux-only block in lib.rs for the same reason.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.content-body has overflow-x:hidden — setting overflow-x:visible on
.tracklist would clip wide tables instead of scrolling them. Keep auto.
Also remove Spanish code comments (contributors are international).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PR #191 moved the declaration to the top of the component but left
the original at line 139, causing a 'has already been declared' error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- s.isrc from Subsonic API may be non-string at runtime; add typeof guard
to prevent TypeError crashing all 80 search calls into "network errors"
- Restore isPublic toggle in PlaylistEditModal footer (accidentally removed
in PR #190); keep new Cancel button alongside it
- Move @types/papaparse from dependencies to devDependencies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PlaylistEditModal footer lost the isPublic toggle in the CSV import PR;
restore it alongside the new Cancel button
- @types/papaparse is a type-only package — belongs in devDependencies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>