Commit Graph
2272 Commits
Author SHA1 Message Date
vicrodh b12a06fff3 feat(v2): expand V2 commands with playback and dual event emission
V2 Commands expanded:
- Add searchType parameter to search commands
- Add blacklist filtering to V2 search commands
- Add V2 playback commands (pause, resume, stop, seek, volume, state)

TauriAdapter dual emission:
- Emit both "core-event" (new) and legacy events during migration
- Legacy events: queue:updated, queue:shuffle-changed, queue:repeat-changed
- Legacy events: auth:logged-in, auth:logged-out, error

Frontend migration (partial soft-delete test):
- SearchView, ArtistDetailView, LocalLibraryView use v2_search_*
- HomeView, +page.svelte use v2_get_album, v2_get_track
- TrackReplacementModal uses v2_search_tracks

Old commands remain registered but are no longer called by migrated
frontend code. This enables testing V2 architecture while keeping
fallback available.
2026-02-14 20:10:58 -06:00
vicrodh a8f56444ce feat(v2-commands): fix parameter names for frontend compatibility
- Use camelCase parameter names (albumId, trackId, artistId) to match
  frontend expectations
- Add #[allow(non_snake_case)] to suppress warnings

V2 commands are ready for testing. Full frontend migration pending:
- Search commands need searchType parameter
- Need to add get_user_info, login with full flow, etc.
2026-02-14 19:48:12 -06:00
vicrodh b930fa11ba fix(v2-commands): return SearchResultsPage for search commands
Update search commands to return full SearchResultsPage<T> with pagination
info instead of just Vec<T>. This makes V2 commands compatible with the
frontend's expected response format.
2026-02-14 19:46:16 -06:00
vicrodh 8ed8f02fcd Phase 7a: Replace src-tauri audio module with qbz-audio re-exports
- Remove duplicate audio module files from src-tauri
- Update audio/mod.rs to re-export from qbz_audio
- Single source of truth for audio types now in qbz-audio crate
- Compilation passes, types are aligned
2026-02-14 17:48:03 -06:00
vicrodh 71f8d09c41 feat(qbz-player): Phase 6.5 - export full Player module
Reorganize qbz-player to export the complete Player struct with all
playback functionality.

Changes:
- Restructure: mod.rs -> player/mod.rs with submodules
- Add rodio, serde, reqwest, qbz-qobuz dependencies
- Export Player, SharedState, PlaybackState, PlaybackEvent
- Export streaming types (BufferedMediaSource, etc.)

The Player module is now fully available from qbz-player crate.
Full workspace and src-tauri compile successfully.
2026-02-14 15:38:53 -06:00
vicrodh 0238b1c559 feat(qbz-nix): Phase 6.4 - add search and catalog V2 commands
Add search and catalog retrieval methods to QbzCore and V2 commands.

QbzCore methods added:
- search_albums, search_tracks, search_artists
- get_album, get_track, get_artist

V2 commands added:
- v2_search_albums, v2_search_tracks, v2_search_artists
- v2_get_album, v2_get_track, v2_get_artist

All commands registered in invoke_handler.
2026-02-14 15:16:31 -06:00
vicrodh 255dcf06fe feat(qbz-nix): Phase 6.3 - wire up CoreBridge in app setup
Initialize CoreBridge in Tauri setup hook and register V2 commands.

Changes:
- Add CoreBridgeState to managed Tauri state
- Initialize CoreBridge asynchronously in setup hook
- Register V2 commands in invoke_handler

The app now initializes the new multi-crate architecture alongside
the existing system. V2 commands are available for frontend testing.
2026-02-14 14:56:36 -06:00
vicrodh f59f0e1b87 feat(qbz-nix): Phase 6.2 - CoreBridge and V2 commands
Add bridge layer connecting QbzCore to Tauri and example V2 commands.

New files:
- core_bridge.rs: CoreBridge wraps QbzCore for Tauri integration
- commands_v2.rs: Example commands using new architecture

V2 commands implemented:
- v2_is_logged_in, v2_login, v2_logout (auth)
- v2_get_queue_state, v2_set_repeat_mode, v2_toggle_shuffle, v2_clear_queue

These coexist with old commands during migration. Frontend can
gradually switch to V2 commands as they're tested.
2026-02-14 14:54:20 -06:00
vicrodh d0e46aca05 feat(qbz-nix): Phase 6.1 - integrate extracted crates
Add dependencies on extracted crates and create TauriAdapter.

Changes:
- Add qbz-models, qbz-audio, qbz-player, qbz-qobuz, qbz-core as deps
- Add async-trait dependency for FrontendAdapter impl
- Create tauri_adapter.rs implementing FrontendAdapter
- TauriAdapter emits CoreEvents to frontend via Tauri event system

This is the first step of integration. The old modules still exist
but the new crates are now available for gradual migration.
2026-02-14 14:32:24 -06:00
vicrodh 30fb25a187 feat(qbz-core): implement Phase 5 - core orchestrator
Add QbzCore<A: FrontendAdapter> as the main orchestrator that ties
all subsystems together and provides a unified API for frontends.

Changes:
- Add core.rs with QbzCore struct
- Add error.rs with CoreError enum
- Update lib.rs with re-exports

QbzCore provides:
- Initialization and shutdown
- Authentication (login/logout)
- Queue operations (shuffle, repeat, clear)
- Event emission through FrontendAdapter
- Access to underlying subsystems (client, queue)

Full workspace compiles with cargo check.
2026-02-14 14:20:24 -06:00
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
Joel C 1ad22000ea Add unit tests 2026-02-14 17:57:26 +01:00
Joel C f29f95f6d2 Fix clear queue, preserve playing track if needed 2026-02-14 17:57:16 +01:00
Joel C 4908421308 Fix play queue move_track, map incoming from, to index to internal state 2026-02-14 17:57:00 +01: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
Victor RH 7e80279427 Merge branch 'features/1.1.14' 2026-02-13 07:58:56 -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
boxdot c207a60ba4 fix: UI lag
The lag was due to using sqlite in non-WAL mode.
2026-02-13 09:29:26 +01: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