Commit Graph
442 Commits
Author SHA1 Message Date
vicrodh 541c93f06d fix(suggestions): fix config deserialization and effect loop
- Add #[serde(default)] to SuggestionConfig to allow partial config
  from frontend (fixes "missing field max_artists" error)

- Fix effect loop in PlaylistSuggestions.svelte:
  - Add loadingInitiated flag to prevent re-triggering
  - Add cleanup effect when playlistId changes
  - Prevent concurrent loading attempts
2026-01-27 12:07:08 -06:00
vicrodh 1560cd4e3d debug(suggestions): add detailed timing logs for debugging
Frontend logging:
- Log when effect triggers with artist count
- Log when backend call starts/finishes with timing
- Log result details (tracks, artists counts)

Backend logging (playlist_suggestions.rs):
- Log MBID resolution with cache hits/misses/failures
- Log config values
- Log engine call timing

Engine logging (suggestions.rs):
- Step 1: ensure_vector timing per artist
- Step 2: playlist vector computation
- Step 3: find_nearest timing
- Step 4: track search timing per artist
- Early exit when pool size reached
2026-01-27 12:02:31 -06:00
vicrodh e9a5dfcb80 perf(suggestions): optimize loading with backend MBID resolution
- 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
2026-01-27 11:58:54 -06:00
vicrodh cadb346e32 feat(suggestions): add vector-based playlist suggestions engine
- Add suggestions.rs with SuggestionsEngine for generating suggestions
- Add SuggestionConfig with configurable parameters
- Add SuggestedTrack and SuggestionResult types
- Integrate with MusicBrainz relationships and Qobuz track search
- Add Tauri commands: get_playlist_suggestions_v2, get_vector_store_stats
- Add cleanup_vector_store command for maintenance
- Add Serialize to StoreStats for IPC
- Register ArtistVectorStoreState in app initialization
- 23 tests passing
2026-01-27 11:38:56 -06:00
vicrodh f3dcb6ebf2 feat(vectors): add vector builder with MB and Qobuz sources
- 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)
2026-01-27 11:30:06 -06:00
vicrodh 1d2e28c1a8 feat(vectors): add sparse vector foundation for playlist suggestions
Phase 1 of playlist-suggestions feature:
- SparseVector: memory-efficient vector with math operations
  - set/get with sorted indices for O(log n) access
  - add, sub, scale, dot product, magnitude
  - normalize, cosine_similarity, top_k_similar
- ArtistVectorStore: SQLite persistence
  - artist_index table: MBID to integer index mapping
  - vector_entries table: sparse representation (one row per non-zero)
  - vector_metadata table: track freshness with TTL
  - find_nearest: cosine similarity search with exclusion
- 17 unit tests all passing
2026-01-27 11:19:55 -06:00
vicrodh b3117cc7c2 Bump version to 1.1.7 2026-01-27 04:58:28 -06:00
vicrodh 561f7487d0 Add Flatpak welcome modal and sequential modal priority system
- 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
2026-01-27 04:37:17 -06:00
vicrodh b664a5afd0 Change defaults: streaming ON, check for updates OFF 2026-01-27 03:59:32 -06:00
vicrodh 54740ee225 feat(updates): add update checks and what's new modal
cx
2026-01-27 02:59:46 -06:00
vicrodh 426584aa59 feat(backend): add custom track order persistence for playlists
- Create playlist_track_custom_order table for storing user-defined track positions
- Add database functions: get, init, set, move track, has, clear custom order
- Add Tauri commands for frontend access
- Migration-safe for existing databases

cc
2026-01-26 21:25:25 -06:00
vicrodh 65adf352b6 feat(TagEditor): show real-time progress when writing tags
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
2026-01-26 20:46:38 -06:00
vicrodh e0d2439f56 fix: correct Discogs adapter to use extended search result
- 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
2026-01-26 20:30:11 -06:00
vicrodh fd08dc8f2c feat(TagEditor): add track count and descriptive result status
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
2026-01-26 20:14:33 -06:00
vicrodh dddb4d4fd1 fix: resolve type mismatches and add Phase 6 polish
- Fix RemoteTrackMetadata field types (disc_number, track_number as u8)
- Fix RemoteAlbumMetadata fields (genres, tracks as Vec, disc_count as u8)
- Fix cache method calls (async, proper signatures)
- Fix error variant construction (ProviderUnavailable, InvalidProviderId)
- Add track count mismatch warning in UI
- Add rate limit error message handling
- Add 'Open in browser' button for selected result
2026-01-26 18:55:05 -06:00
vicrodh 6d20baf914 feat: add remote metadata service and Tauri commands
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
2026-01-26 18:46:29 -06:00
vicrodh 25e877181a discogs: add search_releases and get_release_metadata methods
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
2026-01-26 18:42:41 -06:00
vicrodh b8340ad6c7 musicbrainz: add get_release_with_tracks with media/tracks support
- 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
2026-01-26 18:39:47 -06:00
vicrodh 70df504af3 library: add remote_metadata module structure and unified DTOs
- Add RemoteProvider enum (MusicBrainz, Discogs)
- Add RemoteAlbumSearchResult for lightweight search results
- Add RemoteAlbumMetadata for full metadata with tracks
- Add RemoteTrackMetadata for track info
- Add RemoteMetadataCache with TTL-based expiration
- Add adapter functions for MusicBrainz and Discogs
- Add Discogs position/duration parsing utilities
2026-01-26 18:37:45 -06:00
vicrodh 5d812f4966 feat(library): add LocalLibrary tag editor backend
cx
2026-01-26 13:24:51 -06:00
Victor RH 19aa5ca4a1 Adds Flatpak sandbox instructions
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.
2026-01-26 11:34:32 -06:00
vicrodh 2f4b79f1bf fix(cast): cleanup Chromecast connection on app exit 2026-01-26 11:01:14 -06:00
vicrodh 1384a1d276 feat(cast): add position tracking and auto-advance for Chromecast
- 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
2026-01-26 10:41:35 -06:00
vicrodh 71ae2f92c1 feat(dlna): add position tracking and seekbar support
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
2026-01-26 09:41:11 -06:00
vicrodh 003468a28e fix(dlna): use fixed port 9876 and add extensive logging
CHANGES:
- MediaServer now uses fixed port 9876 (falls back to random if busy)
  This improves firewall compatibility for DLNA casting
