Commit Graph
873 Commits
Author SHA1 Message Date
vicrodh 6e8b8410c9 fix: set explicit 100ms buffer size to replace vendored cpal tuning
Replace BufferSize::Default with BufferSize::Fixed(sample_rate / 10)
for non-exclusive mode. This preserves the 100ms period tuning that was
previously applied in the vendored cpal ALSA backend, reducing CPU
wakeups during playback.
2026-02-28 07:48:03 -06:00
vicrodh b5ec5de785 refactor: migrate all code to rodio 0.22 API
- OutputStream/OutputStreamHandle -> DeviceSinkBuilder/MixerDeviceSink
- Sink -> Player (as RodioPlayer)
- current_frame_len -> current_span_len in all Source impls
- Source::channels() returns NonZero<u16>, sample_rate() returns NonZero<u32>
- SamplesBuffer no longer generic, constructor takes NonZero args
- SampleRate is now u32 type alias (no tuple struct)
- Decoder::new_mp4 no longer takes Mp4Type hint
- Applied across both crates/ workspace and src-tauri/ legacy code
2026-02-28 07:48:03 -06:00
vicrodh faf8844584 chore: upgrade rodio 0.19 to 0.22, remove vendored rodio/cpal
Remove vendored rodio and cpal from src-tauri/vendor/ since rodio 0.22
now includes upstream f32 decoder support (our vendored modification).
Update all Cargo.toml files to use rodio 0.22 from crates.io and remove
the [patch.crates-io] section.
2026-02-28 07:48:03 -06:00
vicrodh 99196db70f fix: graceful shutdown on RunEvent::Exit to prevent heap corruption
Handle RunEvent::Exit (fires on SIGTERM from tauri dev hot reload)
to stop audio playback and visualizer before process teardown. The
previous CloseRequested handler only fires on user-initiated close,
not on SIGTERM, leaving ALSA/CPAL C threads active during cleanup
which causes "free(): corrupted unsorted chunks" heap corruption.

- Switch from .run(context) to .build(context).run(callback)
- Stop visualizer and both legacy/V2 players on Exit event
- Destroy miniplayer window on Exit event
2026-02-28 07:48:03 -06:00
vicrodh 6c8354f825 feat: add booklet download button, remove pdfjs-dist dependency
- Add save/download button to booklet toolbar with native file dialog
- Add v2_booklet_save command to copy PDF to user-chosen location
- Remove pdfjs-dist npm package (replaced by MuPDF backend)
- Delete static/pdfjs/ files (~1.9MB: worker, WASM decoders, fallbacks)
- Add bookletDownload i18n keys to all 4 locales
2026-02-28 07:48:03 -06:00
vicrodh 53fc99446b fix: disable alpha in booklet renderer for opaque white background 2026-02-28 07:48:03 -06:00
vicrodh bb723c7ca5 feat: switch booklet viewer from pdfjs-dist to MuPDF backend
Replace browser-side pdfjs-dist canvas rendering with server-side
MuPDF rendering for high-quality PDF booklet display. MuPDF renders
pages to PNG at configurable DPI in Rust, producing much sharper
results than the JavaScript canvas approach which had persistent
JPEG 2000 WASM decoder issues.

- Add mupdf crate with sys-lib features (avoid GTK symbol conflicts)
- Create pdf_viewer.rs with v2_booklet_open/render_page/close commands
- Download PDF to temp file, render pages on demand via MuPDF
- Display rendered PNGs as img tags instead of canvas
- DPI scales with zoom and devicePixelRatio for crisp output
- Support rotation via MuPDF transformation matrix
2026-02-28 07:48:03 -06:00
vicrodh 883f3546c5 fix: fetch booklet PDF through Tauri backend to bypass CORS
PDF loading was failing because pdfjs-dist cannot fetch from
static.qobuz.com due to CORS restrictions in the WebView.

Added v2_fetch_url_bytes command that fetches URL bytes through
Rust's reqwest (no CORS), then passes the binary data to pdfjs.

