Commit Graph
100 Commits
Author SHA1 Message Date
vicrodh ee3a50af89 feat(qbz-player): implement Phase 3b - player module
Extract player/mod.rs, playback_engine.rs, and streaming_source.rs to
qbz-player crate. Also adds settings.rs and visualizer module to
qbz-audio (Tauri-specific code removed, core functionality retained).

Changes:
- Add settings.rs to qbz-audio with AudioSettings, AudioSettingsStore
- Add visualizer/ to qbz-audio with RingBuffer, TappedSource, VisualizerTap
- Add player module files to qbz-player
- Update all imports from crate::audio to qbz_audio
- Add serde_json dependency to qbz-audio

Full workspace compiles with cargo check.
2026-02-14 14:18:06 -06:00
vicrodh 0be0f6dcb0 feat(qbz-qobuz): implement Phase 4 - Qobuz API client
Extracted Qobuz API client from qbz-nix to qbz-qobuz crate.

Modules:
- client.rs: Main QobuzClient with all API methods
- auth.rs: Request signing and authentication
- bundle.rs: Bundle token extraction
- endpoints.rs: API endpoint definitions
- error.rs: API error types
- performers.rs: Performers parsing

Updated qbz-models with additional types:
- Discover types (DiscoverResponse, DiscoverAlbum, etc.)
- Artist page types (PageArtistResponse, ReleasesGridResponse, etc.)
- Playlist tags (PlaylistTag, PlaylistTagsResponse)

Dependencies:
- reqwest for HTTP
- md-5 for request signing
- futures-util for concurrent requests
- anyhow for error handling
2026-02-14 13:25:18 -06:00
vicrodh ad7333d426 feat(qbz-player): implement Phase 3a - queue management
Extracted QueueManager from qbz-nix to qbz-player crate.

Features:
- Queue manipulation (add, remove, reorder, clear)
- Current track tracking
- Shuffle mode with Fisher-Yates algorithm
- Repeat modes (off, all, one)
- Play history for going back
- Full test coverage preserved

Uses types from qbz-models:
- QueueTrack
- QueueState
- RepeatMode

Phase 3b (player module extraction) deferred - requires:
- Trait abstraction for API client
- Trait abstraction for audio backend
- Config system refactoring
2026-02-14 13:13:25 -06:00
vicrodh 3c8fc35fcb feat(qbz-audio): implement Phase 2 - audio backend system
CRITICAL: Copied audio modules from qbz-nix WITHOUT modifying logic.
This code handles bit-perfect playback and is IMMUTABLE.

Copied modules:
- backend.rs: Backend trait and manager
- pipewire_backend.rs: PipeWire/PulseAudio backend
- alsa_backend.rs: ALSA backend with device enumeration
- pulse_backend.rs: PulseAudio fallback
- alsa_direct.rs: Direct ALSA hardware access (hw:X,Y)
- diagnostic.rs: Audio diagnostic tools
- loudness.rs: ReplayGain extraction
- loudness_analyzer.rs: EBU R128 analysis
- loudness_cache.rs: SQLite cache for loudness data
- dynamic_amplify.rs: Dynamic volume amplification
- analyzer_tap.rs: Audio analyzer tap

Dependencies:
- Uses vendored rodio from src-tauri/vendor/rodio
- symphonia-all feature for f32 pipeline
- ebur128 for loudness analysis
- rusqlite for caching

Also updated qbz-models to remove incomplete audio types
(AudioBackendType, AudioDevice) - these stay in qbz-audio.
2026-02-14 13:10:41 -06:00
vicrodh deafa15205 feat(qbz-models): implement Phase 1 - shared types, events, and traits
Tier 0 crate with no dependencies on other qbz-* crates:

- types.rs: Core API types (Track, Album, Artist, Playlist, Quality, etc.)
- playback.rs: Playback types (QueueTrack, RepeatMode, PlaybackState, etc.)
- events.rs: CoreEvent enum for frontend communication
- traits.rs: FrontendAdapter trait enabling multiple frontends
- error.rs: QbzError enum with error codes and recovery flags

All types extracted from qbz-nix/src-tauri/src/api/models.rs and
queue/mod.rs. Ready for use by Tier 1 crates (qbz-audio, qbz-qobuz).
2026-02-14 12:54:52 -06:00
vicrodh 0911cf75df refactor(backend): Phase 0 - Initialize multi-crate workspace
Create the foundational workspace structure for the multi-crate
architecture refactor. This phase establishes placeholder crates
that will be populated in subsequent phases.

Crates created:
- qbz-models: Shared types, events, and traits (Tier 0)
- qbz-audio: Audio backend system (Tier 1)
- qbz-player: Playback engine and queue (Tier 1)
- qbz-qobuz: Qobuz API client (Tier 1)
- qbz-core: Orchestrator (Tier 3)

All crates compile successfully with placeholder implementations.
The workspace uses shared dependencies via workspace.dependencies.

