- 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.
- Add cast_get_position command for Chromecast media position
- Add CastPositionInfo struct with position, duration, player_state, idle_reason
- Extend position polling to work with both DLNA and Chromecast
- Detect track end on Chromecast via IDLE state with FINISHED reason
- Unify polling logic for both protocols
cpvsc
DLNA POSITION TRACKING:
- Added GetPositionInfo SOAP action to query current playback position
- Added GetTransportInfo SOAP action to get transport state (PLAYING/PAUSED/STOPPED)
- New dlna_get_position command returns position, duration, and transport state
- Added parse_time_string() helper for HH:MM:SS format
FRONTEND INTEGRATION:
- castStore: Added position polling (1s interval) when connected to DLNA
- castStore: New getCastPosition() for UI components
- castStore: Automatic polling start/stop on track play/disconnect
- playerStore: Subscribes to castStore for position updates
- playerStore: Updates currentTime from cast position when casting
This enables the seekbar to display accurate position during DLNA playback.
Seek functionality was already implemented (dlna_seek command).
Files modified:
- src-tauri/src/cast/dlna/device.rs
- src-tauri/src/cast/dlna/commands.rs
- src-tauri/src/cast/dlna/mod.rs
- src-tauri/src/lib.rs
- src/lib/stores/castStore.ts
- src/lib/stores/playerStore.ts
PROBLEM:
- DLNA device detection and connection worked
- Audio playback on DLNA devices did not work
ROOT CAUSE:
- DIDL-Lite metadata had hardcoded protocolInfo='audio/flac'
- Many DLNA devices silently reject content when protocolInfo
doesn't match the actual MIME type
- No debug logging to diagnose SOAP action failures
FIX:
- Pass actual content_type from Qobuz stream to DIDL builder
- Generate protocolInfo dynamically based on real MIME type
- Add extensive debug logging for:
- MediaServer URL generated
- Content-Type from Qobuz
- Complete DIDL-Lite metadata
- SOAP action responses (SetAVTransportURI, Play)
- Error details when actions fail
Files modified:
- src-tauri/src/cast/dlna/device.rs
- src-tauri/src/cast/dlna/commands.rs
The previous logic checked total album count (< 10) to decide whether
to compare "X" vs "The X" variants. This failed for artists like
"Traveling Wilburys" (19 total albums, but 0 studio albums).
Now we:
1. Always count studio albums for the initial match
2. If studio albums < 10, search for "The X" variant
3. Compare studio album counts
Example fix:
- "Traveling Wilburys": 19 total, 0 studio albums
- "The Traveling Wilburys": 17 total, 6 studio albums
- Now correctly prefers "The Traveling Wilburys"
When comparing "X" vs "The X" variants, now fetches actual albums
and counts only studio albums (main discography) instead of total
album count. This filters out:
- Tributes (albums by other artists)
- Compilations (greatest hits, best of)
- Unofficial releases (bootlegs, broadcasts)
- Live albums
- EPs and singles
Uses the same categorization logic as the frontend (qobuzAdapters.ts).
This provides cleaner matching, e.g., "The Beatles" with 200+ studio
albums vs "Beatles" tribute compilation with 2 albums.
MusicBrainz often returns names without "The" prefix (e.g., "Beatles",
"Traveling Wilburys") but Qobuz has both variants, with the prefixed
version being the legitimate artist.
New matching logic:
1. Search for exact name match first
2. If no exact match, check for "The X" variant in results
3. If match has <5 albums, search for "The X" specifically and compare
4. Prefer the variant with more albums (likely the real artist)
This fixes navigation to "The Beatles", "The Traveling Wilburys", etc.
AlbumSummary only has id, title, image, and label fields.
The code was incorrectly trying to access artist and
release_date_original which only exist on the full Album struct.
- Use track.performer for artist_name instead of album.artist
- Set year to None since AlbumSummary doesn't have release_date
- Fix image access since ImageSet is direct field, not Option
- Fix title access since String is direct field, not Option
- All band members go to single 'Members' list
- Show ended status with muted color
- Tooltip shows roles + period (e.g. 'guitar (1981 - 2003)')
- Remove separate 'Former Members' section
cc
The playback thread was terminating on pause instead of waiting.
Now uses two separate flags:
- is_playing: controls pause/resume (thread waits when false)
- should_stop: controls full termination (thread exits when true)
This allows the ALSA Direct playback thread to pause and resume
without losing its position or requiring a full restart.
Bit-perfect audio path remains unchanged - only control flow modified.
- Pass track duration from frontend to streaming playback for seekbar
- Add StreamInfo struct with speed measurement during probe
- Implement StreamingConfig::from_speed_mbps() for adaptive buffer sizing
- Add play_streaming_dynamic() method using speed-based buffer config
- Enhanced download logging with progress %, speed (current/avg), and ETA
- Auto-cache streamed tracks for instant replay
The seekbar now shows playback progress during streaming by using
the track's known duration from metadata rather than detecting it
from incomplete audio data.
BREAKING CHANGE: Replace blocking decode_streaming_source with IncrementalStreamingSource
Key improvements:
- IncrementalStreamingSource implements rodio::Source and decodes packets on-demand
- Playback starts immediately after initial buffer (512KB default) fills
- Audio is decoded incrementally while download continues in background
- Reduced initial buffer from 2MB to 512KB for faster startup
- Added fast_start() config option for minimal 256KB buffer
The previous implementation blocked until the ENTIRE file was downloaded
and decoded, making streaming slower than traditional download-first.
Now playback truly starts within ~1 second of initial buffer filling.
Signed-off-by: cpvsc
- Add PlayStreaming variant to AudioCommand enum
- Add play_streaming() method to Player for starting stream-first playback
- Modify play_track command to check stream_first_track setting
- Add get_stream_info() for HEAD request to get content-length
- Add extract_audio_format_from_header() to probe audio format from initial bytes
- Add download_and_stream() for async chunked download with buffer push
- Update BufferedMediaSource with create_reader() for independent read positions
- Add has_min_buffer() method for playback readiness check
- Implement Clone for BufferWriter
Phases 3-4/7: Conditional streaming path with async download integration
cpvsc
- Create streaming_source.rs with BufferedMediaSource implementation
- Implements Read + Seek + MediaSource traits for symphonia compatibility
- BufferWriter allows async chunk pushing from HTTP downloads
- StreamingConfig with configurable min_buffer and max_buffer sizes
- Supports seeking within buffered range and EOF detection
- Thread-safe with Arc<Mutex<>> interior for shared state
- Includes comprehensive unit test suite
Phase 2/7: Core streaming infrastructure for first-track playback
cpvsc