Also added rotation support (R key or toolbar button) for
vertical booklets.
2026-02-28 07:48:03 -06:00
vicrodh 9ccc0537d4 fix: destroy miniplayer window before app exit
WebKit's WebProcess was crashing (SIGABRT) during EGL/GPU resource
cleanup when the app exited with the miniplayer window still alive.
Now explicitly destroy the miniplayer window before the main process
exits, giving WebKit time to clean up gracefully.
2026-02-28 07:48:02 -06:00
Armin Felder db32a29550 feat(settings): validate window size before saving 2026-02-28 00:43:42 +01:00
vicrodh 6d2128e90a chore: bump version to 1.1.18 2026-02-26 03:41:45 -06:00
vicrodh 339f2b8106 fix(miniplayer): restore next/prev playback and remanent shuffle
cx
2026-02-26 02:03:15 -06:00
vicrodh ddedca1b5e chore: fix remaining Rust warnings
- Allow dead_code on TokenValidationResponse.code (deserialized but not read)
- Remove unused MusicProvider import in commands_v2.rs
- Restore #[allow(non_snake_case)] on v2_plex_cache_get_tracks
- Replace drop(tokens) with let _ = tokens (drop on reference is no-op)
2026-02-25 20:57:30 -06:00
vicrodh e829c5dc7f fix(home): diversify More From Favorites with per-artist cap and shuffle
Shuffle favorite artists randomly so all get a fair chance (was
deterministic, first artist dominated). Cap at 2 albums per artist.
Shuffle final results for variety on each load.
2026-02-25 20:03:48 -06:00
vicrodh fddb7e0c40 chore: suppress camelCase warnings in V2 Tauri commands
Add #[allow(non_snake_case)] to V2 command functions and structs that
use camelCase parameter names for Tauri frontend compatibility.
Renaming to snake_case would break the frontend-backend contract.
2026-02-25 19:51:15 -06:00
vicrodh 56b4d467e3 fix(listenbrainz): fix error display, persistence, and build warning
- Remove unused ListenBrainzResponse struct (build warning)
- Fix error display showing [object Object] instead of actual message
- Persist credentials to legacy SQLite cache on connect/disconnect
  so they survive app restarts
2026-02-25 19:13:15 -06:00
vicrodh 0227e0cb50 fix: More From Favorites now shows discovery albums from favorite artists
Previously, the section displayed the user's own favorite albums — the
same albums they had already marked as favorites.  Now it resolves the
artists behind those favorite albums (and favorite tracks), fetches
their catalogs from the Qobuz API (parallel, max 8 artists, 25 albums
each), and filters out albums the user already has in favorites or
recently played.  Results are cached in reco_album_meta for instant
future lookups.

Closes #122
2026-02-24 15:30:03 -06:00
vicrodh 4073b9fd35 feat: track download format in SQLite for per-quality status
Migrate downloaded_purchases to compound PK (track_id, format_id) so
the same track downloaded in MP3 and FLAC are tracked separately.
Album detail endpoint now returns downloaded_format_ids per track.
2026-02-24 15:07:58 -06:00
vicrodh 328dd39dc2 fix: download path order now Artist/Album/Quality/track
Quality folder was being prepended to the destination path by the
frontend, resulting in Quality/Artist/Album/track. Now quality_dir
is passed as a separate parameter through the full stack and inserted
between album and filename by the backend path builder.
2026-02-24 14:28:26 -06:00
vicrodh 5f1d5a24ce feat(ui): add album cover context menu in AlbumDetailView
Right-click on album artwork shows context menu with:
- Add/Change/Remove custom album cover
- Open in browser
- Save as...

Also adds v2_save_image_url_to_file backend command.

cc
2026-02-24 10:06:02 -06:00
vicrodh 958561ab5d feat(backend): add custom album cover commands
v2_library_set_custom_album_cover: resize, cache, store in db
v2_library_remove_custom_album_cover: delete files + db entry
v2_library_get_all_custom_album_covers: bulk load for frontend store

cc
2026-02-24 09:59:29 -06:00
vicrodh 2b15a0828c chore(packaging): unify .desktop files across all formats
- Add flac; keyword to all .desktop files (was only in deb template)
- Add StartupNotify=true to flatpak .desktop files (was missing)
- Add Keywords line to snap .desktop (was missing entirely)
- Unify shortDescription to match other .desktop Comment fields