cc
2026-02-14 12:48:52 -06:00
vicrodh 1c9bb8dfdb Merge branch 'pr-79' into pre-release 2026-02-14 12:37:54 -06:00
vicrodh 6c509225a4 fix(playlist): ensure progressive loading completes for large playlists
- Track failed batches and retry them once after initial load
- Detect playlist change and abort background loading if user navigates
- Log final status (COMPLETE vs INCOMPLETE with missing count)
- Prevents silent failures that left playlists partially loaded
2026-02-13 18:54:19 -06:00
vicrodh 752c236c11 fix(queue): correct index calculation when no track is playing
- When current_index is None, upcoming starts at index 0, not 1
- Add syncQueueState() after removing track to refresh UI
2026-02-13 14:23:00 -06:00
vicrodh 8dcad203c7 fix(queue): correct remove-from-queue index and add history context menu
- Add remove_upcoming_track command that correctly maps upcoming index
  to actual queue index (handles shuffle mode properly)
- Update frontend to use remove_upcoming_track instead of remove_from_queue
- Add working context menu for history tracks (Add to Playlist, Track Info)
- Remove unused .track-menu CSS class
2026-02-13 13:29:30 -06:00
vicrodh 3a8ef43227 feat(audio): add device refresh button and per-DAC sample rate limit
- Add refresh button next to device dropdown to re-enumerate devices
  without app restart (fixes #69)
- Re-enable quality limiting with per-device sample rate storage
- User can manually set max sample rate per DAC (44.1k to 384k)
- Quality limiting is opt-in (disabled by default)
- Streaming Quality dropdown always takes priority (fixes #72)

Backend: HashMap<device_id, max_rate> stored as JSON in SQLite
Frontend: Conditional UI shows rate dropdown when limit is enabled
2026-02-13 12:56:50 -06:00
vicrodh 333e6f224a feat(ui): show default device name under System Default option
Adds a help text showing which device is the current system default.
This helps users understand what "System Default" will actually use.

Backend:
- New command `get_default_device_name` that uses pactl for PipeWire/Pulse
  and CPAL for ALSA to get the current default device

Frontend:
- Shows "(System Default is: <device name>)" under the output device setting
- Styled as secondary description text (italic, muted)

Translations added for en, es, de, fr.
2026-02-13 12:35:56 -06:00
vicrodh d3fec83aa2 refactor(alsa): use /proc/asound as primary enumeration source
Previously, CPAL was the primary source and /proc/asound supplemented
missing devices. This caused inconsistent device lists because CPAL
cannot enumerate devices that are in exclusive mode.

New architecture:
- /proc/asound = PRIMARY source (always complete, stable IDs)
- CPAL = OPTIONAL enrichment (sample rates, may be unavailable)

Benefits:
- Device list is now consistent regardless of playback state
- No more devices appearing/disappearing when entering settings
- Stable device IDs using card NAMES instead of numbers (fixes #69)

Tradeoff:
- Sample rates may be unavailable when device is in exclusive mode
- This is acceptable: consistency > sample rate display
2026-02-13 11:56:08 -06:00
vicrodh 543f49ef82 fix(alsa): use consistent description format for proc devices
When adding devices from /proc/asound (because CPAL couldn't enumerate
them during exclusive mode), use the same description format as CPAL:
"{card_name}, {device_id}" instead of the longer format with
"Direct Hardware - Bit-perfect" suffix.

This ensures the device dropdown shows consistent names regardless
of whether the device was found by CPAL or added from /proc.
2026-02-13 11:50:01 -06:00
vicrodh abb2a30e6e fix(ui): prevent duplicate System Default in device list
Skip the 'default' device from backend enumeration since we already
add an explicit "System Default" entry. This prevents showing
"System Default" twice in the dropdown.
2026-02-13 11:49:56 -06:00
vicrodh 4af65fede5 fix(alsa): remove hw:X,Y enumeration for device ID stability
ALSA hw:X,Y device IDs use card NUMBERS which are unstable and change
between boots depending on USB enumeration order. This caused saved
devices to become unrecognized after reboot (issue #69).

Now only enumerate front:CARD=name,DEV=Y devices which use the card
NAME instead of number. Both formats are functionally equivalent for
bit-perfect playback, but front: format is stable across reboots.

Fixes #69
2026-02-13 11:38:39 -06:00
vicrodh c07325d26f fix(ui): use device.id as each block key to prevent duplicates
Changed the keyed each block in DeviceDropdown.svelte from device.value
to device.id. This fixes the each_key_duplicate error when multiple
devices have the same display name (e.g., "System Default").
2026-02-13 11:38:33 -06:00
vicrodh 0e6b1fc686 refactor(alsa): replace aplay with /proc/asound reads
Remove dependency on alsa-utils package by reading device information
directly from /proc/asound filesystem:

- /proc/asound/cards for card list with names
- /proc/asound/cardX/ for detailed card info
- /proc/asound/cardX/pcmXp/ for PCM device info

This addresses Flathub reviewer feedback about not using CLI tools
when API alternatives exist. The /proc/asound interface provides
all the information we need for device enumeration and descriptions.

Changes:
- alsa_backend.rs: New enumerate_with_proc_descriptions() using /proc
- audio_backends.rs: Remove aplay calls from DAC capabilities query
- check_alsa_utils_installed() now always returns true (no longer needed)
2026-02-13 10:30:42 -06:00
vicrodh d3a4e4a688 Bump version to 1.1.14 for release
Changes since 1.1.13:
- Fix MPRIS dbus name to use appid (Flathub compliance)
- Fix SQLite WAL mode for all connections (UI lag fix)
- Add volume +/- step buttons in player bar
- Add playlist search in sidebar with folder auto-expand
- New playlist collage layout (3+1 grid)
2026-02-13 09:07:02 -06:00
vicrodh 78aa6694c7 fix(flatpak): use appid as MPRIS dbus name, remove own-name permission
Change dbus_name from "qbz" to "com.blitzfc.qbz" so the MPRIS name
matches the appid pattern. This eliminates the need for the explicit
--own-name permission as Flatpak allows apps to own names under their
appid namespace automatically.

cc
2026-02-13 07:54:36 -06:00
vicrodh e977ca8b34 feat(ui): new playlist collage layout with 3+1 grid
- Change 4-cover layout from 2x2 to 3 small left + 1 large right
- Always use track artwork collage instead of low-res API image
- Upscale images to 600px for better quality

cc
2026-02-13 07:54:36 -06:00
vicrodh e8929cb2a4 fix(ui): volume buttons and playlist search refinements
- Remove border from volume step buttons (no circles in player)
- Remove separator between volume and queue button
- Make playlist search input taller using negative margins
- Auto-expand folders with matching results during search
- Hide folders without matches during search

cc
2026-02-13 07:54:36 -06:00
vicrodh ebae1aa83a feat(ui): add volume step buttons and playlist search
- Add +/- buttons to volume control for 5% increments
- Reduce SongCard min-width from 508px to 436px
- Add playlist search in sidebar header with expandable input
- Add translations for new features in all 4 locales

cc
2026-02-13 07:54:36 -06:00
Victor RH 5adfbbfffe Merge pull request #76 from boxdot/main
Merged to pre-release for testing
2026-02-13 07:51:36 -06:00
vicrodh 45a420cf7e fix: update snapcraft source-tag to v1.1.13 2026-02-12 16:04:27 -06:00
vicrodh 2040ca00c4 Fix AUR workflow curl check for GitHub redirects
GitHub returns 302 redirects for release assets, not 200.
Use curl -L to follow redirects and check final status code.
2026-02-12 15:33:26 -06:00
vicrodh 008dfbcbb0 Bump version to 1.1.13 for Flathub submission 2026-02-12 15:12:07 -06:00
vicrodh 1135aab857 Add Flatpak metadata files for Flathub submission
- Desktop entry file
- AppStream metainfo with screenshots
- Screenshot images for Flathub listing

These files are required by Flathub reviewers to be in upstream.
2026-02-12 15:10:59 -06:00
vicrodh 7926e3eaa5 Fix AUR workflow to trigger after release-linux completes
- Change trigger from release:published to workflow_run on release-linux
- Only run if the triggering workflow succeeded
- Increase timeout to 30 minutes for aarch64 build to complete
- Extract version from workflow's head branch (the tag)
2026-02-12 14:37:47 -06:00
vicrodh f70000bbcc Bump version to 1.1.12 2026-02-12 14:32:52 -06:00
vicrodh 3f8914e8cb Move graphics settings to Composition section
- Add Hardware Acceleration toggle to Composition section
- Move Force DMA-BUF toggle from Developer Mode to Composition
- Add i18n keys for new composition toggles (en, es, de, fr)

These settings logically belong together since they both control
GPU rendering behavior. Hardware Acceleration is the broad switch
that enables GPU; Force DMA-BUF controls the specific buffer-sharing
mechanism with the compositor.
2026-02-12 14:16:52 -06:00
vicrodh 901b0edab8 Fix system title bar sync only propagating true values
The localStorage->backend sync only ran when useSystemTitleBar was true,
so toggling it OFF never persisted to the database. On restart, the
backend read the old 'true' value and created the window with system
decorations, while the UI showed 'false' from localStorage.

Now syncs the value unconditionally on init.
2026-02-12 13:56:33 -06:00
vicrodh 348e615d41 Fix infinite loop in AudioOutputBadges causing log spam
The $effect watching outputStatus.is_playing would call loadStatus(),
which updates outputStatus, triggering the effect again. This caused
~30 calls per second when hovering over the audio badges.

Removed the problematic $effect - mouseenter handler and 1s polling
for hardware status are sufficient.
2026-02-12 13:45:58 -06:00
vicrodh 14e657e8af Optimize console log capture in production
Only capture log/info levels in dev builds or when verbose mode is
explicitly enabled in Developer Mode. Always capture error/warn for
bug tracking. Reduces GC/CPU overhead in production builds.

Adds toggle in Settings > Developer Mode to enable verbose capture
when debugging production issues.
2026-02-12 13:34:02 -06:00
vicrodh 51a24de9cb Fix async listener leak on fast unmount/HMR
Apply disposed flag pattern to prevent listener leaks when component
unmounts before async listen() calls complete.

+page.svelte:
- Add disposed flag for tray/media control listeners
- Check disposed after each await listen(), cleanup immediately if true
- Set disposed=true at start of cleanup function

offlineCacheState.ts:
- Add listenersDisposed flag
- Check flag after each listener registration
- Cleanup any registered listeners if disposed during async sequence
- Set flag in stopOfflineCacheEventListeners()

This prevents memory leaks during HMR and fast navigation.
2026-02-12 13:27:00 -06:00
vicrodh 9c5245f2ce Add context menu for queue tracks
QueuePanel:
- Add vertical ... icon to the right of track duration
- Context menu with 3 options:
  - Remove from Queue (calls backend remove_from_queue)
  - Add to Playlist (reuses existing playlist modal)
  - Show Track Info (reuses existing track info modal)
- Menu shows on button click, closes on click outside

+page.svelte:
- Add handlers: handleRemoveFromQueue, handleQueueTrackAddToPlaylist,
  handleQueueTrackInfo
- Wire up new QueuePanel props

i18n: Add toast messages for remove from queue (all 4 locales)
2026-02-12 13:20:08 -06:00
vicrodh 1959000fd0 Expose graphics fallback status to UI with warning
Backend:
- Add static atomics to track graphics startup state
- Add set_startup_graphics_state() called from main.rs
- Add get_graphics_startup_status command for frontend

Frontend (SettingsView):
- Query graphics startup status on mount
- Show prominent red warning in Composition section when
  using fallback graphics settings
- Display recovery command (qbz --reset-graphics)

i18n: Add fallbackWarning/fallbackDesc to all 4 locales

This helps users understand when graphics settings failed to
load and may be causing performance degradation.
2026-02-12 13:11:47 -06:00
vicrodh 0d5c50a23a Add visible logging for graphics settings fallback
When graphics settings fail to load from DB:
- Logs warning to stderr (visible in terminal)
- Logs to startup log with recovery instructions
- Tracks fallback state for potential UI exposure
- Adds summary log of graphics config at startup

Helps users diagnose performance degradation caused by
silent graphics settings failures.
2026-02-12 13:05:41 -06:00
vicrodh 36632548fe Add idempotency guard to offline cache listeners
Prevents duplicate event listeners if startOfflineCacheEventListeners
is called multiple times (e.g., during HMR or accidental remount).
2026-02-12 13:02:41 -06:00
vicrodh 20ab229cfe Increase session save interval (30s -> 60s)
Reduces IPC overhead for large queues. Full queue serialization
every 30s was excessive; 60s provides same crash recovery with
half the overhead.
2026-02-12 13:02:25 -06:00
vicrodh dae8d509e5 Reduce playback state emission frequency (250ms -> 500ms)
Reduces UI reconciliation work during playback from 4Hz to 2Hz.
Seekbar updates remain smooth while cutting CPU overhead in half.
2026-02-12 13:02:16 -06:00
vicrodh 42034623be Disable MiniPlayer feature (incomplete)
- Hide MiniPlayer button in NowPlayingBar when not provided
- Comment out miniplayer IPC emissions in player state subscriber
- Removes unnecessary IPC overhead during playback

MiniPlayer needs significant work before re-enabling.
2026-02-12 12:59:16 -06:00
vicrodh 2514c53e7b Remove Vinyl panel from Immersive focus mode tabs
Removes vinyl from the focus mode tab options while keeping the
VinylPanel component intact for potential future use. Users can
now choose from coverflow, static, visualizer, lyrics, and queue.
2026-02-12 12:44:45 -06:00
vicrodh e03c823bfd feat: add --reset-graphics CLI flag for full composition reset
Resets all graphics/composition settings to defaults:
- force_x11: false
- gdk_scale: auto
- gdk_dpi_scale: auto
- force_dmabuf: false

Use when QBZ crashes on startup due to bad graphics config.

cc
2026-02-12 11:40:53 -06:00
vicrodh 9f574fce4a docs: add graphics troubleshooting section to README
Documents composition settings, environment variables, and common
scenarios for GPU/rendering issues (NVIDIA Wayland, Intel Arc, VMs).

cc
2026-02-12 11:23:59 -06:00
vicrodh 5d48e15e76 merge: integrate bugfixes/pre-release-1.1.12
Includes:
- fix(graphics): revert to v1.1.9 rendering defaults
- feat(graphics): GPU rendering summary in startup logs
- feat(settings): collapsible Composition section
- fix: disable gapless when DAC Passthrough active
- fix: prevent multiple instances
- feat: playlist offline option

cc
2026-02-12 09:47:53 -06:00
vicrodh a69009d23d feat: add "Make available offline" option for playlists
Cherry-picked from main (126a5714)

cc
2026-02-12 09:46:13 -06:00
vicrodh 700183b0bb fix: prevent multiple instances when reopening from desktop icon
Cherry-picked from main (c163435b)

cc
2026-02-12 09:46:03 -06:00
vicrodh 968a9d7116 fix: disable gapless when DAC Passthrough is active
Cherry-picked from main (ab3dac32)

cc
2026-02-12 09:45:55 -06:00
vicrodh 0170519a6b feat(updates): add snap welcome modal with plug setup commands
cx
2026-02-11 09:07:38 -06:00
vicrodh aed7e43c0f feat(settings): add collapsible Composition section in Appearance
Replace the misleading Hardware Acceleration toggle (which was a no-op
since v1.1.12 reverted to env-var-only control) and the standalone
Force X11 toggle with a collapsible "Composition" subsection that:

- Groups all GPU/rendering settings in one place
- Shows help text explaining what the section controls
- Includes a crash recovery warning with terminal command
- Keeps Force X11 toggle (functional, writes to DB)
- Adds GDK_SCALE and GDK_DPI_SCALE inputs (shown when Force X11 on)
- Lists all env var overrides with descriptions
- Translated in all 4 locales (en, es, de, fr)
2026-02-10 20:32:40 -06:00
vicrodh f0b7b2efb9 feat(graphics): add GPU rendering summary to startup logs
Shows effective rendering state after all env var decisions:
- FULL: compositing GPU + DMA-BUF GPU
- partial: which subsystems are on CPU vs GPU
- OFF: software rendering via llvmpipe

cc
2026-02-10 20:08:09 -06:00
vicrodh 31019d9b6a fix(graphics): revert to v1.1.9 rendering defaults, fix UI lag
Reverts the aggressive GPU disabling introduced in v1.1.10/11 that
caused severe UI lag for all users. The previous approach disabled
WEBKIT_DISABLE_DMABUF_RENDERER globally (not just NVIDIA) because
hardware_accel defaulted to false.

New behavior (matches v1.1.9 targeted mitigations):
- X11 non-NVIDIA: nothing disabled (full GPU acceleration)
- X11 + NVIDIA: only DMA-BUF disabled
- Wayland: compositing + DMA-BUF disabled (prevents EGL crashes)
- Wayland + force_x11: runs as X11 (XWayland)
- VM: software rendering via llvmpipe

Improvement over v1.1.9: DMA-BUF now disabled for ALL Wayland GPUs
(not just NVIDIA), which prevents the Intel Arc EGL crash from #59.

All settings bypassable via env vars:
- QBZ_HARDWARE_ACCEL=0 disables all GPU rendering (nuclear opt-out)
- QBZ_HARDWARE_ACCEL=1 bypasses all safety (full GPU everywhere)
- QBZ_FORCE_X11=1 forces XWayland backend
- QBZ_FORCE_DMABUF=1 / QBZ_DISABLE_DMABUF=1 fine-grained DMA-BUF
- QBZ_SOFTWARE_RENDER=1 forces Mesa llvmpipe

Also adds GDK_SCALE and GDK_DPI_SCALE DB persistence for XWayland
scaling (workaround from #67).

cc
2026-02-10 19:27:39 -06:00
vicrodh 63abf1f90c fix: graphics settings store fails on existing databases
The CREATE TABLE included force_x11 column but INSERT OR IGNORE
also referenced it, so on existing DBs (without the column) the
entire execute_batch failed before reaching the ALTER TABLE
migration. Keep original schema in CREATE/INSERT, let ALTER add
the new column.
2026-02-10 08:37:35 -06:00
vicrodh d6e9375333 chore: bump version to 1.1.11 2026-02-10 08:00:42 -06:00
vicrodh 0f31dcd310 feat: add persistent Force X11 setting for Wayland crash workaround
Add a toggle in Settings > Appearance to force X11 (XWayland) backend
on Wayland sessions. Persisted in graphics_settings.db, env var
QBZ_FORCE_X11=1|0 overrides for crash recovery. Also fixes the
display server log to report effective GDK backend instead of
session type.
2026-02-10 07:18:15 -06:00
vicrodh d3d719c609 fix: log effective display server after GDK backend selection
The "Display server" log message was checking WAYLAND_DISPLAY env var
before GDK_BACKEND selection, so GDK_BACKEND=x11 still showed
"Display server: Wayland". Now logs AFTER selection and reports
the effective backend (including "X11 (XWayland)" when forcing x11
on a Wayland session).
2026-02-10 07:10:06 -06:00
vicrodh cff32e5da7 chore(flatpak): add upstream screenshots for AppStream
cx
2026-02-10 07:08:41 -06:00
vicrodh 07f9f64a08 fix: persist volume and tray settings across restarts
Volume was saved to per-user localStorage but loaded from unscoped key
before login set the userId. Added resyncPersistedVolume() called after
login to re-read from the correct scoped key.

Close/minimize to tray read settings once at startup from the flat-path
DB, but user changes were saved to per-user DB after login. Changed
close-to-tray to read dynamically from state, and minimize-to-tray to
re-fetch before each action.
2026-02-10 06:23:05 -06:00
vicrodh 3edfd165cb fix(snap): copy desktop file and icon to install dir 2026-02-10 04:58:32 -06:00
vicrodh 3356d76ade fix(snap): install SQLite 3.46 to fix sqlite3_error_offset link error
Core22 ships SQLite 3.37 but rusqlite needs 3.38+ for
sqlite3_error_offset. The RUSTFLAGS -L flags cause the linker
to find the system libsqlite3.a before the bundled one.
2026-02-10 03:18:37 -06:00
vicrodh 3c26880e56 feat: add aarch64 architecture support to snapcraft 2026-02-10 02:51:30 -06:00
vicrodh a7409bfdd4 chore: bump version to 1.1.10 2026-02-10 02:17:48 -06:00
vicrodh 051c9a3197 fix: remove z-index from artist page sticky nav to prevent sidebar dropdown overlap 2026-02-10 02:08:11 -06:00
vicrodh c071b86ae2 feat: preserve original Qobuz playlist image when copying to library 2026-02-10 02:05:17 -06:00
vicrodh bbc3b4a32e fix: use localized playlist tags and align tag selector below section title 2026-02-10 01:52:56 -06:00
vicrodh b76b369d9b feat: wire up discover playlists browse page with navigation and i18n 2026-02-10 01:51:31 -06:00
vicrodh 0f8bba5f89 feat: add playlist tags endpoint and DiscoverPlaylistsBrowseView component 2026-02-10 01:48:37 -06:00
vicrodh d912bd1284 feat: add genre_ids support to discover playlists endpoint 2026-02-10 01:43:49 -06:00
vicrodh d453bcd559 fix: remove z-index from favorites-nav and dropdown-container
Both were creating stacking contexts that trapped the sidebar playlist
dropdown behind them. Removing z-index entirely avoids creating stacking
contexts — sticky positioning handles scroll overlap natively, and the
dropdown-menu child manages its own z-index.
2026-02-10 01:25:17 -06:00
vicrodh 8857ef0597 fix: lower favorites-nav z-index to prevent sidebar dropdown overlap
The sticky .favorites-nav header had z-index: 4 which created a stacking
context that trapped the sidebar playlist dropdown menu behind it. Lower
to z-index: 1, matching the pattern used in ArtistDetailView and
LocalLibraryView.
2026-02-10 01:22:04 -06:00
vicrodh 0fe1174a0b fix: move See All link below section title instead of beside it
Wrap title + See All in a flex column group so the link sits directly
under the section heading, left-aligned. Remove margin-left: auto that
was pushing it to the right edge.
2026-02-10 01:13:30 -06:00
vicrodh 3e4f95d297 fix: replace pipe separator with arrow icon for See All links
Remove the pipe divider that looked bad. Replace ChevronRight with
ArrowRight for a cleaner See All > appearance. Revert HorizontalScrollRow
gap change that was only needed for the pipe.
2026-02-10 01:11:21 -06:00
vicrodh ba62057545 feat: add Qobuzissimes, Albums of the Week, Press Accolades browse pages
- Add 3 new Rust endpoint constants and command mappings for
  /discover/qobuzissims, /discover/albumOfTheWeek, /discover/pressAward
- Add 3 new navigation ViewType values and genre filter contexts
- Extend DiscoverBrowseView to handle the 3 new endpoint types
- Add See All links with chevron caret and pipe separator to all 6
  discover sections in HomeView (newReleases, mostStreamed,
  essentialDiscography, qobuzissimes, editorPicks, pressAwards)
- Wire up 3 new views in +page.svelte with full album action callbacks
- Rename Editor's Picks to Albums of the Week, Press Awards to Press
  Accolades across all 4 locale files (en, es, de, fr)
- Fix rank badge: top-right position, Impact font, 35px size
- See All styling: inline-flex with ChevronRight icon, pipe divider,
  15px gap before nav buttons, #666666 color matching nav buttons
2026-02-10 01:10:28 -06:00
vicrodh a575c0c805 fix: use existing genre filter, fix ranking display and See All positioning
- Replace custom genre <select> with GenreFilterButton component, each
  discover page gets independent persistence via separate contexts
- Rank badge in grid mode: 3x bigger (33px), positioned on right side
  of album card, no # symbol
- See All link: pushed to right edge (margin-left: auto), color matches
  nav buttons (#666666), hover transitions to text-primary
- Add proper page padding to DiscoverBrowseView (18px left, 8px right)
2026-02-10 00:57:57 -06:00
vicrodh c9bdcae855 feat: add discover browse pages for New Releases, Ideal Discography, Top Albums
Add full-page browsing views for the three discover sections that were
previously only shown as limited carousels on the Home page.

Backend: add get_discover_albums Tauri command with endpoints for
newReleases, idealDiscography, and mostStreamed. Uses paginated API
calls with genre_ids filtering.

Frontend: new DiscoverBrowseView component with virtualized grid/list,
genre filter dropdown, client-side search, and infinite scroll.
VirtualizedFavoritesAlbumGrid gains ranking support (showRanking),
infinite scroll (onLoadMore), and loading indicator.

HomeView sections now show "See All" links that navigate to the
dedicated browse pages. i18n keys added to all 4 locales.
2026-02-10 00:46:39 -06:00
vicrodh ed06a397ef fix(lyrics): re-fetch when cached entry lacks synced lyrics
Cached entries with plain-only lyrics were served forever, even after
the search-first strategy can now find synced versions. Treat plain-only
cache entries as a miss so the provider chain runs again. If synced
lyrics are found, the cache is updated via upsert.
2026-02-09 23:58:46 -06:00
vicrodh 8cf84b64e8 fix(lyrics): search-first strategy to prioritise synced lyrics from LRCLIB
GET /api/get often returns results without synced lyrics even when they
exist in the database. Switch to search-first: /api/search returns
multiple candidates so pick_best_match can select one with synced lyrics.
GET is now only a fallback when search yields nothing.

Also pass track_name + artist_name as separate search filters instead
of a combined q param for more precise results.
2026-02-09 23:35:25 -06:00
vicrodh dcdddf384c fix: remove inner scroll from Search tabs, use parent scroll container
Search tabs (albums, tracks, artists) had their own overflow-y scroll
containers, causing double scrollbars and preventing the global
back-to-top button and sticky search bar from working.

- Remove height, min-height, overflow from .virtual-scroll-container
- Remove scrollbar pseudo-element CSS for virtual containers
- Feed virtual scroll calculations from parent .search-view scroll
- Remove per-container onscroll handlers (unused)
- ResizeObservers now only track width for grid column calculations
- Container height comes from parent scroll viewport
2026-02-09 23:15:56 -06:00
vicrodh 7b5a7d963d feat: move back-to-top to global layout level
Replaced the per-view back-to-top in PlaylistDetailView with a
global one in +page.svelte. Uses capture-phase scroll listener
on .main-content to track scroll from any child view. Appears
after 800px scroll on any view. Resets on view change.
2026-02-09 23:08:40 -06:00
vicrodh 170b6408f7 fix(artist,library): bump jump-nav z-index above scrolling content
Content elements (track artwork z:1, play overlay z:2) were rendering
on top of the sticky jump-nav (also z:1). Bumped to z:10, still below
dropdown menus (z:50+) and context menus (z:100).
2026-02-09 23:05:21 -06:00
vicrodh cb3b518eb2 fix(playlist): add bottom spacer when suggestions section is absent
Prevents the back-to-top button from covering the last track's
action buttons on playlists without a suggestions section.
2026-02-09 23:01:01 -06:00
vicrodh cdc051d476 fix(playlist): position back-to-top 10px above the player bar 2026-02-09 22:58:50 -06:00
vicrodh 3cd9b99153 feat(playlist): add back-to-top button for large playlists
Floating button appears after scrolling 600px on playlists with
100+ tracks. Smooth scrolls back to top. Positioned bottom-right
with sticky positioning inside the scroll container.
2026-02-09 22:53:31 -06:00
vicrodh 8270de2e5e perf(api): replace Mutex with RwLock for QobuzClient
QobuzClient has no &mut self methods — all API calls are read-only.
The Mutex serialized all concurrent API requests (favorites sync,
sidebar playlists, playlist loading) causing 6+ second wait times
at startup.

RwLock allows concurrent reads. All 94 call sites across 20 files
changed from .lock() to .read(). ListenBrainz/LastFm clients keep
their own Mutex (different state, different lock).
2026-02-09 22:50:35 -06:00
vicrodh 8f1cc735a7 perf(playlist): use $state.raw for tracks to eliminate proxy bloat
$state() creates deep reactive proxies for every object in the array.
With 2000 tracks, each background batch reassignment triggered proxy
creation for all existing + new objects, growing quadratically.

$state.raw() only tracks the reference — no deep proxying. Combined
with a fast path in displayTracks that skips the full copy/interleave
when there are no local tracks, search, or custom sort.
2026-02-09 22:45:12 -06:00
vicrodh 33eb990a6c fix(playlist): eliminate placeholders and proxy bloat for large playlists
The black screen was caused by Svelte 5 creating deep reactive proxies
for 2000 objects (200 real + 1800 placeholders) synchronously, blocking
the main thread for ~27s.

Now: start with only loaded tracks (200), no placeholders. Background
batches append via single array reassignment per group (not N individual
mutations) and yield to the browser between groups. The displayTracks
derived no longer re-spreads 2000 objects on every mutation.
2026-02-09 22:39:14 -06:00
vicrodh e7ff1d647e fix(playlist): keep spinner until first viewport batch loads
For large playlists (>500 tracks), the spinner now stays visible
until the first group of tracks (up to 200) has real data. This
eliminates the placeholder flash — users see spinner then real
tracks. Remaining batches load in the background.
2026-02-09 22:27:23 -06:00
vicrodh de33a41aa4 feat(playlist): adaptive loading — single call for small playlists
Small playlists (≤500 tracks) now use get_playlist_track_ids for
metadata then get_playlist for full data in one shot — no placeholders,
no visible two-phase loading. Progressive batched loading only kicks
in for large playlists (>500 tracks) where it actually helps.
2026-02-09 22:20:26 -06:00
vicrodh 1c7a14d789 fix(api): use POST with JSON body for track/getList endpoint
The Qobuz track/getList endpoint requires a POST request with
JSON body {"tracks_id": [...]} — not GET with query params or
form-encoded POST. This matches the Qobuz web player behavior.
2026-02-09 22:15:18 -06:00
vicrodh 042a307c41 fix(api): add POST fallback and flexible response parsing for track/getList
The Qobuz track/getList endpoint may require POST instead of GET.
Try GET first, fall back to POST with form body if response is an error.
Also handle multiple response structures and log the actual response
keys for debugging.
2026-02-09 22:03:28 -06:00
vicrodh fa784df62e feat(playlist): progressive loading via track_ids + track/getList
Replace monolithic get_playlist call with two-phase approach:
- Phase 1: playlist/get?extra=track_ids returns metadata + all track
  IDs in a single lightweight call (~50KB vs ~5MB)
- Phase 2: track/getList fetches full details in batches of 50,
  4 concurrent groups, with tracks rendering progressively

Backend changes:
- Add TRACK_GET_LIST endpoint and PlaylistWithTrackIds model
- Add get_playlist_track_ids and get_tracks_batch API client methods
- Add corresponding Tauri commands
- Update remove_tracks_from_playlist to accept either
  playlist_track_ids or regular track_ids (resolves internally)

Frontend changes:
- Rewrite loadPlaylist() for two-phase progressive loading
- Show playlist header + placeholder tracks after Phase 1 (~0.5s)
- Fill in full track details as batches arrive
- Add placeholder track row CSS (gray bars, no shimmer)
- Use Qobuz pre-made collage images from playlist metadata
- Update track removal to handle missing playlist_track_id
2026-02-09 21:56:15 -06:00
vicrodh 6a56a1492d debug: add timing logs across entire playlist load pipeline 2026-02-09 21:02:44 -06:00
vicrodh c084e64756 perf(api): fetch playlist pages concurrently after first page
First page is fetched normally to get metadata and total track count.
Remaining pages are launched concurrently via futures::join_all and
reassembled in offset order. For a 2000-track playlist this means
3 concurrent requests instead of 3 sequential ones (~3x faster).
2026-02-09 20:56:04 -06:00
vicrodh 0717e1c478 fix(playlist): clear old tracks immediately on playlist switch to prevent stale recomputation 2026-02-09 20:51:04 -06:00
vicrodh 542eb9b9b5 fix(playlist): reset virtual scroll on playlist change, manual suggestions for all
- Reset trackListScrollTop and scrollContainer.scrollTop to 0 when
  playlistId changes. Fixes tracks not rendering until user scrolls
  (old scroll offset exceeded new playlist's track count).
- Recalculate trackListViewHeight after content mounts via rAF.
- Set SUGGESTIONS_AUTO_THRESHOLD to 0: all playlists require manual
  activation to rule out suggestions as unresponsiveness cause.
2026-02-09 20:45:39 -06:00
vicrodh c36f8580e6 debug: log playlist name on load for virtualization tracking 2026-02-09 20:39:58 -06:00
vicrodh 9ca74daba0 perf(playlist): virtualize track list and defer suggestions computation
- Virtualize playlist track list using absolute positioning and viewport
  culling. Only ~20 track rows are in the DOM at any time instead of all
  2000. Eliminates main-thread blocking when navigating between playlists.
- Gate extractAdaptiveArtists and excludeTrackIds computation: playlists
  >=2000 tracks never compute (Qobuz limit), >500 tracks defer until
  manual activation. The computation itself is skipped, not just the UI.
- Move manual launch button from PlaylistSuggestions to PlaylistDetailView
  so the component only mounts after activation (zero overhead when idle).
- Simplify PlaylistSuggestions by removing redundant manual launch logic.
2026-02-09 20:34:35 -06:00
vicrodh 02f08e5c8b fix(suggestions): skip at Qobuz limit, manual launch for large playlists
Playlists at the 2000-track Qobuz limit no longer mount the
suggestions component at all (nothing can be added).

Playlists with >500 tracks require a manual button press to launch
the suggestion engine instead of auto-loading on mount. This avoids
heavy background work that causes UI freezes on large playlists.
2026-02-09 20:18:10 -06:00
vicrodh 4bbf97a131 feat(playlist-import): multipart playlists for 2000+ track imports
Qobuz limits playlists to 2000 tracks. When matched tracks exceed
this limit, the importer now automatically splits into multiple
playlists named "Playlist (Part 1)", "Playlist (Part 2)", etc.

- models.rs: qobuz_playlist_id -> qobuz_playlist_ids Vec, add parts_created
- importer.rs: chunk matched tracks by 2000, create playlist per chunk
- Frontend: show parts count in summary, move all parts to folder
- i18n: add partsCreated key in en/es/de/fr
2026-02-09 18:43:46 -06:00