- Added timeout (10s) for SOAP actions to prevent deadlocks
- Added extensive logging throughout DLNA flow:
  - Command entry point logging
  - MediaServer lifecycle (start, thread, requests)
  - SOAP action responses
  - Content-type and DIDL metadata details

VERIFIED WORKING:
- DLNA device discovery ✓
- Connection to gmediarender ✓
- SetAVTransportURI SOAP action ✓
- Play SOAP action ✓
- MediaServer serving audio to DLNA renderer ✓

Files modified:
- src-tauri/src/cast/dlna/device.rs
- src-tauri/src/cast/dlna/commands.rs
- src-tauri/src/cast/media_server.rs
2026-01-26 09:30:00 -06:00
vicrodh 892341b773 fix(dlna): use dynamic protocolInfo and add debug logging
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
2026-01-26 07:56:08 -06:00
vicrodh 078f33085f fix: check studio albums count, not total albums
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"
2026-01-25 23:14:59 -06:00
vicrodh fbc1938838 feat: compare studio albums only when matching artists
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.
2026-01-25 23:08:46 -06:00
vicrodh c6c18460d0 fix: handle "The" prefix variations in artist matching
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.
2026-01-25 22:37:37 -06:00
vicrodh 74d2059b8a fix: use correct fields from AlbumSummary in musician appearances
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
2026-01-25 22:18:15 -06:00
vicrodh fc138d801c feat(backend): add musician resolution types and commands
Phase 1 of Musician Page implementation:

Types (musicbrainz/models.rs):
- MusicianConfidence enum (Confirmed/Contextual/Weak/None)
- ResolvedMusician struct with confidence assessment
- AlbumAppearance and MusicianAppearances for 'Appears On'

Commands (commands/musician.rs):
- resolve_musician: Determines confidence level and destination
- get_musician_appearances: Returns deduplicated album appearances

Confidence logic:
- Level 3 (Confirmed): Has Qobuz artist page with albums
- Level 2 (Contextual): Appears in 3+ album credits
- Level 1 (Weak): Sparse Qobuz results
- Level 0 (None): No safe match
2026-01-25 22:06:16 -06:00
vicrodh 8271cb2436 fix(musicbrainz): don't separate members by ended status
- 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
2026-01-25 20:38:44 -06:00
vicrodh dfbf64b1a4 fix(musicbrainz): correct member/group relationship logic
When viewing a BAND: direction=backward means related is a MEMBER
When viewing a PERSON: direction=forward means related is a GROUP

cc
2026-01-25 20:11:44 -06:00
vicrodh 4fdb61978c fix(alsa-direct): implement proper pause/resume support
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.
2026-01-25 18:28:39 -06:00
vicrodh 008c5623cb feat(streaming): add seekbar progress and dynamic buffer sizing
- 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.
2026-01-25 18:06:41 -06:00
vicrodh 7d0aa215a5 feat(streaming): implement incremental decoding for true streaming playback
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
2026-01-25 13:50:24 -06:00
vicrodh 36b3a4da56 refactor(streaming): remove unused writer field from PlayStreaming command
The download task is spawned before the command is sent, so the writer
doesn't need to be included in the command payload.

cpvsc
2026-01-25 12:41:08 -06:00
vicrodh 39dd249f98 feat(streaming): integrate streaming playback in play_track command
- 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
2026-01-25 12:39:43 -06:00
vicrodh 5519fc7b25 feat(streaming): add BufferedMediaSource for streaming playback
- 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
2026-01-25 12:25:41 -06:00
vicrodh 1d0e069be1 feat(config): add stream_first_track setting to AudioSettings
Add two new fields to AudioSettings struct:
- stream_first_track: bool flag to enable streaming for uncached tracks
- stream_buffer_seconds: u8 for initial buffer size (1-10 seconds)

