ToS acceptance was stored in localStorage which gets cleared on app
updates, causing users to re-login frequently. Now stores ToS state
in SQLite (~/.local/share/qbz/legal_settings.db) which survives
updates.
- Add legal_settings.rs with SQLite storage for ToS acceptance
- Update qobuzLegalStore.ts to use Rust invoke commands
- LoginView now loads ToS from Rust before auto-login check
- Migration from localStorage for existing users
Closes#31
- Add check_alsa_utils_installed and get_linux_distro Tauri commands
- Detect Linux distribution from /etc/os-release
- Show appropriate package manager command for each distro
- Add (?) help icon next to ALSA Direct device dropdown in Settings
- Modal explains why alsa-utils is needed for bit-perfect detection
- Updated PKGBUILDs to list alsa-utils as optdepend
Backend changes:
- Add device_bus and is_hardware fields to AudioDevice
- Parse PipeWire Flags (HARDWARE) and device.bus property from pactl output
Frontend changes:
- DeviceDropdown now supports both ALSA and PipeWire backends
- ALSA grouping: Defaults, Bit-perfect, Plugin Hardware, Other
- PipeWire grouping: Defaults, USB Audio (BP capable), HDMI/DisplayPort, Other HW, Virtual
- BP badge shown for USB Audio devices in PipeWire mode
Backend changes:
- Add supported_sample_rates field to AudioDevice struct
- Detect common audio sample rates (44.1, 48, 88.2, 96, 176.4, 192, 352.8, 384 kHz)
- Use CPAL's supported_output_configs() to check rate support for hw: devices
Frontend changes:
- Display sample rates as subtitle in DeviceDropdown (e.g., "44.1, 48, 96, 192 kHz")
- Only shown for bit-perfect devices where detection is available
- Search not sensitive to sample rates field (per user request)
Adds streamable field throughout the stack to detect tracks that have
been removed from Qobuz but still appear in playlists/favorites:
Backend (Rust):
- QueueTrack: add streamable field with default true
- PersistedQueueTrack: add streamable for session persistence
- radio.rs: log warning when non-streamable track is encountered
Frontend:
- BackendQueueTrack: add streamable? field
- QobuzTrack, Track, PlaylistTrack types: add streamable
- qobuzAdapters.ts: include streamable in track conversions
- trackActions.ts: all queue builders now include streamable
- +page.svelte: debug logging includes streamable field
This prepares the infrastructure for graying out unavailable tracks
in the UI (to be implemented when we can reproduce the issue).
Refs: User report about "Failed to fetch audio" for removed tracks
This ensures favorites sync properly across all views (album detail,
continue listening, etc.) by using local persistence as the source
of truth for UI, with API calls for actual operations.
Backend (Rust):
- Add favorites_cache.rs module with SQLite tables:
- favorite_tracks (track_id INTEGER PRIMARY KEY)
- favorite_albums (album_id TEXT PRIMARY KEY)
- favorite_artists (artist_id INTEGER PRIMARY KEY)
- Add Tauri commands for cache operations:
- get_cached_favorite_* - read from local cache
- cache_favorite_* / uncache_favorite_* - add/remove single items
- sync_cached_favorite_* - bulk sync from API data
- clear_favorites_cache - for logout
Frontend:
- Rewrite favoritesStore.ts to use persistence:
- loadFavorites(): Load from cache first, then sync from API
- toggleFavorite(): API call first → update cache on success
- No optimistic updates - UI shows loading state until confirmed
- Rewrite albumFavoritesStore.ts with same pattern
- Create artistFavoritesStore.ts for artist favorites
- Update +page.svelte to load all favorites on login
- Update FavoritesView to sync cache when loading from API
Sync strategy:
- On login: Fetch from Qobuz API → sync to local cache
- On toggle: API call → on success → update local cache → notify UI
- FavoritesView reads from API and syncs local cache
- All other views read from local cache (instant UI)
Previously, streaming tracks could not be resumed after pause timeout
because current_audio_data was never set for streaming playback.
Changes:
- Store BufferedMediaSource in current_streaming_source for streaming tracks
- On resume, check if streaming source has complete data and use it
- Clear streaming source on Stop and when switching to non-streaming play
- Log buffered bytes when streaming not complete yet
This allows users to pause streaming tracks and resume them after the
download completes, instead of getting "no audio data available" error.
- metadata.rs now creates 500px thumbnails instead of copying full images
- Added cache management commands: library_get_cache_stats,
library_clear_artwork_cache, library_clear_thumbnails_cache
- Thumbnails stored in ~/.local/share/qbz/thumbnails/ (was ~/.cache/qbz/artwork)
- Reduces cache size from GB to MB for large libraries
Quality was hardcoded to UltraHiRes in backend. Now:
- Frontend passes quality from localStorage to play_track command
- Backend parses quality string to Quality enum
- All prefetch and stream URL calls use the user's preferred quality
- Add all_artists field to LocalAlbum for accurate artist matching
- Consolidate duplicate artists with case differences (e.g., "Alice in Chains" / "Alice In Chains")
- Fix album count calculation to avoid double-counting
- Show Various Artists albums under contributing artists
- Handle multi-artist names (e.g., "A, B, C") with union matching
- Add cleanup missing files feature in settings maintenance section
- Improve settings UI with side-by-side Maintenance/Danger Zone layout
- Add status indicator during cleanup operations
Albums now show a small badge indicating their source:
- HardDrive icon for local files (source='user')
- Qobuz logo for offline cached content (source='qobuz_download')
Badge appears in bottom-right corner of album artwork.
When deleting a cached track (individually, via clear all, or via LRU
eviction), the audio file was deleted but cover.jpg remained orphaned.
Now the system removes the entire album folder if no audio files remain,
and recursively removes the artist folder if it also becomes empty.
- Add artist_id field to SuggestedTrack for navigation
- Make artist names clickable to navigate to artist view
- Make album names clickable to navigate to album view
- Add TrackInfo button (info icon) to view track credits
- Integrate TrackInfoModal component with suggestions
- Update PlaylistDetailView to pass navigation callbacks
The build_vector function was calling build_from_musicbrainz and
build_from_qobuz while holding the store lock. These functions also
try to acquire the store lock, causing a deadlock.
Fixed by saving the vectors from the first call and reusing them
for storage, instead of calling the functions again.
Major optimizations:
- Extract only top 5 artists by track frequency instead of all artists
- Use faster rate limiter (200ms) when using MusicBrainz proxy
- Remove skip_vector_build since we now have manageable artist count
Expected load time: ~5-8 seconds for first load with 5 artists
vs previous: 50+ seconds with 50 artists
When skip_vector_build is enabled, also skip network calls for MBID
resolution of uncached artists. This prevents rate limiting (429) errors
that were causing 55+ second delays on initial load.
Tradeoff: First load with empty cache may have no results, but
subsequent loads will be instant using cached data.
- Add skip_vector_build config option to SuggestionConfig
- When true, skips ensure_vector calls (which can be very slow)
- Uses only cached/existing vectors for similarity search
- Frontend now uses skip_vector_build=true for initial load
- Add timestamped logging helper in frontend component
- Move MBID resolution from frontend to backend
- Eliminates multiple sequential IPC calls
- Uses MusicBrainz cache for faster resolution
- Logs resolution progress for debugging
- Implement progressive loading in UI
- Initial load: 18 tracks (3 pages)
- "Load more" expands to 60 tracks on demand
- Better feedback during loading states
- Fix refresh button styling
- Remove background while spinning
- Update title based on available actions
- Add "Load more" link in pagination area
- Add weights.rs with configurable RelationshipWeights
- Add builder.rs with ArtistVectorBuilder for constructing vectors
- Integrate MusicBrainz relationships (members, groups, collaborators)
- Integrate Qobuz similar artists API
- Add ensure_vector() with TTL for lazy building
- Add resolve_qobuz_to_mbid() for ID resolution via name search
- 21 tests passing (sparse_vector, store, weights, builder)
- Add FlatpakWelcomeModal component shown once for Flatpak users
- Add persistence for Flatpak welcome shown state (updates.db)
- Implement sequential modal priority: Flatpak > What's new > Update available
- Add Flatpak section to Settings navigation (only when running in Flatpak)
- Clean up duplicate Flatpak section and invalid style block in SettingsView
- Add copyable command styling for Flatpak section
Backend:
- Emit 'library:tag_write_progress' events with current/total count
- Progress updates as each file is written
Frontend:
- Listen for progress events during direct write mode
- Save button shows "Writing X of Y..." dynamically
- Cleanup listener on completion or error
- Remove duplicate discogs_release_to_search_result function
- Use existing discogs_extended_to_search_result which properly maps:
year, country, label, catno, format
- MusicBrainz already maps label_info with catalog_number
Backend:
- Add media/track-count fields to MusicBrainz ReleaseResult model
- Extract track_count and format from search results
Frontend:
- Show "{n} results" or "No results, try different search"
- Auto-expand results panel when search returns data
- Track hasSearched state for proper status display
Phase 4 of service integration:
- Add provider adapters (MusicBrainz, Discogs to unified DTOs)
- Add RemoteMetadataState with search_albums and get_album_metadata
- Create remote_metadata commands module with Tauri handlers
- Wire up state and commands in lib.rs
Available commands:
- remote_metadata_search: Search albums on MusicBrainz or Discogs
- remote_metadata_get_album: Get full album metadata with tracks
- remote_metadata_cache_stats: Get cache statistics
- remote_metadata_clear_cache: Clear the in-memory cache
Phase 3 of service integration:
- Add DiscogsReleaseMetadata struct with full album info
- Add DiscogsArtist, DiscogsLabel, DiscogsTrack structs
- Add DiscogsSearchResultExtended for detailed search results
- Add search_releases() for release search with extended metadata
- Add get_release_metadata() for full release with tracklist
- Add ReleaseFullResponse model with media, tracks, and tags
- Add Medium, MediumTrack, TrackRecording, Tag models
- Add get_release_with_tracks() for fetching full release details
- Add search_releases_extended() with configurable limit and catalog number
- Export new types from module
Provides instructions and a UI section to guide users on granting filesystem access and network sharing permissions to the Flatpak version of the app.
Removes redundant home directory permission in tauri config.