Commit Graph

400 Commits

Author SHA1 Message Date
Maxim Isaev c165669db5 feat(queue): show playback source in tech strip with preload tracking
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.
2026-04-17 16:34:11 +03:00
Psychotoxical c50addaacf fix(discord): remove dead fields, timeChanged invoke loop, and unsupported {paused} placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:33:58 +02:00
Maxim Isaev 689d2dc019 fix(audio): harden stream playback transitions and cache promotion
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.
2026-04-17 13:49:31 +03:00
kveld9 5576fb0d13 fix DRP timer 2026-04-16 21:41:22 -03:00
kveld9 28844e8456 release 2026-04-16 20:34:55 -03:00
Psychotoxical b6812de26b remove(seekbar): drop realtime_waveform style
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>
2026-04-16 21:15:48 +02:00
Psychotoxical 7db42d74ef feat: tracklist column reset, device sync cross-platform template fix, filename template builder, privacy policy
- 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>
2026-04-16 19:47:41 +02:00
Psychotoxical bf3d896016 fix(device-sync): auto-import manifest on mount, clear view on disconnect
- 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>
2026-04-15 23:16:06 +02:00
Psychotoxical 442ed9d191 feat(device-sync): manifest, cancel, font picker + sync status fix
- 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>
2026-04-15 22:59:27 +02:00
Psychotoxical f437017359 fix(cli): gate OnceLock import behind linux cfg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 20:03:56 +02:00
Psychotoxical 34acc46c12 fix(cli): suppress dead-code warnings on Windows for Linux-only helpers
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>
2026-04-15 20:01:17 +02:00
Maxim Isaev 6533991e7d feat(cli): expand remote player, opaque play id, tray without libindicator
Add CLI commands for stop, shuffle, repeat, mute, star, rating, reload,
server list/switch, and Subsonic search; publish extra snapshot fields and
server/search JSON for tooling. Single `play <id>` resolves track, album,
or artist; artists play a shuffled full discography.

