- Quality badge now has border (alpha-15) and background (alpha-10)
- Added 5px top margin for separation
- Title and subtitle now use ticker animation on hover for long text
- Animation matches AlbumCard behavior (scroll left then back)
- Card now has background like artist-card (bg-secondary, rounded)
- Artwork reduced to 148x148 with 6px padding around
- Quality badge moved to bottom of card (outside artwork)
- Genre only visible on hover overlay with slide-in animation
- Text centered and sized down for consistency
- Quality badge now at bottom-right with lighter background (0.55 opacity)
- Genre badge at top-left for albums (tracks don't have genre data)
- Both badges use consistent styling with blur backdrop
- New popular-card with square image, title, and subtitle
- Hover overlay with heart, play, and menu buttons
- Dropdown menu with play controls and navigation options
- Track cards show "Go to album", album cards show "Go to artist"
- Artists still use the existing circular card design
- Make auth_token() public for catalog/search endpoint authentication
- Fix setSearchQuery to skip notification when value unchanged
- Fix setSearchState to only notify listeners when query actually changed
- Add title case transformation for better most_popular API results
- Add stale result detection to ignore outdated search responses
- Preserve most_popular when API returns inconsistent results
- Clean up debug logging
Replace heuristic-based most popular detection with Qobuz's actual
most_popular field from the catalog/search endpoint.
- Add CATALOG_SEARCH endpoint path
- Make app_id() and get_http() public on QobuzClient
- Add MostPopularItem enum with tagged serialization (artists/albums/tracks)
- Update search_all to use catalog/search and parse most_popular field
- Update SearchView template to use allResults.most_popular with new types
- Add MostPopularItem type to searchState store
The Most Popular result in search now intelligently determines
the best match based on query:
- Exact name match: artist > album > track
- Starts with match: artist > album > track
- Fallback: first artist, then album, then track
Added track card display for when a track is the most popular result.
- Update "Streaming Only" description to be less technical:
"Disables temporary cache. Not recommended without a fast connection.
Offline library tracks always play first."
- Fix account card vertical centering with explicit align-self on
avatar, info container, and logout button
Adds UI toggle for streaming_only mode that skips L1/L2 cache writes.
When enabled, tracks are streamed fresh each time but offline cache
still works for downloaded tracks (local-first principle).
Add streaming_only setting that skips L1+L2 cache writes while still
respecting offline cache (local-first). When enabled:
- download_and_stream() skips final cache.insert()
- Standard download path skips cache.insert()
- Offline cache continues to work normally
This reduces storage usage for users who prefer pure streaming.
- Clear both L1 memory and L2 disk caches when quality setting changes
This ensures cached tracks at wrong quality are removed
- Fix device name not showing in popup on first hover
Now refreshes status on each hover instead of only when null
- Add quality downgrade indicator when stream quality is lower than
track's original quality (shows yellow arrow with tooltip)
- Store original track quality values for comparison with actual stream
Issue #34 - Display actual stream quality, not track metadata
Backend changes:
- Add sample_rate and bit_depth to PlaybackEvent
- Add stream quality tracking to SharedState
- Extract bit_depth from FLAC/M4A headers in streaming path
- Player sets stream quality when decoding audio
Frontend changes:
- Update PlaybackEvent interface with new fields
- Update currentTrack with actual stream quality from backend
This allows the UI to show the actual quality being played (e.g., 44.1kHz
when streaming a 192kHz track at CD Quality) instead of the track's
maximum advertised quality.
Issue #34 - Max quality setting not respected
Changes:
- Remove 'Prefer Highest Available' toggle (was dead code, conflicted with quality dropdown)
- Clear playback cache (L1+L2) when streaming quality changes
- Show toast notification when cache is cleared
- Add description to streaming quality setting
- Update i18n for new messages (en/es)
This ensures the quality setting takes effect immediately, important for:
- Users with metered internet connections
- Users with hardware sample rate limitations (e.g., DACs limited to 96kHz)
The Qobuz API respects format_id and downsamples server-side, so this
change guarantees the correct quality is fetched for new streams.
- Log requested vs returned format_id and sample_rate in get_stream_url_with_fallback
- Log quality_str, parsed quality, and format_id in play_track and prefetch_track commands
- Add console.log in getStreamingQuality for frontend debugging
This will help diagnose why 192kHz is played despite lower quality settings.
- Add branch workflow section explaining pre-release integration
- Clarify that all PRs must target pre-release, not main
- Update maintainer procedure for new workflow
- Add release procedure section
- Add TrackReplacementModal to search for replacement tracks
- Add onFindReplacement action to TrackMenu context menu
- Show replacement option only for owned playlists with unavailable tracks
- Replacement searches by track title + artist
- Shows quality info (bit depth, sample rate) to help user choose
- Supports preview playback before confirming
- Replace old track with new one in playlist
Also fix tooltip for unavailable tracks to show a complete message
instead of "is no longer available" (now shows "Track no longer
available in Qobuz library")
- Add isUnavailable prop to TrackRow to show AlertCircle instead of track number
- Show unavailable icon only for user's own playlists (not Qobuz official playlists)
- Gray out unavailable tracks in both owned and non-owned playlists
- Remove all interactions for unavailable tracks in non-owned playlists
- Keep only "remove from playlist" option for unavailable tracks in owned playlists
- Create unavailableTracksStore for persisting unavailable track IDs to localStorage
- Load unavailable tracks on app startup in bootstrap.ts
- Auto-mark tracks as unavailable when playback fails with TrackUnavailable error
- Add streamable field to PlaylistTrack and DisplayTrack interfaces
- Add isTrackRemovedFromQobuz() check for tracks with streamable: false
- Show red AlertCircle icon for unavailable tracks
- Gray out and apply grayscale filter to unavailable track rows
- Limit menu actions to only 'Remove from Playlist' for removed tracks
- Hide favorite and download buttons for removed tracks
- Keep wrapper interactive for context menu access
Backend:
- Add TrackUnavailable error type for tracks removed from Qobuz
- Validate stream URL response - detect missing URL with restrictions
- Add SampleRestrictedByRightHolders to restriction check
- Return specific error when track is unavailable vs quality unavailable
Frontend:
- Detect track unavailable errors in playTrack()
- Show friendly toast: Track title is no longer available
- Auto-skip to next track in queue after 500ms delay
- Add i18n translations (en/es) for trackUnavailable message
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
Digital outputs like S/PDIF transmit audio as-is without DA/AD conversion,
making them bit-perfect by nature. Now iec958: devices get the BP badge.
Updated group label to "Bit-perfect (Hardware / Digital)" to reflect
both direct hardware access (hw:) and digital outputs (iec958:).
- 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)
- New DeviceDropdown component with grouped options:
- Defaults (System Default, default ALSA)
- Bit-perfect (Auto-detected) - hw:X,Y devices with BP badge
- Plugin Hardware - plughw:X,Y devices
- Other Outputs - remaining devices
- Only shows for ALSA Direct backend, other backends use regular dropdown
- Pure UI change, no backend modifications
- Artist names in sidepanel now animate with ticker effect on hover when text overflows
- Each album section (Discography, EPs & Singles, Live Albums) now has a sort dropdown
- Sort options: Default, Newest, Oldest, A-Z, Z-A
- Use sorted arrays for rendering instead of raw grouped arrays
- Group albums by category: Discography, EPs & Singles, Live Albums
- Exclude tributes and compilations (others) from display
- Each section has its own header that scrolls with content
- Updated footer: "To view compilations and other content, go to..."
- All content scrolls together (sections + footer)
- Add no-outer-scroll class when sidepanel mode is active
- Set up proper flex container hierarchy for full height:
- .favorites-view becomes flex column container
- .content gets flex: 1 with min-height: 0
- ViewTransition wrapper gets flex: 1 via :global selector
- Fix negative margins to match parent padding (was -32px, now -8px/-18px)
- Reduce artist column width to 240px (matches LocalLibrary)
- Fix album column right padding to 8px