cc
2026-02-24 09:32:37 -06:00
vicrodh 1981771584 chore: bump version to 1.1.17 2026-02-23 17:59:24 -06:00
vicrodh 67ef1d5918 fix: apply GDK_DPI_SCALE on all backends, not just XWayland
GDK_DPI_SCALE is a float multiplier that works on X11, XWayland, and
native Wayland. Previously it was only applied when force_x11 was
enabled, leaving native Wayland users with no way to compensate for
their DE scale factor from within the app.

- Move GDK_DPI_SCALE application out of the force_x11 block in main.rs
- Show the DPI Scale setting in the UI for all backends (not gated
  behind the Force X11 toggle)
- GDK_SCALE remains XWayland-only (integer, not useful on Wayland)
- Setting is already persistent in graphics_settings.db
2026-02-23 17:10:41 -06:00
vicrodh 57f8bdefad feat: persist window size and maximized state across sessions
- Add window_width, window_height, is_maximized columns to window_settings DB
  with automatic migration for existing installs
- Restore saved size on startup; restore maximized state after window creation
- Save non-maximized size on every Resize event
- Save maximized flag on CloseRequested
- Default remains 1280x800 for first launch
2026-02-23 16:58:35 -06:00
vicrodh e32ad6edd0 fix: store ToS after successful login instead of gating login on ToS
- Remove require_tos_accepted gate from all login commands
  (v2_auto_login, v2_manual_login, v2_start_oauth_login, v2_login)
- Call accept_tos_best_effort after each successful session activation
  so the value is persisted the moment login succeeds
- Re-initialize LegalSettingsStore in accept_tos_best_effort if torn
  down (e.g. after factory reset that deletes legal_settings.db)
- Bootstrap ToS check now fails open (unwrap_or(true)) so saved
  credentials still trigger auto-login after a factory reset

The frontend checkbox already gates the UI; the backend gate was
causing double-failures when the DB was unavailable post-factory-reset.
2026-02-23 16:47:24 -06:00
vicrodh f8514c8c20 feat(oauth): support Google/Apple/Facebook OAuth popups via on_new_window
window.open() calls from social login providers are intercepted and opened
as related WebviewWindows (required on Linux/WebKitGTK). Both the main
window and all popups watch for play.qobuz.com?code_autorisation= so the
code is captured regardless of which window receives the final redirect.

Cleanup on timeout/cancel iterates all qobuz-oauth* windows.
2026-02-23 16:07:44 -06:00
vicrodh 835fd19934 feat(oauth): persist session token for automatic re-auth on restart
- Save user_auth_token after successful OAuth login
- On bootstrap, restore session via saved token if no email/password creds
- If token is expired, clear it so user sees login screen normally
- Clear OAuth token on v2_clear_saved_credentials (logout)
- Add login_with_token() to api client (reuses OAuth /user/login step)

Also rename t->translateStore in NowPlayingBar.svelte (ADR-001 fix from Codex)
and improve clean-dev-runtime.sh to kill all stale processes and ports
2026-02-23 15:30:43 -06:00
vicrodh eb609faf0b chore(oauth): remove sensitive values from log output 2026-02-23 15:25:31 -06:00
vicrodh 21358aa876 fix(oauth): use 'code' param for /oauth/callback API (Qobuz uses code_autorisation in redirect URL but 'code' in the API) 2026-02-23 15:24:40 -06:00
vicrodh 674990ebe5 debug(oauth): log response body on /oauth/callback failure, add app_id query param 2026-02-23 15:22:06 -06:00
vicrodh 22060d1d35 fix(oauth): correct parameter names (code_autorisation, not code)
Qobuz redirects to play.qobuz.com/discover?code_autorisation=XXX
not ?code=XXX. Both the navigation intercept and the /oauth/callback
API call were using the wrong parameter name, so the code was never
captured and the exchange request was malformed.
2026-02-23 15:18:52 -06:00
vicrodh fea13ecee7 fix(oauth): close window from navigation callback, always return true
on_navigation returning false does not reliably block HTTP 302 redirects
on WebKitGTK (Linux). Closing the window from within the callback is
more reliable than calling close() from an async Tokio thread.

