Commit Graph
100 Commits
Author SHA1 Message Date
vicrodh 3d2a4cbe97 fix(deep-link): robust qobuzapp:// handler across all packages
- Auto-detect installed .desktop filename (com.blitzfc.qbz.desktop,
  qbz.desktop, qbz-nix.desktop) instead of hardcoding
- Run update-desktop-database after register/deregister so xdg-open
  and browsers pick up the MIME association immediately
- Fix v2_check_qobuzapp_handler to match all known desktop filenames
- Align StartupWMClass to com.blitzfc.qbz across all packaging formats
- Add StartupNotify=true to Arch/AUR/Snap desktop files

Fixes browser URI handler not working for .deb users (Discussion #44)
2026-02-23 00:38:10 -06:00
vicrodh 7a5fe2f5ce fix(ui): keybindings settings modal z-index and 2-column layout
KeybindingsSettings (Customize Shortcuts) had z-index 1000, behind
NowPlayingBar (2001). Bumped to 3000 per ADR-003. Also widened to
660px and added 2-column grid layout with responsive fallback.

This was the actual broken modal — KeyboardShortcutsModal was already
fixed but KeybindingsSettings was missed.
2026-02-22 22:28:07 -06:00
vicrodh abc7a1a2a3 fix(ui): right-click context menu on all card components
- AlbumCard: right-click opens AlbumMenu at click position
- FavoritePlaylistCard: right-click opens menu at click position
- QobuzPlaylistCard: add right-click context menu with click positioning
- SearchPlaylistCard: add right-click context menu with click positioning
- All card menus now use floatingMenuStore for mutual exclusion
- Menu positioning uses click coordinates instead of trigger button
2026-02-22 20:17:40 -06:00
vicrodh 0ee8b9db0e feat(settings): make sync audio on startup opt-in
The reload_settings() call at bootstrap is now gated behind a new
sync_audio_on_startup setting (default OFF). Most users don't need
it; it exists for edge cases where Player::new() holds stale settings
(e.g., after Flatpak updates changing DB schema).

Adds toggle in Settings > Audio with i18n for all 4 locales.
2026-02-22 19:54:12 -06:00
vicrodh 0dea116c69 fix(audio): move reload_settings from per-play to once at bootstrap
reload_settings() was running before every v2_play_track call, which
is excessive and can overwrite correct in-memory audio settings with
stale DB values (especially in Flatpak where DB sync timing differs).

Moved to runtime_bootstrap Step 6, after session activation when
per-user stores are ready. v2_reinit_audio_device still reloads on
explicit device changes.
2026-02-22 19:48:32 -06:00
vicrodh 33e8d34bef fix(i18n): add missing diacritics in es, fr, de locales 2026-02-22 18:45:14 -06:00
vicrodh e811afe27e feat(ui): add right-click context menu to album and playlist cards 2026-02-22 18:13:11 -06:00
vicrodh d5203d498a feat(favorites): add random album and artist buttons 2026-02-22 18:12:20 -06:00
vicrodh 6229fa8400 feat(ui): add loading spinner to radio buttons 2026-02-22 18:09:07 -06:00
vicrodh 6c1979b7ea fix(session): persist empty queue state on clear 2026-02-22 18:05:18 -06:00
vicrodh 643bb20c2e feat(settings): add opt-in session persistence toggle 2026-02-22 18:05:02 -06:00
vicrodh d7213e06a4 fix(runtime): wait for CoreBridge init before bootstrap auth 2026-02-22 17:59:22 -06:00
vicrodh 2e51c2e1f2 fix(ui): favorites albums and artists height overflow behind player 2026-02-22 17:58:58 -06:00
vicrodh 37cca5dccf fix(ui): render sanitized HTML in playlist descriptions 2026-02-22 17:58:41 -06:00
vicrodh 66d25b773b fix(ui): keybindings modal z-index and 2-column layout 2026-02-22 17:57:46 -06:00
vicrodh 02bfc7ba91 fix(ui): suppress WebKit default context menu globally 2026-02-22 17:57:05 -06:00
vicrodh 221a66c675 fix(audio): reload settings before first play to prevent 48kHz lock
On first play after app start, the audio thread held stale settings from
Player::new() — if the user had configured PipeWire/ALSA backend, the
thread might not use the correct backend/device, defaulting to 48kHz.

Now v2_play_track reloads settings from the DB before every play_data()
call, matching what v2_reinit_audio_device already does. This is why
re-selecting the device fixed the issue: it called reload_settings first.

Also extracts convert_to_qbz_audio_settings() helper to deduplicate the
settings conversion between v2_play_track and v2_reinit_audio_device.

Fixes #69
2026-02-21 08:54:57 -06:00
vicrodh 06c6b3457b feat(radio): add track radio submenu to playlist and favorites views
Thread onCreateQbzRadio/onCreateQobuzRadio through TrackRow,
VirtualizedTrackList, PlaylistDetailView, and FavoritesView so
track context menus in playlists and favorites show the radio
submenu with QBZ Radio and Qobuz Radio options.
2026-02-21 07:34:58 -06:00
vicrodh 209cacb569 feat(mixes): add FavQ and TopQ to Your Mixes section
FavQ shuffles up to 50 tracks from the user's Qobuz favorites.
TopQ seeds the Qobuz suggest API from the user's most-played playlists.
Both have same-day cache, refresh button, red/blue gradient artwork,
and a "qbz" badge (vs "qobuz" on DailyQ/WeeklyQ).
2026-02-20 22:45:55 -06:00
vicrodh f4113a9622 feat(ui): add right-click context menu to track rows
Add context menu support (right-click) to the inline track rows in
ArtistDetailView and LabelView popular tracks sections. Right-clicking
a track opens the TrackMenu at the cursor position with all available
actions. Also wires up TrackRow component with contextMenuPosition
prop for the same functionality in reusable track rows.
2026-02-20 22:12:35 -06:00
vicrodh 2dcaa76027 feat(radio): add Qobuz artist/track radio with submenu selection
Add /radio/artist and /radio/track Qobuz API endpoints alongside the
existing /radio/album. TrackMenu now shows a radio submenu with QBZ
Radio (RadioPoolBuilder) and Qobuz Radio options when both are
available. Artist header radio button opens a dropdown to choose
between the two engines.

Backend: get_radio_artist(), get_radio_track() in client.rs,
v2_create_qobuz_artist_radio and v2_create_qobuz_track_radio commands.
Frontend: TrackMenu radio submenu with mutual exclusion against
share/download submenus, ArtistDetailView dropdown, LabelView support.
i18n keys added to all four locale files.
2026-02-20 22:10:44 -06:00
vicrodh f8a56d7f4b fix(radio): fetch full track data for album radio
The /radio/album endpoint returns partial track objects without the
performer field, causing all tracks to show as "Unknown Artist".
Extract track IDs from the response and fetch each via get_track()
to get complete data, matching the pattern used by artist/track radio.
2026-02-20 21:38:56 -06:00
vicrodh 557414ce4e feat(radio): add album radio button to UI
Add Radio button to album detail header (next to favorite button)
that calls v2_create_album_radio, populates the queue with Qobuz
API recommended tracks, and starts playback of the first track.

Add i18n keys for album radio in all 4 locale files (en/es/de/fr).
2026-02-20 21:35:15 -06:00
vicrodh d47f89c6b2 feat(radio): add RadioResponse model, API method, and v2_create_album_radio command
Add /radio/album Qobuz API endpoint support:
- RadioResponse struct in models.rs with lenient deserialization
- get_radio_album() client method
- v2_create_album_radio command that calls the API directly (no
  RadioPoolBuilder/RadioDb), filters blacklisted artists, populates
  queue, and sets Radio playback context
- Register command in lib.rs invoke handler
2026-02-20 21:32:23 -06:00
vicrodh 06c0e9735c fix(session): use struct return type and proactive saves
Two fixes for session not persisting:

1. Changed v2_get_all_queue_tracks to return a struct instead of a
   tuple — Tauri IPC serialization of tuples is unreliable.

2. Added proactive session saves instead of relying on async
   beforeunload (which races with window close):
   - Debounced full save on track change (2s after new track starts)
   - Debounced full save on pause
   - Reduced periodic save from 60s to 15s during playback
   - Close handler remains as best-effort backup
2026-02-20 20:43:35 -06:00
vicrodh aa1aa32f7a fix(session): save full queue with all tracks and metadata
The save was broken in two ways:
1. Only current_track + upcoming (capped at 20) were saved, dropping
   all history tracks. Queue was reconstructed incomplete on restore.
2. Track metadata (hires, bit_depth, sample_rate, source, etc.) was
   not being persisted, only the basic 6 fields.

Add v2_get_all_queue_tracks command that returns the full uncapped
track list with correct current_index. Use it in saveSessionBeforeClose
instead of the capped v2_get_queue_state. Map all metadata fields.
2026-02-20 20:33:39 -06:00
vicrodh 5a0d4c50a1 feat(session): re-enable session restore and add startup page setting
Re-enable queue/track restore on startup (visual only, paused at 0:00).
Update saveSessionBeforeClose to persist current view with context IDs.
Add "Startup page" dropdown in Settings > Appearance (Home / Where you
left off) that controls whether the last view is restored on launch.
i18n keys added to all 4 locale files.
2026-02-20 20:02:26 -06:00
vicrodh 07d5d95cea feat(session): extend frontend service and add restoreView helpers
Update PersistedSession interface with last_view fields. Add lastView,
viewContextId, viewContextType params to saveSessionState(). Add
restoreView(), setRestoredPlaylistId(), setRestoredLocalAlbumId() to
navigationStore for session restore without triggering data fetches.
2026-02-20 19:58:54 -06:00
vicrodh cda07f97c5 feat(session): extend schema with last_view columns and V2 params
Add last_view, view_context_id, view_context_type to player_state
table via migration. Update PersistedSession struct, save/load/clear,
and v2_save_session_state with 3 new optional parameters.
2026-02-20 19:58:17 -06:00
vicrodh 6a04f13174 fix(keybindings): resolve 4 keyboard shortcut bugs (#95)
- Strip Shift modifier for symbol keys in eventToShortcut() so ? maps correctly
- Remove duplicate Space handler in ImmersivePlayer that caused double-fire
- Add bindingsVersion dependency to KeybindingsSettings for instant visual updates
- Add missing uiOpenLink i18n key and use getActionLabel() fallback in modal
- Add full keybindings translations to de.json and fr.json
2026-02-20 19:33:40 -06:00
vicrodh cd57d37e7a feat(settings): add font family selector in Appearance
- Add 4 bundled fonts: Montserrat, Noto Sans, Source Sans 3, plus
  existing LINE Seed JP (default) and System fallback
- Font selector dropdown in Settings > Appearance, after Language
- Persisted in localStorage, applied on app bootstrap via data-font
  attribute on <html> which overrides --font-sans CSS variable
- @font-face declarations for variable font files in static/fonts/
- i18n key "fontFamily" added to all 4 locales
2026-02-20 18:13:57 -06:00
vicrodh e82940810b feat(immersive): make window draggable and rename to Immersive Mode
- Add persistent drag region (32px strip) at top of immersive overlay
  that remains active even when the header auto-hides after 4 seconds
- Add app-region: drag CSS to ImmersiveHeader's left spacer for proper
  drag support when header is visible
- Add app-region: no-drag to tabs and window controls to keep them
  clickable
- Double-click on drag strip toggles maximize
- Rename "Full Screen" to "Immersive Mode" in all 4 locales (en/es/de/fr)
2026-02-20 18:03:41 -06:00
vicrodh fec3db17fa feat(link-resolver): direct platform APIs + smart Qobuz search
- Deezer/Spotify/Tidal: fetch metadata directly from platform APIs
  (bypasses Odesli ~300ms vs ~2-3s) with Odesli as fallback
- Smart Qobuz search with 3 progressive attempts:
  1. Full "title artist" query
  2. Cleaned title (strip parenthetical/bracket suffixes like Remastered)
  3. Artist-only search with fuzzy title matching
- Fixes albums not found when platform titles differ (e.g. Iron Maiden
  "Senjutsu (2021 Remaster)" vs Qobuz "Senjutsu")
2026-02-20 16:46:20 -06:00
vicrodh 97e319f2d9 fix(link-resolver): add retry for transient Odesli API errors
The Odesli API can fail with connection errors on rapid successive
requests (connection pool staleness). Add a single retry with 500ms
delay to handle transient network failures gracefully.

Also improve song.link URL handling to hint track vs album detection.
2026-02-20 16:37:58 -06:00
vicrodh 9ba09ff37b fix(share): handle Odesli entities with numeric IDs (Bandcamp)
Odesli's API returns entity IDs as integers for some platforms (e.g.
Bandcamp) while others return strings. The Entity.id field was typed as
String which caused serde deserialization to fail on numeric values.

Add a custom deserializer that accepts both strings and numbers.
Also show actual backend error in LinkResolverModal for easier debugging.
2026-02-20 16:31:02 -06:00
vicrodh e581508946 fix(link-resolver): search Qobuz catalog instead of relying on Odesli platform map
Odesli does NOT include Qobuz in its linksByPlatform response, so the
previous approach of extracting platforms["qobuz"] would always fail.

New strategy: use Odesli to identify the content (title/artist), then
search the Qobuz catalog via CoreBridge to find the matching album or
track. Falls back from track search to album search if needed.
2026-02-20 15:54:33 -06:00
vicrodh b78b67fa8c feat(link-resolver): cross-platform music link resolution via Odesli
Ctrl+L now accepts URLs from Spotify, Apple Music, Tidal, Deezer, and
song.link/album.link in addition to Qobuz. Non-Qobuz track/album URLs
are resolved to their Qobuz equivalent via the existing Odesli API
integration. Playlist URLs redirect to the Playlist Importer.

- Add MusicResource/MusicProvider enums and detect_music_resource()
- Add detect_resource() to each provider (spotify, apple, tidal, deezer)
- Add v2_resolve_music_link command with Odesli-based cross-resolution
- Upgrade LinkResolverModal with platform icon detection and playlist redirect
- Update i18n keys in all 4 locales (en, es, de, fr)
2026-02-20 15:27:49 -06:00
vicrodh 45a1a6471b fix(player): album art hover preview renders above sidebar
NowPlayingBar z-index raised from 100 to 2001 (above sidebar's 2000)
so the artwork preview floats over the sidebar instead of behind it.
Safe because the bar and sidebar occupy non-overlapping vertical space.

Fixes #96
2026-02-20 14:57:37 -06:00
vicrodh 417ece2fb9 fix(library): clean up deleted files during scan refresh
Library scan now automatically removes tracks whose files no longer
exist on disk, both for full scans and single-folder scans. Previously,
deleted files would remain in the library until the user manually ran
"Cleanup missing files" from Settings.

Fixes #93
2026-02-20 14:54:00 -06:00
vicrodh 3f17c83847 fix(purchases): persist verbose log toggle, fix dropdown stacking, wire Add to Library button
- Verbose capture toggle now persists to localStorage across sessions
- PurchasesView dropdown menus now have mutual exclusion (opening one closes others)
- Add to Library button wired to v2_library_add_folder with download destination tracking
- Added i18n keys for add-to-library success/error toasts (all 4 locales)
2026-02-20 14:46:03 -06:00
vicrodh 8051f1745b feat(about): two-column build info, show git commit hash
- Build Info grid now uses 2-column layout (Version|License, Platform|Build)
- Build field shows date + short commit hash (6 chars) injected at
  build time via VITE_BUILD_COMMIT
2026-02-20 14:36:54 -06:00
vicrodh 321a165285 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/sveltejs/kit-2.52.2' into pre-release
# Conflicts:
#	package-lock.json
2026-02-20 14:34:45 -06:00
vicrodh 7b4cccad7b Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/devalue-5.6.3' into pre-release
# Conflicts:
#	package-lock.json
2026-02-20 14:34:35 -06:00
vicrodh dfa6ae3d04 style(about): wider modal, two-column acknowledgments, author as pill
- Increase modal max-width by 110px (672 -> 782)
- Acknowledgments section now uses 2-column grid layout
- Replace plain author name with GitHub avatar pill (slightly larger
  than contributor pills)
2026-02-20 14:32:48 -06:00
vicrodh 384952a500 feat(about): add contributors section with GitHub links
Add horizontal list of code contributors (vorce, boxdot) below the
author section, with avatars and links to their GitHub profiles.
2026-02-20 14:29:49 -06:00
vicrodh 04eeaefdce fix(context-nav): add click navigation for DailyQ and WeeklyQ contexts
The context icon showed correct tooltip on hover but clicking did
nothing for daily_q and weekly_q types. Add missing switch cases
to navigate to the respective views.
2026-02-20 10:39:17 -06:00
vicrodh 908716a230 merge: dependabot svelte 5.53.0 update
Resolve package-lock.json conflict by regenerating lockfile.
2026-02-20 10:33:21 -06:00
vicrodh 74243d4581 feat(playlist-manager): add folder/flat toggle and tree view mode
Add folder/flat toggle button that switches between showing playlists
organized in folders vs a flat list of all playlists. Add tree view as
a third view mode (grid -> list -> tree cycle) when folder mode is on.

- folderMode state persisted to localStorage
- Tree view groups playlists by folder with expand/collapse
- Flat mode shows all playlists regardless of folder assignment
- Auto-switch from tree to grid when folder mode turns off
- i18n keys added to all 4 locale files (en, es, de, fr)
2026-02-20 09:07:48 -06:00
vicrodh 6849a9b3d6 fix(label): add playlist action buttons, increase section spacing
Wire onPlaylistPlay, onPlaylistPlayNext, onPlaylistPlayLater,
onPlaylistCopyToLibrary, onPlaylistShareQobuz through LabelView Props
to QobuzPlaylistCard. Increase section spacing from 48px to 64px.
2026-02-20 04:13:22 -06:00
vicrodh cda9619d65 fix(label): batch artist image fetch, increase section spacing
- Single state update for all artist images instead of N updates
- Prefer thumbnail/small images for carousel performance
- Increase section spacing to 48px between sections
2026-02-20 04:06:04 -06:00
vicrodh 27e765cc83 fix(label): fetch artist images, section spacing, scroll offset
- Fetch artist images via v2_get_artist for artists without images
  from the label page data (parallel fetches with fallback)
- Add 20px extra spacing between sections
- Add scroll-margin-top to section anchors so jump-nav doesn't
  cover section titles when clicking nav links
2026-02-20 04:02:01 -06:00
vicrodh 77b269f346 fix(label): load releases from v2_get_label for carousel
The /label/page endpoint releases containers were unreliable.
Now fetches 20 albums from v2_get_label (same as old LabelView)
for the releases carousel. Also loads description as fallback.
Critics' picks still parsed from /label/page containers.
2026-02-20 03:56:45 -06:00
vicrodh 2d2df20c45 fix(context): add DailyQ, WeeklyQ, LabelTop playback context types
DailyQ and WeeklyQ were incorrectly using 'home_list' context type.
LabelTop was defined in frontend but missing from Rust backend.

- Add LabelTop, DailyQ, WeeklyQ variants to Rust ContextType enum
- Add match arms in commands_v2.rs and display_info()
- Update frontend ContextType union and display strings
- DynamicSuggestView now uses 'daily_q' context type
- WeeklySuggestView now uses 'weekly_q' context type
2026-02-20 03:55:39 -06:00
vicrodh df3b203875 fix(label): playlist images, artist images, jump-nav gap
- Switch from SearchPlaylistCard to QobuzPlaylistCard (matches HomeView)
- Add getPlaylistImage() handling rectangle/covers/images300 formats
- Improve getArtistImageUrl() with picture field and medium size fallback
- Fix jump-nav gap: set padding-top:0 to match ArtistDetailView
- Add debug logging for raw playlist/artist data
2026-02-20 03:51:42 -06:00
vicrodh 4f223ba31a fix(label): jump-nav below header, track play resilience, description fallback
1. Move jump-nav below header (was above), match ArtistDetailView margins
2. Wrap handleTrackPlay context/queue in try-catch so onTrackPlay always fires
3. Add description fallback: fetch from v2_get_label if label/page omits it
4. Add detailed console logging for track play debugging
2026-02-20 03:45:07 -06:00
vicrodh 4c2a63239e fix(label): rewrite track row, add jump-nav, fix description border
- Replace track row with ArtistDetailView pattern (favorites, TrackMenu, radio)
- Add sticky jump-nav bar with IntersectionObserver for all sections
- Fix description border leak with CSS reset
- Make "More labels" containers round
- Wire onTrackAddFavorite prop in +page.svelte
- Add i18n keys: label.about, label.otherLabels (all 4 locales)
2026-02-20 03:29:13 -06:00
vicrodh e414121273 fix(label): polish label detail view UI
- Add back button with ArrowLeft icon
- Make header label image circular (border-radius: 50%)
- Remove play pill button from header (redundant with player bar)
- Replace artist circles with Search-style cards (160x220, bg-secondary)
- Fix artist image extraction (prioritize image.large over portrait hash)
- Replace QobuzPlaylistCard with SearchPlaylistCard for proper collage images
- Add missing action-btn-circle CSS for Popular Tracks controls
- Fix duplicate color property in label card placeholder
2026-02-20 03:11:53 -06:00
vicrodh 585053bd42 feat(label): redesign label detail view with Android-style sections
Replace the flat album grid LabelView with a rich detail page featuring:
- Header with label image, name, description, and play button
- Popular Tracks section (5/10/all expand, same UX as ArtistDetailView)
- Releases horizontal scroll row with "See all" → LabelReleasesView
- Critics' Picks section from second releases container
- Playlists, Artists, and More Labels sections

Backend: add /label/page and /label/explore API endpoints with V2
commands (v2_get_label_page, v2_get_label_explore). Models use
serde_json::Value for flexible section parsing.

Old LabelView renamed to LabelReleasesView for the "See all" flow.
i18n keys added to all 4 locales (en, es, de, fr).
2026-02-20 02:51:47 -06:00
vicrodh 318318a3ab feat(purchases): create quality subfolder for downloads
Append a subfolder named after the selected format quality
(e.g. "FLAC 24-bit - 96 kHz") to the user-chosen destination.
Different quality downloads of the same album no longer overwrite
each other.
2026-02-20 02:06:10 -06:00
vicrodh d8f28385d9 fix(purchases): persist download progress across navigation
Move download state from component-local $state() to a persistent
module-level Svelte store (purchaseDownloadStore). Downloads continue
in the background when navigating away from album detail, and
progress bars are restored when returning.

Closes #90
2026-02-20 02:04:27 -06:00
vicrodh 1ba4622828 fix(purchases): add stopPropagation to per-track download buttons in album detail
Clicking the download icon on a track row also triggered row playback
due to event bubbling. Add e.stopPropagation() to both the normal
download and failed-retry download buttons.
2026-02-20 01:43:21 -06:00
vicrodh 7bdaa2976f fix(purchases): wire track download button in tracks tab
The download button in the tracks tab had no onclick handler, so
clicking it triggered the row's playback handler instead. Now it
opens the native folder picker, downloads with the best available
format, and shows download status (spinner/checkmark). Artist link
also uses stopPropagation to avoid triggering playback. Fixes #91.
2026-02-20 01:38:31 -06:00
vicrodh 62e2aaea51 fix: replace info tooltip hover with click-to-show popup
Title attribute doesn't work reliably. Now the (i) icon opens a
tooltip on click, auto-dismisses after 2 seconds or on click anywhere.
2026-02-20 01:31:02 -06:00
vicrodh ca3f43614b feat: add back button, info tooltip, duration, fix context for mixes
- Add back navigation button to both DailyQ and WeeklyQ views
- Replace subtitle with home card descriptions
- Remove cache/live source text, add total playback duration
- Remove refresh button from WeeklyQ (regenerates weekly on Fridays)
- Fix weekly cache to use Friday-based key instead of daily
- Add algorithm info icon with tooltip on both views
- Fix playback context: align queue index with blacklist-filtered tracks
- Change header label from "Playlists" to "Your mixes"
- Add algorithmInfo i18n key to all 4 locales
2026-02-20 01:26:21 -06:00
vicrodh 5ef0d56cb4 feat: wire full playback system for DailyQ and WeeklyQ views
Add parent-driven track playback with queue setup, playback context,
context menus, offline cache status, and artist blacklist filtering.
Both views now behave like normal playlist views.
2026-02-20 01:20:47 -06:00
vicrodh f8a62afcaa style: use transform-based silk animation with rotation and scale
Replaced background-position animation with a ::before pseudo-element
(inset -40%) animated via transform (translate + rotate + scale).
GPU-composited, zero repaints. Each card has its own keyframes
(silk-daily 30s, silk-weekly 34s) with different movement patterns
so they never look synchronized.
2026-02-20 01:11:20 -06:00
vicrodh 376c789f8e style: make silk animation longer and more erratic
Increased duration to 25s with 7 irregularly spaced keyframe stops
to make the loop less predictable. WeeklyQ offset at -11s for
desync. Same CPU cost — still just background-position interpolation.
2026-02-20 01:09:26 -06:00
vicrodh e0770eb4fa style: add highlight and shadow streaks to mix gradients
Two narrow linear-gradient bands (3% spread each) layered on top:
a bright highlight streak and a dark shadow streak. They shift with
the silk animation creating visible contrast lines across the surface.
2026-02-20 01:08:57 -06:00
vicrodh fcac8294bc style: add silk-shift animation to mix gradient backgrounds
Slow background-position animation on 200% oversized gradients
creates a subtle flowing silk effect. 10s cycle, alternate
direction. Applied to Home cards and detail view artworks.
2026-02-20 01:07:18 -06:00
vicrodh a114693993 fix: align mix card labels bottom, add badge spacing, reserve 3 lines
Restored justify-content: flex-end so qobuz/name stay at bottom of
gradient. Added margin-bottom between badge and name for separation.
Set min-height on description to 3 lines so cards stay aligned.
2026-02-20 01:06:05 -06:00
vicrodh 1dee84c0ac style: redesign mix cards with descriptions and top-aligned labels
Replaced redundant title/owner below cards with descriptive text
(DailyQ: customized selection, WeeklyQ: every Friday emphasis).
Moved qobuz badge and mix name to top of gradient card. Added
yourMixes/weeklyMixes i18n sections to de.json and fr.json.
2026-02-20 01:04:50 -06:00
vicrodh 5475ea0095 style: replace mix videos with CSS gradients, remove nav buttons
Replaced video backgrounds with layered radial/linear CSS gradients
matching the iOS Qobuz style. DailyQ uses gold/amber tones, WeeklyQ
uses purple/lilac. Cards overlay "qobuz" badge and mix name on the
gradient. Removed HorizontalScrollRow wrapper since only two cards
exist. Deleted video assets from static/video/.
2026-02-20 01:01:12 -06:00
vicrodh 10edb92c0c fix: enable scroll on DailyQ and WeeklyQ views
Added height: 100%, overflow-y: auto, and padding-bottom to the
container so the track list is scrollable.
2026-02-20 00:56:58 -06:00
vicrodh 8aa9c472fa feat(dailyq): revamp DailyQ/WeeklyQ views and add video assets
Redesigned DynamicSuggestView and WeeklySuggestView with updated
VirtualizedTrackList. Added looping video backgrounds for mix cards.
2026-02-20 00:54:45 -06:00
vicrodh fc5da6cfc4 feat(home): add Your Mixes section with DailyQ and WeeklyQ cards
Static section (no API call) with video-background cards that navigate
to DynamicSuggestView and WeeklySuggestView. Positioned after Qobuz
Playlists in default Home order, toggleable via Home Settings.
2026-02-20 00:54:08 -06:00
vicrodh 494ddce28b feat(v2): register purchases-get-ids, purchases-get-by-type, and dynamic-suggest commands
New V2 Tauri commands:
- v2_purchases_get_ids: fetch purchase IDs with optional type filter
- v2_purchases_get_by_type: fetch all albums or tracks with download status
- v2_dynamic_suggest: typed dynamic suggestions from Qobuz API
- v2_dynamic_suggest_raw: raw JSON variant for debugging

Registered all four in the Tauri invoke handler.
2026-02-20 00:15:12 -06:00
vicrodh a7ffe13469 feat(api): add purchases-by-type, purchase-ids, and dynamic-suggest endpoints
New API client methods:
- get_user_purchases_page_typed: fetch purchases filtered by type (album/track)
- get_user_purchases_ids_page_typed: fetch purchase IDs by type
- get_user_purchases_all_typed: paginated fetch for a single purchase type
- get_user_purchases_ids_page: generic purchase IDs page
- get_dynamic_suggest / get_dynamic_suggest_raw: Qobuz dynamic suggestions

New models: DynamicSuggestRequest/Response/Track, DynamicTrackToAnalyse,
PurchaseIdsResponse, lenient serde deserializers for robust API parsing.
2026-02-20 00:15:05 -06:00
vicrodh 3a297c0b2b style: apply cargo fmt across codebase
Automated formatting pass covering 79 Rust source files.
No logic changes -- only line wrapping, import ordering,
and match/closure reformatting.
2026-02-20 00:14:58 -06:00
vicrodh f9313c2d80 feat(weeklyq): add WeeklyQ view scaffold and i18n labels
cx
2026-02-20 00:01:43 -06:00
vicrodh ea9051409a feat(dailyq): add generated DailyQ view and dynamic suggest client
cx
2026-02-20 00:01:31 -06:00
vicrodh 741559184d feat(purchases): load purchases by type and ids metadata
cx
2026-02-20 00:01:27 -06:00
vicrodh 2fad220bc9 feat(label): add API-side search within label view
Use v2_search_albums with post-filter by label ID instead of client-side
filtering. This finds albums across the full Qobuz catalog for the label,
not just the ones already loaded via infinite scroll.
2026-02-19 21:03:58 -06:00
vicrodh 867b1dfbe0 perf(share): eliminate Odesli API call, construct song.link URLs directly
ISRC/UPC paths now use a single Deezer API request instead of two
sequential requests (Deezer + Odesli). The song.link/album.link URL
is constructed directly from the Deezer ID (e.g. song.link/d/12345).

This cuts latency from ~2-5s (often timing out) to ~200ms.
The Odesli fallback is kept only for get_by_url (no ISRC/UPC).
2026-02-19 18:47:44 -06:00
vicrodh 844bd60319 fix(share): use Deezer ID + platform param for faster Odesli lookups
Instead of passing a full Deezer URL to Odesli (which requires URL
resolution), use platform=deezer&type=song&id=XXX params directly.
This is faster since Odesli skips URL parsing.

Also fix album sharing (get_by_upc) — Odesli no longer accepts ?upc=
directly, same Deezer resolution approach applied.

Bump request timeout from 10s to 20s with 5s connect timeout.
2026-02-19 18:42:14 -06:00
vicrodh cfa66ef316 fix(share): restore Song.link sharing via Deezer ISRC resolution
Odesli no longer accepts ISRC as a direct query param or Spotify search
URLs. Replace with a two-step approach: resolve ISRC to a real Deezer
track URL via Deezer's free API, then pass that URL to Odesli.

Also fix error display in frontend — RuntimeError objects were shown as
[object Object] instead of extracting the details message.
2026-02-19 18:22:25 -06:00
vicrodh cbc2ff0b26 feat(audio): add PipeWire force bit-perfect mode
Add experimental toggle to force PipeWire clock.force-quantum alongside
clock.force-rate for true bit-perfect playback. Quantum is matched to
sample rate (1024 for <=48kHz, 2048 for <=96kHz, etc). Both values
reset to 0 on stop and pause-suspend to avoid affecting other apps.

Requires DAC Passthrough to be enabled, PipeWire backend only.
2026-02-19 18:01:42 -06:00
vicrodh 98ffd3dff2 chore: bump version to 1.1.15-rc-2 2026-02-19 16:57:19 -06:00
vicrodh 6f612e699f feat(purchases): add playback support to purchases views
Purchased tracks are streamable via normal Qobuz API. Reuse existing
handleDisplayTrackPlay/playAlbumById infrastructure to enable click-to-play
on track rows and a Play button on album detail header. Active track
highlights with accent color and animated playing bars.
2026-02-19 16:57:16 -06:00
vicrodh 9bd8aec91b fix(purchases): add lenient deserialization for non-vital fields
If the Qobuz API returns an unexpected type for optional fields like
purchased_at, label, genre, quality metadata, etc., the field silently
defaults to None instead of crashing the entire purchases fetch.

cc
2026-02-19 16:36:07 -06:00
vicrodh 45ca758f47 fix(purchases): deserialize purchased_at as i64 unix timestamp
The Qobuz API returns purchased_at as a Unix timestamp integer, not a
string. Updated Rust structs, TypeScript types, and frontend date
formatting to handle numeric timestamps correctly.

Fixes #88

cc
2026-02-19 16:29:13 -06:00
vicrodh 183a0ec50d chore(types): add track_number and description to interfaces
Add optional track_number to QobuzTrack and description to QobuzAlbum.

cc
2026-02-19 16:19:02 -06:00
vicrodh 45116707bf fix(player): add null guard for seek target position
TypeScript narrowing for seekTargetPosition after the initial null
check to prevent potential runtime error.

cc
2026-02-19 16:19:01 -06:00
vicrodh e245b702bd fix(ui): improve error message formatting in LoginView
Extract structured error messages from objects instead of raw
String(err) which shows [object Object].

cc
2026-02-19 16:19:00 -06:00
vicrodh e375826fa3 fix(ui): ADR-001 compliance in SpectralRibbon, remove unused AlsaUtilsHelpModal
Rename t variable to interpWeight in SpectralRibbon to avoid shadowing
the i18n store. Delete unused AlsaUtilsHelpModal component.

cc
2026-02-19 16:18:58 -06:00
vicrodh 2508ea9e55 chore(dev): improve dev scripts and runtime cleanup
Add svelte-kit sync + --force to dev script, add clean-runtime script,
bind vite to 127.0.0.1 explicitly.

cc
2026-02-19 16:18:53 -06:00
vicrodh 33aa848dcf fix(flatpak): improve metainfo for Flathub compliance
Shorten summary, add branding colors, add xml:lang to screenshot
captions, remove redundant Features heading.

cc
2026-02-19 16:18:52 -06:00
vicrodh c380434619 fix(ci): skip RC tags in AUR workflow
Only trigger AUR package update on stable version tags, not release
candidates.

cc
2026-02-19 16:18:47 -06:00
vicrodh 3d497a33b0 fix(desktop): add %u to Exec and complete tarball .desktop for URI handling
The Tauri .desktop template was missing %u in the Exec line, preventing
the desktop environment from passing qobuzapp:// URIs as arguments when
launched from the browser. The tarball .desktop was also missing MimeType,
StartupWMClass, and StartupNotify fields.

Closes #44

cc
2026-02-19 16:17:25 -06:00
vicrodh 13d9ec3724 refactor(ui): convert queue panel from overlay to inline sidebar
Queue now pushes main content instead of overlaying it, fixing the
search header z-index conflict. Lyrics sidebar height fixed to 100%
to fill parent container correctly. Queue and lyrics are now mutually
exclusive - opening one closes the other.
2026-02-19 08:55:09 -06:00
vicrodh 5b76530a4d docs: add glibc 2.38+ requirement for DEB/RPM packages
Ubuntu 24.04 build target (required for Snap Store) means native
packages need glibc 2.38+. Older distros should use Flatpak, Snap,
or AppImage instead.
2026-02-18 16:52:30 -06:00