Includes:
- Database schema migration for existing users
- Getter/setter methods in AudioSettingsStore
- Tauri commands for frontend integration
- Registration in lib.rs invoke_handler

cpvsc
2026-01-25 12:20:47 -06:00
vicrodh 2136a2a0e7 feat(search): add search type filters for refined results
Backend:
- Add search_type parameter to search_albums, search_tracks, search_artists
- Pass optional type filter to Qobuz API (Performer, Composer, Label, ReleaseName)

Frontend:
- Add SearchFilterType to search state management
- Add filter radio buttons in SearchView (visible when results exist)
- Remove redundant counts from tabs
- Add Clear filter button independent from Clear search
- Filters reset automatically when starting new search or clearing

Filters allow finding performers, composers, and labels that default
search misses (e.g., searching "Nicko McBrain" with Performer filter
shows Iron Maiden tracks)
2026-01-25 12:04:49 -06:00
vicrodh 6467ca038e feat: add LabelView to browse albums by record label
- Add get_label API endpoint and Tauri command
- Create LabelView component with album grid, search, and infinite scroll
- Add clickable label links in AlbumDetailView, AlbumCreditsModal, and TrackInfoModal
- Add label_id to AlbumInfo and AlbumDetail types
- Add label to QobuzTrack album summary for track info display
2026-01-25 11:28:51 -06:00
vicrodh 04197b98f0 feat(musicbrainz): add smart playlist generation backend (Stage 4)
- Add PlaylistRule enum with BandMembers, ArtistGroups, CollaboratorNetwork variants
- Add GeneratedPlaylist struct for playlist preview results
- Add PlaylistGenerationConfig for customizing track limits
- Implement smart_playlist_preview command to generate playlists from MusicBrainz relationships
- Implement smart_playlist_resolve_artist helper to find artist MBID by name
- Implement smart_playlist_get_available_types to check what playlist types are available
- Search Qobuz for member/collaborator tracks and return matching track IDs
2026-01-25 09:51:47 -06:00
vicrodh 23d208b3d0 feat: implement ListenBrainz integration (Stage 2)
Add ListenBrainz scrobbling support with:
- Client module for ListenBrainz API
- Token-based authentication (user provides token from listenbrainz.org)
- Now-playing submission support
- Scrobble submission with MusicBrainz enrichment
- SQLite-based offline queue with TTL
- Credential persistence across sessions
- Settings UI for connection management

API integration uses Stage 1 MusicBrainz data to enrich scrobbles with
recording MBID, release MBID, and artist MBIDs when available.

cc
2026-01-25 06:49:40 -06:00
vicrodh 55693e66ba feat(musicbrainz): add MusicBrainz integration engine (Stage 1)
- Add MusicBrainz client with rate limiting (1 req/sec)
- Add SQLite cache with TTL for recordings, artists, releases
- Add Tauri commands for entity resolution
- Support ISRC, UPC/barcode, and name-based lookups
- Include artist relationship extraction (Stage 3 prep)

cc
2026-01-25 06:33:35 -06:00
vicrodh 6eff22632a feat(credits): complete Album Credits Modal with Review tab
- Use custom info SVG icon without circle outline
- All tracks collapsed by default (removed auto-expand)
- Add description field to Album model for editorial reviews
- Implement Review tab with album description content
- Tab switches between Credits (track list) and Review (description)
- Review tab disabled when no description available
2026-01-25 03:09:53 -06:00
vicrodh 3410774f32 feat(credits): add Album Credits Modal (Phase 4)
- Add get_album_credits Tauri command with TrackCredits, AlbumCredits,
  and AlbumInfo structs for consolidated album/track credits view
- Create AlbumCreditsModal component with two-column layout:
  album info (artwork, metadata, quality badges) and collapsible
  track list with performers and copyright
- Add Info button to AlbumDetailView header actions
- Integrate modal in +page.svelte with track play support
2026-01-25 02:53:46 -06:00
vicrodh e767809857 feat(track-info): add TrackInfoModal and context menu option
Phase 2 of track-info feature:
- Add performers, composer, copyright fields to Track model
- Add get_track_info command with parsed performers
- Create TrackInfoModal component to display track details and credits
- Add "Track info" option to TrackMenu context menu

cc
2026-01-25 02:23:42 -06:00
vicrodh eccadb6af9 feat(api): add performers string parser
Parse Qobuz performers field into structured data.
Format: "Name, Role1, Role2 - Name, Role1 - ..."

cc
2026-01-25 02:17:24 -06:00
vicrodh dcbe7e802a feat(playlist): track copied playlists and add attribution
- Hide "Copy to My Library" button after playlist has been copied
- Persist copied playlist IDs in localStorage for subsequent visits
- Add attribution note to description: "Originally curated by X on Qobuz"
2026-01-25 01:39:32 -06:00