Changes:
- Always return true from on_navigation (never try to block)
- Close oauth window inside on_navigation callback after code capture
- Best-effort close via app.get_webview_window() after await (fallback)
- Add logging of every URL visited for debugging
- Idempotent: ignore duplicate code captures if callback fires twice
2026-02-23 15:16:05 -06:00
vicrodh e5d34ccee7 feat(commands): add v2_start_oauth_login Tauri command with WebView browser login 2026-02-23 14:45:30 -06:00
vicrodh afbe541fca feat(api): add private_key extraction, OAUTH_CALLBACK path, and login_with_oauth_code to legacy client 2026-02-23 14:45:27 -06:00
vicrodh 9d6be623d4 feat(core): add set_session to QbzCore and login_with_session to CoreBridge for OAuth 2026-02-23 14:45:24 -06:00
vicrodh 26db1c1728 chore(release): bump version to 1.1.15 2026-02-23 12:18:08 -06:00
vicrodh 5678032722 Merge branch 'pre-release' of github.com:vicrodh/qbz into pre-release
# Conflicts:
#	src/lib/components/views/ArtistDetailView.svelte
2026-02-23 12:15:38 -06:00
vicrodh 9de0c3c233 feat(backend): add bulk queue and bulk favorites commands
- v2_bulk_add_to_queue: add multiple tracks to end of queue
- v2_bulk_add_to_queue_next: add multiple tracks as play next (reversed
  to preserve selection order)
- v2_bulk_add_favorites: add multiple tracks to favorites via API then
  update local cache atomically (lock not held across await points)
- v2_add_tracks_to_playlist and v2_remove_tracks_from_playlist already
  accept Vec<u64> — no changes needed for playlist bulk actions
2026-02-23 10:55:59 -06:00
vicrodh c473b86a2d fix: cleanup stale KWin SSD rule written by 1.1.14 on startup 2026-02-23 10:00:46 -06:00
vicrodh 9db1e5ca6b perf(cache): increase L1 to 400MB and L2 to 800MB
L1 (memory): 300MB -> 400MB (+100MB)
L2 (disk):   500MB -> 800MB (+300MB)
2026-02-23 08:55:33 -06:00
vicrodh 7c6e07908f feat(backend): add bulk fetch for custom artist images
Add get_all_custom_artist_images() database method and
v2_library_get_all_custom_artist_images command to support
loading all custom artist image overrides at once for the
global resolution store.
2026-02-23 08:55:25 -06:00
vicrodh 01d57ca7bb feat(backend): add remove and get custom artist image commands 2026-02-23 08:55:25 -06:00
vicrodh 98d719d833 feat(backend): resize and thumbnail custom artist images 2026-02-23 08:55:25 -06:00
vicrodh e9c4b4ce0d perf(cache): increase L1 to 400MB and L2 to 800MB
L1 (memory): 300MB -> 400MB (+100MB)
L2 (disk):   500MB -> 800MB (+300MB)
2026-02-23 05:24:33 -06:00
vicrodh fd2457c9ff Merge branch 'feature/image-lightbox' into pre-release 2026-02-23 05:21:38 -06:00
vicrodh 9b4f0dd353 chore(window): suppress dead_code warning on remove_kwin_window_rule
Will be called when v2_set_use_system_titlebar toggles off.
2026-02-23 05:01:41 -06:00
vicrodh abb4f1de50 fix(window): use KWin window rules instead of scripting for native SSD
KWin scripting noBorder=false cannot override the xdg-decoration
protocol on Wayland. Window rules (kwinrulesrc) operate at a deeper
level and should force SSD before protocol negotiation. Uses
kwriteconfig6 to add/remove rules and qdbus6 to trigger reconfigure.
2026-02-23 04:57:58 -06:00
vicrodh 3072a135ef chore(window): remove diagnostic print() from KWin SSD script
Now that the script is confirmed working (noBorder=false set on QBZ
window, resourceClass=qbz), remove the chatty per-window log lines
that would spam journalctl on every app launch.
2026-02-23 04:54:20 -06:00
vicrodh ce8a899244 fix(window): use Script.run() instead of Scripting.start() for KWin SSD
The KWin D-Bus scripting API requires calling run() on the specific
script object (/Scripting/Script{id}) returned by loadScript(), not
start() on the /Scripting root. Also unloads previous script instance
before reloading, and adds 500ms delay to ensure window is mapped.
2026-02-23 04:52:30 -06:00