Commit Graph
100 Commits
Author SHA1 Message Date
vicrodh 8b7a2dd6e6 fix(i18n): catch and remove remaining t-shadow reduce callback
cx
2026-02-09 02:34:40 -06:00
vicrodh 3b1f6b59d4 fix(i18n): remove t-shadow callbacks in core views
cx
2026-02-09 02:30:55 -06:00
vicrodh fb3204f2e4 merge(pre-release): sync latest integration branch into plex feature
cx
2026-02-09 02:25:37 -06:00
vicrodh ff15efde39 chore(i18n): add checker for t shadowing in svelte files
cx
2026-02-09 02:15:24 -06:00
vicrodh 3baa167d54 fix(i18n): avoid callback shadowing in local library view
cx
2026-02-09 02:13:10 -06:00
vicrodh b1d47ded31 merge fixes/1.1.10/debug into pre-release
Brings in: sidebar virtualization, alpha-index fix, quality badge
tooltips, developer mode, factory reset, logs modal, back navigation,
scroll position memory, custom tooltip system, artist click-through,
download button fixes, and more.

Conflicts resolved:
- main.rs: keep pre-release WebKitGTK pin + fixes' VM detection,
  developer settings, and logging module
- FavoritesView: keep virtualized components + fixes' top-bar
- SearchView: keep virtualized album grid
2026-02-09 01:59:41 -06:00
vicrodh b08dc17c0d fix(plex): hydrate track list quality without blocking UI
cx
2026-02-09 01:58:45 -06:00
vicrodh bdde90fc0d feat(plex): persist hydrated quality metadata in cache
cx
2026-02-09 01:51:30 -06:00
vicrodh bf99a19eb3 fix(plex): hydrate track quality and show plex source indicator
cx
2026-02-09 01:47:24 -06:00
vicrodh ddf4142188 fix(appimage): pin WebKitGTK to 2.44.0-2 to prevent EGL crash
The AppImage EGL_BAD_PARAMETER crash is caused by how Tauri's
linuxdeploy bundles newer WebKitGTK versions (tauri-apps/tauri#11994).

Fix: pin WebKitGTK to 2.44.0-2 on ubuntu-24.04, matching the proven
fix from other Tauri apps (audioling#40, OmniKee).

Also revert runtime env var workarounds (LIBGL_ALWAYS_SOFTWARE,
default-off hardware accel) since the fix is at the build level.
Keep only the NVIDIA DMA-BUF workaround which is still needed.
2026-02-09 01:28:11 -06:00
vicrodh 228563454b feat(plex): checkpoint plex lan integration and quality sync
cx
2026-02-09 01:28:11 -06:00
vicrodh 52f2d9ded5 fix: force software rendering when hardware accel is disabled
WEBKIT_DISABLE_DMABUF_RENDERER alone is not enough — WebKit still calls
eglGetDisplay() for compositing and crashes with EGL_BAD_PARAMETER on
systems without working EGL (VMs, some driver combinations).

Setting LIBGL_ALWAYS_SOFTWARE=1 forces Mesa to use llvmpipe so the EGL
initialization succeeds through software rasterization.
2026-02-09 00:50:41 -06:00
vicrodh 3db93a00bf fix: disable WebKit hardware acceleration by default on Linux
EGL/DMA-BUF renderer crashes on many GPU/driver/compositor combinations,
not just NVIDIA. Default to software rendering for all GPUs and allow
opt-in via QBZ_HARDWARE_ACCEL=1.
2026-02-09 00:27:15 -06:00
vicrodh 0c3a2d15b1 fix: remove invalid artistId/onArtistClick props from AlbumCard usage
AlbumCard's Props interface doesn't include artistId or onArtistClick.
Remove these from VirtualizedFavoritesAlbumGrid to fix type error.
2026-02-08 21:33:07 -06:00
vicrodh 0c5c9f22ec perf: batch album download status checks
Change loadAllAlbumDownloadStatuses from Promise.all(all) to
chunked batches of 10 with setTimeout(0) between batches to
yield to the UI thread and prevent blocking during large
album collections.
2026-02-08 21:32:10 -06:00
vicrodh 5e970f9ca8 feat: add inline virtualization to SearchView results
Virtualize albums, tracks, and artists tabs with binary search
and absolute positioning. Each tab uses its own scroll container
with ResizeObserver for responsive grid columns. Load More buttons
render as virtual items at the bottom of each list.
2026-02-08 21:31:32 -06:00
vicrodh fda6449ec9 feat: integrate VirtualizedFavoritesAlbumGrid into FavoritesView
Replace 4 album render paths (grouped grid, grouped list,
ungrouped grid, ungrouped list) with single virtualized component.
Remove ~110 lines of duplicated album CSS now in component.
Enable no-outer-scroll for albums tab.
2026-02-08 21:27:24 -06:00
vicrodh d49eaf000f feat: add VirtualizedFavoritesAlbumGrid component
Binary search virtualization for favorite albums in both grid
and list modes. Grid uses AlbumCard with responsive columns,
list uses inline rows with QualityBadge. Supports grouping,
scroll-to-group, and all album action callbacks.
2026-02-08 21:25:59 -06:00
vicrodh 5f7a0ae57d feat: integrate VirtualizedFavoritesArtistList into sidepanel
Replace raw {#each} loops in artist sidepanel left column with
virtualized list component. Remove ticker animation state from
parent (now encapsulated in VirtualizedFavoritesArtistList).
Clean up unused CSS for artist-list-* classes.
2026-02-08 21:24:40 -06:00
vicrodh a0fbd5b7b4 feat: add VirtualizedFavoritesArtistList component
Binary search virtualization for artist sidepanel list.
Includes ticker animation for long names, group headers,
and scroll-to-group support. 64px row height, 10-item buffer.
2026-02-08 21:23:36 -06:00
vicrodh d042000a3d feat: integrate VirtualizedFavoritesArtistGrid into FavoritesView
Replace raw {#each} loops for artist grid (grouped + ungrouped)
with virtualized component. Artists now render only visible cards
in DOM, fixing 20-second UI freezes with large artist collections.
2026-02-08 21:22:44 -06:00
vicrodh af5704df7c feat: add VirtualizedFavoritesArtistGrid component
Binary search virtualization for favorite artists grid view.
Supports grouped/ungrouped modes, responsive columns via
ResizeObserver, scroll-to-group, and lazy image loading.
Part of issue #56 fix for 20-second UI freezes.
2026-02-08 21:21:45 -06:00
vicrodh 67f9d6a505 fix(appimage): disable WebKit GPU compositing to fully prevent EGL
GDK_BACKEND=x11 alone is not enough: WebKitGTK still initializes
EGL for GPU compositing under X11, and the bundled libEGL sees
WAYLAND_DISPLAY, tries the Wayland EGL platform, and aborts with
EGL_BAD_PARAMETER.

Add WEBKIT_DISABLE_COMPOSITING_MODE=1 which prevents WebKitGTK
from initializing EGL entirely. This flag caused ghost app on
native Wayland but is safe on X11 (which we force for AppImage).
2026-02-08 20:59:35 -06:00
vicrodh 76c24f48d3 fix(appimage): unconditionally force X11 on Wayland to prevent EGL crash
The previous workaround guarded the GDK_BACKEND override with
is_none(), so if the AppImage runtime or desktop session had already
set GDK_BACKEND=wayland, the fix was silently skipped.

AppImage builds have a fundamentally broken EGL stack on Wayland:
libEGL is present but incomplete, Mesa EGL loaders are missing,
and eglGetDisplay() returns EGL_BAD_PARAMETER with no possible
runtime fallback.

Changes:
- AppImage + Wayland now unconditionally sets GDK_BACKEND=x11,
  overriding any existing value
- Also sets QT_QPA_PLATFORM=xcb and WEBKIT_DISABLE_DMABUF_RENDERER=1
  in the same block for belt-and-suspenders safety
- Adds QBZ_FORCE_WAYLAND=1 escape hatch for power users
- Skips all other GDK/DMABUF logic when AppImage forced X11
- Simplifies NVIDIA DMA-BUF logic (no longer Wayland-gated)
- Does NOT affect RPM, DEB, Flatpak, or native builds
2026-02-08 20:17:20 -06:00
vicrodh 464b57d573 fix(favorites): prevent content from overlapping sticky alpha-index
Add z-index: 2 to .alpha-index so artist/album cards don't render
on top of the sticky jump-bar when scrolling.
2026-02-08 20:00:55 -06:00
vicrodh 05414eae37 perf(sidebar): virtualize playlist list to prevent UI freeze
Replace the non-virtualized playlist rendering in the Sidebar with
an inline virtual scroll implementation. Users with 500+ playlists
experienced 20-second UI freezes because all playlists were mounted
as DOM nodes simultaneously.

Now only ~20-30 visible items (plus buffer) are rendered at any time,
using absolute positioning with translateY and binary search for
efficient viewport calculation. Follows the existing
VirtualizedArtistList pattern.

Fixes #56
2026-02-08 19:44:16 -06:00
vicrodh 22055d15e4 fix(appimage): force X11 backend on Wayland to prevent EGL crash
WEBKIT_DISABLE_DMABUF_RENDERER alone is not enough: WebKitGTK's
Wayland platform init calls eglGetPlatformDisplay() in
PlatformDisplayWayland before any renderer decision, and the
AppImage's bundled EGL loader is incompatible with the host
Mesa/driver, causing EGL_BAD_PARAMETER abort.

Force GDK_BACKEND=x11 for AppImage + Wayland so WebKitGTK uses
GLX via XWayland instead of EGL, bypassing the crash entirely.
Users who want native Wayland can override with GDK_BACKEND=wayland.

Non-AppImage builds (Flatpak, RPM, DEB) are unaffected and continue
using native Wayland.

cc
2026-02-08 18:57:22 -06:00
vicrodh 09e0aa579a fix(wayland): rework EGL crash prevention for AppImage + Wayland
WEBKIT_DISABLE_COMPOSITING_MODE=1 was set automatically on Wayland
(both in the VM path and the general Wayland path), which caused a
"ghost app": process alive, tray/MPRIS initialized, but UI never
renders on Fedora + GNOME + Wayland.

Rework the defensive strategy:

- Remove WEBKIT_DISABLE_COMPOSITING_MODE from all automatic paths;
  gate it behind opt-in QBZ_WEBKIT_DISABLE_COMPOSITING=1
- Treat AppImage + Wayland as the primary condition (not VM-only):
  disable DMA-BUF renderer for AppImage on Wayland to prevent
  eglGetPlatformDisplay() EGL_BAD_PARAMETER abort
- Keep LIBGL_ALWAYS_SOFTWARE=1 scoped to VMs and QBZ_SOFTWARE_RENDER
- Keep NVIDIA + Wayland DMA-BUF mitigation unchanged
- Flatpak / RPM / DEB builds unaffected (no APPIMAGE env var)

Fixes: #59

cc
2026-02-08 17:09:27 -06:00
vicrodh 579477f21f ImmersivePlayer + panels: propagate original* and format props
Accept originalBitDepth, originalSamplingRate, and format in
ImmersivePlayer Props and pass them through to all 4 focus panels
(Coverflow, Static, Vinyl, Visualizer) and the inline QualityBadge
in split mode. This enables hardware-based downgrade detection in
all immersive views.
2026-02-08 16:28:52 -06:00
vicrodh 6794f00259 QualityBadge: hardware rate detection + custom tooltip
Poll get_hardware_audio_status every 2s to get the actual hardware
sample rate from /proc/asound. Compare decoded rate (kHz) vs
hardware rate (Hz) for real downgrade detection instead of relying
on originalSamplingRate which comes from the same decoder.

Replace native title attribute with custom tooltip (matching
AudioOutputBadges style) for the degraded state, showing source
rate, output rate, resampling warning, and settings fix hint.
2026-02-08 16:27:21 -06:00
vicrodh 659a5b7718 Add i18n keys for structured quality tooltip
Replace single degradedTooltip string with quality.tooltip.*
keys (source, output, resampled, fix) for the new custom tooltip
in QualityBadge. Updated all 4 locales (en, es, de, fr).
2026-02-08 16:26:31 -06:00
vicrodh 2040ab116c Add degraded quality tooltip explaining resampling and how to fix it 2026-02-08 15:55:53 -06:00
vicrodh 4df3fa52a4 Eradicate all t-as-lambda-param shadowing across codebase
Renamed t -> trk (or tab as contextually appropriate) in all
lambda/callback parameters across 6 files that import t from i18n.
This prevents vite-plugin-svelte CSS extraction failures (PostCSS
"Unknown word" errors) caused by t shadowing the i18n store.

Files: +page.svelte, PlaylistDetailView, FavoritesView,
TrackReplacementModal, Sidebar, SuggestionsPanel
2026-02-08 15:33:59 -06:00
vicrodh 386a66c905 Fix t-as-lambda shadowing i18n store in LocalLibraryTagEditorModal 2026-02-08 15:20:52 -06:00
vicrodh edaf6e442c Rework LogsModal: per-tab upload, URL display with copy, bug report hint
Upload is now per-tab (Terminal/Console separately). Each tab shows a
checkmark after upload. URL displayed inline with copy button. Help text
appears after first upload encouraging users to share both URLs.
2026-02-08 15:13:52 -06:00
vicrodh ff968456c8 Show uploaded log URL next to upload button in LogsModal 2026-02-08 15:10:38 -06:00
vicrodh 18d37765c1 Fix 0x0.st upload 403 by adding User-Agent header 2026-02-08 15:09:08 -06:00
vicrodh cdb58a0e08 Fix Toggle props for Force DMA-BUF (enabled/onchange, not checked/onChange) 2026-02-08 15:05:37 -06:00
vicrodh 363495cb15 Add Developer Mode section to Settings; fix Factory Reset always visible bug
Developer Mode: collapsible section (hidden from jump-nav) with Force DMA-BUF
toggle and View Logs button. LogsModal shows Terminal/Console tabs with
0x0.st upload. Console capture wraps console.log/warn/error/info.

Bugfix: Factory Reset row was outside {#if !storageCollapsed}, making it
always visible even when Storage section was collapsed.
2026-02-08 15:01:09 -06:00
vicrodh 2fda58b823 Add i18n keys for Developer Mode section in all 4 locales 2026-02-08 15:01:02 -06:00
vicrodh 3e4cc6921c Add developer settings backend, log capture system, and --reset-dmabuf CLI flag
Developer settings: SQLite-backed store for force_dmabuf toggle.
Logging: TeeWriter captures env_logger output to ring buffer;
log_startup() captures pre-logger startup messages.
Upload: multipart POST to 0x0.st for log sharing.
CLI: --reset-dmabuf flag resets force_dmabuf and exits for recovery.
2026-02-08 15:00:58 -06:00
vicrodh 03e4232cfc Fix EGL crash in VMs by auto-detecting virtual machines and forcing software rendering
Adds is_virtual_machine() detection (DMI product name, sys vendor,
hypervisor type) that triggers LIBGL_ALWAYS_SOFTWARE=1 to bypass
EGL hardware initialization entirely on VirtualBox, VMware, QEMU,
Xen, Hyper-V, and Parallels.

Also adds QBZ_SOFTWARE_RENDER=1 env var for users on real hardware
who experience transparent outlines or EGL failures (issue #59).
2026-02-08 14:37:15 -06:00
vicrodh 681d3e5dac Change playback defaults: gapless off, context icon off, stream uncached off
Aligns defaults with intended new-install and reset behavior:
- gapless_enabled: false (was true)
- show_context_icon: false (was true)
- stream_first_track: false (was true)
Updates DB migration defaults and frontend reset handler to match.
2026-02-08 14:13:06 -06:00
vicrodh bf370e5373 Move streaming settings to Playback section, add Reset Audio and Factory Reset
- Moves Stream Uncached Tracks, Initial Buffer Size, and Streaming Only
  from Audio section to Playback section
- Adds Reset Audio & Playback Settings button at bottom of Audio section
- Adds Delete All Application Data danger zone at bottom of Storage section
  with confirmation checkbox and secondary dialog
- Adds CSS for reset-btn, danger-zone, checkbox-label, factory-reset-btn
2026-02-08 14:09:35 -06:00
vicrodh 33d69ef3f3 Add i18n keys for reset audio, factory reset; move streaming keys to playback
Moves streamUncached, initialBuffer, streamingOnly and related keys
from settings.audio to settings.playback in all 4 locale files.
Adds reset audio/playback keys to settings.audio section.
Adds factory reset keys to settings.storage section.
2026-02-08 14:06:46 -06:00
vicrodh 2c0dc66b17 Add factory_reset Rust command
Tears down all per-user stores, clears credentials from keyring,
then removes ~/.local/share/qbz/, ~/.cache/qbz/, and ~/.config/qbz/.
Makes teardown_type_alias_state public for reuse.
2026-02-08 14:03:38 -06:00
vicrodh 2d94430c0a Add reset_audio_settings Rust command
Adds reset_all() to both AudioSettingsStore and PlaybackPreferencesStore.
The reset_audio_settings tauri command resets both audio and playback
preferences to their default values in a single call.
2026-02-08 14:00:52 -06:00
vicrodh 1a5b8ae428 fix(startup): scope client mutex locks and yield before download status checks
Applies the same fix pattern from issue #56 (search) to Home view commands.

Backend: scope client.lock().await to just the HTTP call in get_featured_albums,
get_album, get_track, get_artist_basic, and get_discover_index so concurrent
Tauri commands no longer serialize through a single lock.

Frontend: await tick() after setting section data before firing download status
checks, and add .catch() to prevent unhandled rejections from blocking the UI.
2026-02-08 13:38:25 -06:00
vicrodh ed4395392b fix(ui): fix artist-link button reset for proper ticker animation
Add display:block, font:inherit, appearance:none to ensure the button
renders identically to the div variant. Without font:inherit, buttons
use the UA stylesheet font-family which causes text width measurement
mismatches and breaks the overflow detection for the ticker.
2026-02-08 13:28:54 -06:00
vicrodh 74a1b12b3b fix(ui): revert badge to original padding, fix artist link overflow
Restore quality badge to original values (padding 4px 6px, no
line-height override). Fix artist-link button: replace all:unset with
targeted button resets and width:100% so the button is constrained by
its parent and overflow/text-overflow/ticker actually work.
2026-02-08 13:20:26 -06:00
vicrodh f1488489ce fix(ui): restore badge inner padding and add ticker to artist link
Set badge padding to 2px 6px with line-height 1.2 for proper text
breathing room. Add ticker scroll animation to the clickable artist
name on hover so long artist names scroll into view.
2026-02-08 13:12:41 -06:00
vicrodh 6b168f3118 fix(ui): correct quality badge dimensions — restore 90px width, reduce height
Revert min-width to 90px (was correct), set line-height: 1 and
padding: 1px 6px to actually reduce the badge height.
2026-02-08 13:10:56 -06:00
vicrodh f3be2b737f feat(ui): wire artist click through all views using AlbumCard
Pass artistId and onArtistClick to AlbumCard in HomeView, SearchView,
FavoritesView, LabelView, and AlbumDetailView. Add artistId to
AlbumCardData interface. Add onArtistClick prop to LabelView and wire
it from +page.svelte.
2026-02-08 13:10:02 -06:00
vicrodh f5d947e8f1 feat(ui): add clickable artist name to AlbumCard
Add artistId and onArtistClick props. When both are provided, the
artist name renders as a button with hover highlight that navigates
to the artist detail page independently of the card click.
2026-02-08 13:05:10 -06:00
vicrodh 25535eabe4 fix(ui): reduce quality badge height in AlbumCard
Reduce vertical padding from 4px to 2px, add explicit line-height: 1.2,
and revert min-width from 90px to 72px for more compact badges.
2026-02-08 13:04:29 -06:00
vicrodh 6b20355737 fix(tooltip): match data-sys-tooltip on subsequent hovers
After first hover, title is stripped and stored as data-sys-tooltip.
Subsequent hovers need to match [data-sys-tooltip] since [title] no
longer exists on the element.
2026-02-08 12:47:49 -06:00
vicrodh 969f2ba71c feat(ui): add global custom tooltip system replacing native title tooltips
Global event delegation intercepts pointerenter on [title] elements,
strips the native title attribute, and shows a styled tooltip matching
QBZ's visual language. Handles viewport edge clamping with
top/bottom/right/left fallback positioning.
2026-02-08 12:44:07 -06:00
vicrodh 67e4fbb286 fix(favorites): move edit button to top bar inline with back button
Edit/settings button now sits in a top-bar flex row alongside the
back button instead of being absolutely positioned over the header.
2026-02-08 12:34:23 -06:00
vicrodh 35cdad6810 fix(favorites): move back button above header as standalone element
Back button now sits above the header icon/title as its own row,
pushing the header down — matching the pattern used in AlbumDetailView
and other detail views.
2026-02-08 12:29:35 -06:00
vicrodh de04fd6b07 fix(favorites): fix back button positioning and async lint error
Move back button inside header div, positioned absolute top-left
to match edit button height (top-right). Fix await tick() inside
non-async .then() callback.
2026-02-08 12:20:04 -06:00
vicrodh 857c2da99d fix(i18n): add missing actions.playNext key to all locales
The Favorites tracks context menu used actions.playNext which did
not exist, showing the raw key instead of the translated label.
2026-02-08 12:16:26 -06:00
vicrodh 65b269628a feat(nav): add scroll position memory to all detail views
Save and restore scroll position for SearchView, AlbumDetailView,
ArtistDetailView, and PlaylistDetailView. Each view saves its
scroll position on scroll events and restores it via
requestAnimationFrame on mount. Most useful for the Search -> Album
and Search -> Artist back-navigation flow.
2026-02-08 12:14:53 -06:00
vicrodh be80ce1470 feat(nav): add back button to FavoritesView and scroll position memory
FavoritesView now has a conditional back button (shown when onBack
prop is provided). Added scroll position save/restore infrastructure
to navigationStore (saveScrollPosition/getSavedScrollPosition keyed
by ViewType). FavoritesView saves scroll position on scroll and
restores it when returning to the view.
2026-02-08 12:09:56 -06:00
vicrodh 7631ed3e20 fix(ui): remove hover background flicker on download button
Hide the square button background on hover when downloading (the
ring needs no bg). Also hide the download indicator completely when
idle (opacity 0 + pointer-events none) instead of showing a faint
ghost that triggers hover artifacts across all track row buttons.
2026-02-08 11:18:50 -06:00
vicrodh b3573af497 fix(ui): replace flickering Loader spinner with SVG progress ring
The Lucide Loader icon has multiple line segments that create a
stroboscopic flicker when rotated. Replace with a clean SVG circle
progress ring: shows actual download progress via stroke-dashoffset
with smooth 300ms transitions, and a simple indeterminate rotation
when queued. No more flickering.
2026-02-08 11:16:34 -06:00
vicrodh c5eb685f11 fix(home): wire download button action in Continue Listening tracks
The DownloadButton in TrackRow receives onDownload from direct props,
not from menuActions. Continue Listening was only passing onDownload
inside menuActions (for context menu) but not as a direct prop, so
clicking the download icon did nothing. Pass onDownload and
onRemoveDownload as direct TrackRow props.
2026-02-08 10:59:49 -06:00
vicrodh e29256170c fix(search): prevent UI freeze during search (#56)
Frontend: yield to UI loop with tick() after setting search results
before running background download status checks (fire-and-forget
instead of blocking await). Backend: drop client mutex lock
immediately after HTTP request completes so parsing and blacklist
filtering don't hold the lock and block other commands.
2026-02-08 10:41:41 -06:00
vicrodh d2531ea700 refactor(ui): move gapless toggle from player bar to settings
Remove gapless button from NowPlayingBar and place it in
SettingsView under Playback section. The toggle is now sensitive to
blocking settings: auto-disables when ALSA Direct backend or
streaming-only mode is activated. Gapless itself never blocks other
settings. Added inline experimental label and descriptive tooltips
with proper i18n for all 4 locales.
2026-02-08 10:18:52 -06:00
vicrodh 71bfb8dc17 feat(ui): add gapless playback toggle button to player bar
Add a toggleable gapless button next to the repeat button in the
NowPlayingBar. The setting persists in audio_settings.db and controls
whether the audio thread pre-queues the next track for gapless
transitions. Includes i18n keys for all 4 locales.
2026-02-08 09:45:13 -06:00
vicrodh a21f7c7a93 feat(audio): implement gapless playback for cached tracks
Backend: Add PlayNext command to audio thread that appends decoded
sources to the existing Rodio Sink for seamless transitions. The audio
thread detects when ~5s remain and signals gapless_ready via SharedState.
Position-based transition detection updates track metadata when the
queued source begins playing.

Frontend: playerStore detects gapless_ready flag, invokes
play_next_gapless (cache-only lookup), and handles transitions by
advancing the queue without stop/play cycle. playbackService gains
gaplessTransition option to skip stop_playback when backend already
transitioned.

Gapless is only eligible when: same sample rate/channels, cached data,
Rodio backend (not ALSA Direct), not streaming, not a user skip.
Falls back to normal stop-play flow when ineligible.
2026-02-08 09:05:39 -06:00
vicrodh eaacc9e3a7 feat(audio): add real-time EBU R128 volume normalization (Phase 2)
Replace static ReplayGain-only normalization with real-time loudness
analysis that works with any audio source including Qobuz streams.

New components:
- DynamicAmplify: Source wrapper that reads gain from shared atomic
  with 50ms linear ramp to prevent clicks on gain changes
- AnalyzerTap: Source wrapper that batches samples and sends them
  to the analyzer thread via bounded channel (never blocks audio)
- LoudnessAnalyzer: Background thread computing EBU R128 integrated
  LUFS, first measurement at ~10s, refinement every ~5s
- LoudnessCache: SQLite persistence for computed LUFS values so
  replayed tracks get instant gain application

Pipeline when normalization ON:
  Decode -> Diagnostic -> AnalyzerTap -> DynamicAmplify -> Visualizer

Pipeline when normalization OFF (bit-perfect, unchanged):
  Decode -> Diagnostic -> Visualizer

Re-enable normalization toggle in NowPlayingBar UI.
2026-02-08 08:24:51 -06:00
vicrodh ac4cd8f4eb chore(ui): hide normalization icon until Phase 2
ReplayGain-only normalization is not useful for Qobuz streaming
(streams lack RG tags). Icon will be re-enabled once EBU R128
real-time analysis is implemented.
2026-02-07 23:26:47 -06:00
vicrodh 00d0ce7835 feat(ui): add normalization toggle to player bar
Adds 3-state normalization icon between fullscreen and volume controls.
Uses mask-image SVGs for theme-compatible coloring. Reduces song card
min-width by 72px to accommodate the new control.
2026-02-07 22:55:18 -06:00
vicrodh 717eb6fed2 feat(audio): expose normalization gain in playback events
Add normalization_gain field to SharedState and PlaybackEvent so the
frontend can display whether normalization is actively applied to the
current track. The gain factor is stored as AtomicU32 (f32 bits) and
emitted with each playback:state event.

cc
2026-02-07 22:47:14 -06:00
vicrodh 6edd2a3cda feat(audio): add volume normalization (Phase 1 — ReplayGain metadata)
Add ReplayGain-based volume normalization to the audio pipeline.
When enabled, extracts ReplayGain track gain/peak tags from audio
file metadata (Vorbis comments, ID3v2 TXXX) and applies a gain
factor via rodio's Amplify wrapper.

Pipeline order: Diagnostic (raw) -> Amplify (normalization) -> Visualizer
When normalization is OFF (default), no Amplify wrapper is inserted
and the pipeline remains 100% bit-perfect.

- New module: audio/loudness.rs (extraction + gain calculation)
- New settings: normalization_enabled, normalization_target_lufs
- New Tauri commands: set_audio_normalization_enabled/target
- Supports -14 LUFS (default), -18 LUFS, -23 LUFS targets
- Clipping prevention using peak metadata
- Gain preserved across resume/seek operations

cc
2026-02-07 21:32:32 -06:00
vicrodh a237be3a83 Differentiate DAC Passthrough tooltip from ALSA Direct bit-perfect
DAC badge tooltip now says "Active" instead of "Bit-perfect" since
PipeWire passthrough is best-effort, not guaranteed bit-perfect like
ALSA Direct hw:X,Y. Added a brief note in Settings when DAC Passthrough
is enabled clarifying the distinction. Also i18n'd the hardcoded
dacPassthroughTooltipOverride string in SettingsView.
2026-02-07 20:37:31 -06:00
vicrodh be56ecff90 i18n: replace hardcoded strings in AudioOutputBadges tooltip
Moved all hardcoded English/Spanish tooltip text to i18n keys
under audioBadges section in all 4 locale files.
Removed dead dacTooltip derived property (unused in template).
2026-02-07 18:11:49 -06:00
vicrodh af624d4642 feat(audio): add internal bit-depth diagnostic capture
Lock-free OR-mask analysis of f32 samples converted to i32,
bypassing PipeWire processing entirely. DiagnosticSource wraps
the audio source at decode level for both rodio and ALSA Direct
paths. Tauri commands: start_bitdepth_capture / stop_bitdepth_capture.

cc
2026-02-07 15:58:26 -06:00
vicrodh 3253e05263 Merge branch 'fixes/1.1.10/debug' into pre-release 2026-02-07 13:17:43 -06:00
vicrodh fdc8a96a7e chore: remove DEBUG-43 diagnostic logging
cc
2026-02-07 13:09:46 -06:00
vicrodh bcbe31d1f4 chore(vendor): fix unused import and mut warnings in rodio
cc
2026-02-07 12:55:23 -06:00
vicrodh 3990d7bdcf fix(updates): gate launch modals on session readiness
decideLaunchModals() was racing activate_user_session() because
setLoggedIn() triggers the $effect before the backend store opens.
Preferences defaulted to showWhatsNewOnLaunch=true, ignoring the
user's saved setting.

Add sessionReady flag set after activate_user_session completes.
The $effect now waits for session + home view before running.
Also reset all update state on logout for clean re-login cycle.

cc
2026-02-07 12:48:34 -06:00
vicrodh 220ac36b33 fix(css): remove remaining store accesses from $derived() blocks
+layout.svelte: $isLoading inside $derived() -> isReady() function
HomeView.svelte: $t() inside $derived.by() -> getGreetingText() function

These caused vite-plugin-svelte CSS extraction to fail, cascading
PostCSS errors to unrelated components during HMR.
2026-02-07 12:41:26 -06:00
vicrodh 1d284ac82a fix(css): remove remaining store accesses from $derived() blocks
+layout.svelte: $isLoading inside $derived() -> isReady() function
HomeView.svelte: $t() inside $derived.by() -> getGreetingText() function

These caused vite-plugin-svelte CSS extraction to fail, cascading
PostCSS errors to unrelated components during HMR.
2026-02-07 12:23:01 -06:00
vicrodh ecd006d176 fix(css): remove $t() from $derived() to fix PostCSS extraction
ImmersiveHeader splitTabs used $t() inside $derived(), causing
vite-plugin-svelte CSS extraction to receive raw script content
instead of CSS — cascading PostCSS errors to unrelated components.

ShortcutInput had the same pattern for displayShortcut.

Both now use the labelKey/function pattern instead.
2026-02-07 12:13:24 -06:00
vicrodh 28742fa19e fix(updates): ensure preferences are loaded from backend after login
initUpdatesStore() set its initialized flag before the async preference
fetch completed. If get_update_preferences failed (backend store not
yet ready), preferences stayed at defaults (both true) and were never
re-fetched. This caused:
- "Show What's New" toggle appearing enabled despite being disabled
- "Check for updates on launch" toggle not persisting
- Changelog modal reappearing on every restart

Changes:
- Split initialized flag into versionLoaded/prefsLoaded so preference
  loading retries until the backend store is ready
- Call refreshUpdatePreferences() in decideLaunchModals() to always
  get fresh data before making modal decisions
- Call refreshUpdatePreferences() in handleLoginSuccess() after
  session activation so SettingsView reflects saved values

cc
2026-02-07 11:58:32 -06:00
vicrodh 865e25e9b1 fix(listenbrainz): restore token and enabled state during session init
init_at() loaded credentials from the user-scoped SQLite cache but
never called restore_token() on the in-memory client. The client
remained empty, so get_status() always returned disconnected after
app restart. Also restore the enabled flag from persisted settings.

Fixes #54

cc
2026-02-07 11:56:34 -06:00
vicrodh e48bbb99df feat: add pkgrel input to AUR workflow
On release events pkgrel defaults to 1. On manual dispatch it can be
set to any value (default 1), allowing same-version rebuilds like
1.1.9-2 without editing the workflow.
2026-02-07 03:34:29 -06:00
vicrodh 1666672f0c fix: version unavailable in changelog button
initUpdatesStore used Promise.all for get_current_version and
get_update_preferences. Since preferences need the SQLite store
(only available after login), the failure killed both promises,
leaving currentVersion as empty string.

Split into independent calls so the version (a compile-time constant
that never needs a session) is always available regardless of
preference store state.
2026-02-07 03:33:52 -06:00
vicrodh b3607d1154 update AUR PKGBUILD version to 1.1.9 2026-02-07 03:01:56 -06:00
vicrodh befef6b1e3 cleanup: fix compiler warnings in playback commands
Remove unused mut on preferred_quality (limit_quality_for_device is
disabled). Add #[allow(dead_code)] to the preserved function.
2026-02-07 03:01:52 -06:00
vicrodh bb496ffaac fix: use bundled SQLite for snap builds
Remove LIBSQLITE3_SYS_USE_PKG_CONFIG and related env vars that forced
linking against system SQLite 3.37.2 on core22. The rusqlite bundled
feature compiles SQLite 3.38+ from source, which provides the
sqlite3_error_offset symbol needed by rusqlite 0.31.
2026-02-07 03:01:47 -06:00
vicrodh 5d617365c2 fix: show changelog button provides loading state and error feedback
The button previously failed silently when version was unavailable or
the GitHub API request failed. Now shows loading state while fetching
and a toast notification on failure.
2026-02-07 02:53:43 -06:00
vicrodh 29eb8b0d96 cleanup: remove DEBUG-43 console logging
Remove all [DEBUG-43] console.warn/error entries added during blank
album investigation. Keep essential error logging without the prefix.
2026-02-07 02:53:38 -06:00
vicrodh 12faa5837c fix: rename t variable in $derived() blocks to prevent PostCSS errors
The Svelte 5 CSS extraction breaks when t is used as a callback
parameter inside $derived() or $derived.by() blocks. Renamed to
track, item, or lt in PlaylistDetailView, FavoritesView,
PlaylistSuggestions, and PlaylistTagFilter.
2026-02-07 02:53:33 -06:00
vicrodh 5e3e3c252a update snapcraft and flatpak metadata for 1.1.9 2026-02-07 01:58:03 -06:00
vicrodh 3f18378c13 bump version to 1.1.9 2026-02-07 01:52:38 -06:00
vicrodh 09774c151f add DE and FR translations for playlist import modal 2026-02-06 23:39:04 -06:00
vicrodh feb007945b playlist import: add name customization and folder selection
Split import into two-step flow: Fetch (preview) then Import.
After fetching, user can edit the playlist name and optionally
assign it to a folder before importing. Backend already supported
name_override — this just exposes it in the UI. Also i18n all
new strings and existing hardcoded modal text.
2026-02-06 23:38:08 -06:00
vicrodh 3776925a5f auto-resume playback after audio settings change
When the user changes any audio setting (output device, exclusive mode,
DAC passthrough, backend type, ALSA plugin), the stream is reinitialized.
Previously playback would die and the user had to manually skip
forward/backward to recover. Now playback automatically resumes at the
same position after reinit, since the backend already preserves position
and audio data across ReinitDevice.
2026-02-06 22:20:23 -06:00