Guard Linux tray creation with catch_unwind when Ayatana/AppIndicator .so
is missing. Harden bash completion against zsh sourcing (compopt). Narrow
AudioEngine field visibility to silence private_interfaces warnings.
2026-04-15 01:47:46 +03:00
Maxim Isaev c75297fcf6 feat(cli): completions, library/audio commands, server switcher
Embed bash and zsh completion scripts and add a `completions` subcommand.
Extend the player CLI (library, audio device, instant mix) and surface
`music_library` in snapshots. Add a shared active-server switcher in the
header and locales; instant mix can reseed the queue from CLI and UI.
2026-04-15 01:47:46 +03:00
Psychotoxical 795701a447 chore(release): bump version to 1.34.11
- Changelog for v1.34.11 (Opus, Device Sync, community themes, SSL fix)
- Contributors updated in Settings About (PR #181, #182, #183)
- Version bumped to 1.34.11 in package.json, tauri.conf.json, Cargo.toml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 22:28:15 +02:00
Maxim Isaev 4c6fc4d6d5 merge: integrate origin/main into feat/opus-support 2026-04-14 22:52:35 +03:00
Psychotoxical d34de09673 feat(device-sync): overhaul Device Sync page
- New two-panel layout with live album search (search3, 300ms debounce)
  and 10 random albums when search is empty — replaces full paginated load
- Pre-sync summary modal: shows files to add/delete, net change, available
  space; Proceed button disabled when space is insufficient
- calculate_sync_payload now filters already-synced tracks (path exists on
  device) so add_bytes/add_count reflect the true delta
- Space check accounts for pending deletions: addBytes > availableBytes + delBytes
- Separate deviceSyncJobStore for ephemeral job progress state
- Removable drive detection + auto-poll every 5 s via sysinfo
- Desired-state diff: de-selecting a synced source stages it for deletion
  instead of silently removing it
- Status badges (Synced / Pending / Deletion) with matching row highlights
- Live-Search badge () and Zufallsalben section label (⇌) in album browser
- Status summary row height aligned with search field (52 px)
- i18n: all new keys added to all 8 locales (en/de/fr/nl/nb/zh/ru/es)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:37:35 +02:00
Maxim Isaev 1ff4f1ef0f feat(audio): add Opus playback via symphonia-adapter-libopus
Wire a lazy CodecRegistry built with symphonia::default::register_enabled_codecs
plus symphonia_adapter_libopus::OpusDecoder for SizedDecoder and radio decoders.

Raise package rust-version to 1.89 to match the MSRV required by
symphonia-adapter-libopus.
2026-04-14 22:17:29 +03:00
Psychotoxical 915f0143f7 fix(ssl): strip trailing slash in getBaseUrl + trust OS certificate store for HTTPS streaming
Fixes two bugs reported in #178:

- getBaseUrl() now strips trailing slashes from server URLs, consistent
  with restBaseFromUrl(). A trailing slash caused double-slash stream URLs
  (//rest/stream.view) which Caddy rejects. Trailing slash also caused
  browsing to return 0 results. Fix manually ported from PR #179 by kveld9.

- reqwest now loads the OS native certificate store in addition to
  Mozilla's webpki roots (rustls-tls-native-roots feature). Fixes HTTPS
  streaming failures where the server certificate is signed by a local CA
  (e.g. Caddy internal CA) that is trusted in the system keychain but not
  in Mozilla's root store.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 18:16:08 +02:00
Psychotoxical 17a5c92174 feat(device-sync): USB/SD card sync page (WIP)
Adds a new Device Sync page for transferring music from Navidrome to
USB drives or SD cards. Sidebar entry is hidden by default.

- Four new Tauri commands: sync_track_to_device, compute_sync_paths,
  list_device_dir_files, delete_device_file
- Filename template engine with cross-platform path sanitization
- 4-concurrent-worker download, live progress via device:sync:progress event
- Persistent source list (albums/playlists/artists) with checkbox deletion
- Expandable artist tree in browser panel (per-album selection)
- i18n: DE + EN complete; other locales have stub keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 00:02:13 +02:00
Psychotoxical 58f99ae846 chore: bump version to 1.34.11-dev 2026-04-13 22:25:49 +02:00
Psychotoxical 01f71cf50c chore(release): bump version to 1.34.10
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 22:21:07 +02:00
cucadmuh 4207455440 fix(audio): Linux output device selection and watcher (#176)
* fix(audio): stabilize Linux output device picker and watcher

Keep pinned ALSA/cpal device ids stable when enumeration omits the active
sink or returns an equivalent name. Stop Linux device-watcher from clearing
the pin based on missing list entries; macOS and Windows still treat repeated
absence as unplugged. Settings refresh flow calls canonicalize and refetches
the list; add i18n for the out-of-list device label.

* fix(settings): sort audio output devices by label

cpal enumeration order is arbitrary; order the dropdown by readable label
and place the current OS default device first among concrete outputs.
2026-04-13 21:48:21 +02:00
Psychotoxical 2326f1f94b feat(linux): AppImage bundle + force X11/XWayland on all Linux packages
- CI: add squashfs-tools dep, APPIMAGE_EXTRACT_AND_RUN=1, appimage to
  --bundles, and *.AppImage to upload glob
- main.rs: set GDK_BACKEND=x11 and WEBKIT_DISABLE_COMPOSITING_MODE=1
  on Linux before Tauri init — WebKitGTK on Wayland is unstable;
  both vars are overridable by setting them before launch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 20:45:00 +02:00
cucadmuh 9cd4743d1c feat(settings): audio output device picker (labels, OS default, live refresh) (#173)
* feat(settings): clearer audio device labels for duplicate ALSA names

Show HDMI outputs as "Card (HDMI n)" from hdmi:DEV indices; include PCM and
optional subdevice for hw/plughw/sysdefault; label other ALSA plugins with
iface and PCM. When labels still collide, append a structured hint
(iface · card · PCM) instead of only truncating the raw device string.

* feat(settings): improve audio output device picker

Parse ALSA-style ids into clearer labels (HDMI with DEV index, PCM/subdevice
for hw/plughw/sysdefault). Disambiguate colliding labels; share stderr
suppression for Linux device enumeration.

Add audio_default_output_device_name and tag the matching list entry as the
current system output (i18n). While the Audio tab is open, refresh list and
mark on audio:device-changed and audio:device-reset without toggling the
refresh spinner. Show an error toast if listing devices fails.
2026-04-13 20:13:22 +02:00
Psychotoxical ec021516c7 fix(audio): device-reset false positive + stderr noise + i18n tooltip
- Device watcher requires 3 consecutive misses (~9 s) before triggering
  audio:device-reset, preventing false positives when ALSA temporarily
  hides a busy device from output_devices() enumeration
- Add stderr suppression to open_stream_for_device_and_rate (last source
  of ALSA terminal noise on Linux)
- Fix tooltip showing raw key 'common.refresh' — replaced with
  settings.audioOutputDeviceRefresh (added to all 8 locales)
- Device switch restarts track from beginning (no seek-back)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:00:36 +02:00
Psychotoxical 1cf5faff21 fix(audio): suppress ALSA stderr noise during device enumeration
Device watcher (3 s loop) and audio_list_devices both called
cpal output_devices(), triggering ALSA to probe unavailable backends
(JACK, OSS, dmix) and spam stderr with error messages.

Fix: redirect fd 2 to /dev/null for the duration of each enumeration
on Unix via libc dup/dup2 + RAII guard. Also add a module-level
cache in Settings.tsx so audio_list_devices is only invoked once
per app session instead of on every Audio tab activation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:30:24 +02:00
Psychotoxical 16cb4f5a6f feat(audio): audio output device selection (closes #169)
Adds the ability to choose which audio output device Psysonic plays
through — useful for USB DACs, dedicated soundcards, and multi-device setups.

Rust (audio.rs):
- open_stream_for_device_and_rate(device_name, rate): opens a named
  device by cpal name, falls back to system default if not found
- AudioEngine.selected_device: persists the chosen device across
  stream reopens so hi-res rate switches stay on the right device
- audio_list_devices: Tauri command — returns all output device names
- audio_set_device: Tauri command — switches device immediately,
  drops old sinks, emits audio:device-changed
- start_device_watcher: now handles two cases:
    1. No pinned device + system default changed → reopen on new default
    2. Pinned device disappeared (DAC unplugged) → clear selected_device,
       fall back to system default, emit audio:device-reset

Frontend:
- authStore: audioOutputDevice (string | null), persisted
- playerStore: applies stored device on cold start
- App.tsx: listens to audio:device-reset, clears authStore device
  and restarts playback on the fallback device
- Settings → Audio tab: device dropdown at top (above Hi-Res and EQ),
  uses CustomSelect (portal-based, styled), all 8 locales
- CustomSelect: added disabled prop

Note: exclusive mode (WASAPI exclusive, CoreAudio exclusive) is out
of scope for now.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:20:23 +02:00
kveld9 21e26e2604 chore: remove tauri generated schemas from tracking 2026-04-12 17:54:16 -03:00
kveld9 bef6941a2b feat: add multi-selection and context menu for playlist management 2026-04-12 17:53:52 -03:00
Psychotoxical 805b6bf163 chore(gen): update Tauri schema files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 18:44:51 +02:00
Psychotoxical 4a2baaf87d chore: bump version to 1.34.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 12:38:48 +02:00
kilyabin 3d07a877f2 feat(fullscreen): performance fixes + appearance settings
Adds no_compositing_mode Tauri command; frontend adds html.no-compositing on Linux to replace GPU-only CSS effects (backdrop-filter, filter, mask-image) with software-friendly equivalents.

Settings → Appearance → Fullscreen Player: toggle for artist portrait visibility + 0–80% dimming slider.

Fix: long words in lyric lines now wrap correctly.
2026-04-12 11:26:44 +02:00
Psychotoxical 20dabbfd03 feat(lyrics): configurable sources with drag-to-reorder + feat(audio): ReplayGain pre-gain & fallback
Lyrics Sources:
- Replace lyricsServerFirst + enableNeteaselyrics toggles with a new
  Settings section — three sources (Server, LRCLIB, Netease) each
  individually toggled and drag-reorderable via psy-drop DnD
- useLyrics.ts iterates sources in user-defined order, skipping disabled
  ones; embedded SYLT from local files still wins unconditionally
- onRehydrateStorage migration from legacy fields on first load

ReplayGain Pre-Gain:
- New authStore fields: replayGainPreGainDb (0…+6 dB) and
  replayGainFallbackDb (-6…0 dB, untagged / radio)
- audio.rs compute_gain applies pre_gain_db and fallback_db
- Settings sliders under ReplayGain mode selector
- Radio HTML5 volume scaled by fallbackDb factor on playRadio

i18n(ru): incorporate PR #148 translation improvements (kilyabin)
All 7 locales updated for new keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 23:57:36 +02:00
Psychotoxical c142e9e983 feat(radio): PLS/M3U playlist resolution + ICY metadata for playlist streams
Resolves PLS and M3U/M3U8 playlist URLs to their first direct stream URL
before playback and ICY metadata fetching. Stations configured with a
.pls or .m3u URL (e.g. SomaFM, schizoid.in) now play correctly and
report track metadata via ICY headers.

- Rust: parse_pls_stream_url / parse_m3u_stream_url helpers
- Rust: resolve_playlist_url (shared) + resolve_stream_url Tauri command
- fetch_icy_metadata: auto-resolves playlist URLs before connecting
- playerStore: playRadio() awaits resolve_stream_url before setting audio src

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 23:18:53 +02:00
Psychotoxical 4b6888049f fix(audio): properly suppress unused-variable warning for DecodeError msg
Use `let _ = msg` under `#[cfg(not(debug_assertions))]` so the binding
remains available in debug builds for the eprintln! format string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:34:55 +02:00
Psychotoxical 16c511c167 fix(audio): suppress unused variable warning in DecodeError match arm
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:27:40 +02:00
Psychotoxical 649f5223b4 chore: bump version to 1.34.8
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:06:19 +02:00
Psychotoxical 6ffcd6f6fa feat(lyrics): add Netease Cloud Music as opt-in fallback source
Netease is queried only when server and LRCLIB both return nothing,
preserving the existing lyrics chain completely. Off by default.

- Rust command `fetch_netease_lyrics` proxies Netease API (CORS bypass)
- `src/api/netease.ts` TypeScript wrapper via invoke
- `authStore.enableNeteaselyrics` toggle (default: false)
- `useLyrics`: Netease fires last in fallback chain when enabled
- Settings toggle in the Lyrics section
- `lyricsSourceNetease` label in LyricsPane
- i18n: all 7 languages (en, de, fr, nl, nb, ru, zh)

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:38:33 +02:00
Nils Israel 46cefb5712 feat: add ICY metadata and AzuraCast radio streaming support
Agent-Logs-Url: https://github.com/nisrael/psysonic/sessions/88faada5-28bb-446f-b53b-46a0efef387e

Co-authored-by: GitHub Copilot <198982749+copilot@users.noreply.github.com>
Signed-off-by: Nils Israel <nils@sxda.io>
2026-04-10 10:38:59 +02:00
Psychotoxical 5f0fb5dcbd feat(audio): auto-switch to new audio output device at runtime
Adds a background device-watcher that polls the OS default output device
every 3 s via CPAL. When the device changes (Bluetooth headphones connecting,
USB DAC plugging in, etc.) the stream is reopened on the new device, the old
Sink is dropped (it was bound to the now-closed OutputStream), and
audio:device-changed is emitted to the frontend.

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

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:40:12 +02:00
Psychotoxical 15dc970f53 fix(audio): restore msg binding in DecodeError arm, use SlidersVertical for EQ icon
- Revert _msg → msg in DecodeError match arm; the variable is used in the
  debug_assertions eprintln! block so the underscore prefix caused a compile error
- Replace SlidersHorizontal with SlidersVertical (lucide-react) in PlayerBar,
  LiveSearch, and AdvancedSearch so the EQ/filter icon shows vertical bars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:28:12 +02:00
Psychotoxical f304589ea1 fix(audio): suppress unused variable warning in DecodeError match arm
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:15:55 +02:00
Psychotoxical 22f7de45e8 fix(audio): set User-Agent header, add debug fetch logging
Set psysonic/<version> as User-Agent on the audio reqwest::Client to
prevent reverse proxies (nginx, Caddy, Traefik) from blocking requests
with a 403. Add #[cfg(debug_assertions)] logging in fetch_data to show
the exact URL, status, content-type and server header — useful for
diagnosing proxy-related 403s reported by users.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:52:02 +02:00
Psychotoxical 9671f89a48 chore: bump version to 1.34.7, add changelog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:28:54 +02:00
Psychotoxical 9567a94dea feat(windows): replace GDI taskbar icons with embedded .ico assets
Strips the legacy monochrome GDI drawing code and loads high-quality
icons from embedded .ico files via CreateIconFromResourceEx. Fixes
windows 0.58 import paths (Controls→Shell subclassing, TaskbarList CLSID)
and adds proper cleanup for all four HICONs on WM_NCDESTROY.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:14:21 +02:00
Psychotoxical 1ba8619a37 chore: remove preview-update debug button, add GDI features, add contributor
- Remove debug "Preview Update Modal" button from Settings/About
- Add Win32_Graphics/Win32_Graphics_Gdi features for taskbar GDI icons
- Add sorensiimSalling contributor credit for Russian translation (PR #140)

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 22:12:35 +02:00
Psychotoxical fd834314ba fix(audio): VLC-style frame dropping for corrupt MP3s, silence logs in release
Replace the fixed DECODE_MAX_RETRIES (3) loop with a consecutive-error counter
that tolerates up to 100 bad frames before giving up — matching how VLC handles
files with a handful of invalid main_data offset frames. Frame-drop logs are
wrapped in #[cfg(debug_assertions)] so production builds stay silent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:42:38 +02:00
Psychotoxical a78c0fe9ac fix: font switching, layout scaling, embedded lyrics, folder opening
- fix(fonts): update CSS var declarations to @fontsource-variable naming
  ('Inter Variable', 'Outfit Variable', etc.) so dynamic font switching works
- fix(layout): 100vh → 100% on .app-shell to fix Windows WebView2 playerbar drift;
  1fr → minmax(0,1fr) in all grid-template-rows + remove min-height: 720px to fix
  Linux playerbar disappearing at high zoom or small window sizes
- fix(updater): replace shell open() with Rust open_folder command to bypass
  shell:allow-open capability scope blocking local paths on Windows
- fix(lyrics): add get_embedded_lyrics Tauri command (id3 crate for MP3 SYLT/USLT,
  lofty for FLAC SYNCEDLYRICS/LYRICS); fix parseLrc regex for LRC without fractional
  seconds; fix SubsonicStructuredLyrics to accept both synced and issynced fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 16:52:27 +02:00