Commit Graph
425 Commits
Author SHA1 Message Date
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
vicrodh ee31c3e445 feat(playlist): add ability to copy Qobuz playlists to user library
When viewing a playlist not owned by the current user, a new "Copy to
My Library" button appears between the favorite and context menu buttons.
Clicking it creates a copy of the playlist with all its tracks in the
user's library.

- Add get_current_user_id command to check playlist ownership
- Add subscribe_playlist command to copy playlist with all tracks
- Add ListPlus button in PlaylistDetailView for non-owned playlists
- Button only shows for playlists owned by others (e.g., Qobuz curated)
2026-01-25 01:34:43 -06:00
vicrodh 49a0a4223e fix(db): Move folder_id index creation to after migrations
The index was being created during schema execution, but for existing
databases the column doesn't exist until the migration runs. Now the
index is created after all migrations have completed.
2026-01-24 23:38:57 -06:00
vicrodh e5779ce6cf feat(backend): Add playlist folders Phase 1 - database and Tauri commands
- Add uuid crate dependency for folder ID generation
- Add PlaylistFolder struct and database schema
- Add folder_id field to PlaylistSettings
- Add database migration for existing installations
- Add CRUD methods for playlist folders:
  - create_playlist_folder
  - get_all_playlist_folders
  - get_playlist_folder
  - update_playlist_folder
  - delete_playlist_folder
  - reorder_playlist_folders
  - move_playlist_to_folder
  - get_playlists_in_folder
- Register Tauri commands for frontend integration
2026-01-24 23:15:48 -06:00
vicrodh 270465e822 fix(share): implement proper album.link resolution via iTunes lookup
- Add UPC field to Album models (Rust and TypeScript)
- Enhance share_album_songlink command to use iTunes lookup:
  - First tries UPC lookup via iTunes API
  - Falls back to Odesli API with UPC
  - Then tries album title/artist search on iTunes
  - Generates direct album.link/i/{id} URLs like tracks do
- Update frontend to use the backend command instead of
  generating non-functional URL-based links

This mirrors the track sharing approach that uses ISRC/iTunes
lookup to generate working song.link URLs.
2026-01-24 12:54:59 -06:00
vicrodh e86734c9a7 fix(tray): restore window properly from tray menu
Add unminimize() call to show_hide menu handler to match left-click
behavior. Also add logging for debugging visibility state.
2026-01-23 16:49:33 -06:00
vicrodh 077aa01356 feat(settings): add system tray settings
Add tray icon configuration options:
- Enable/disable tray icon (requires restart)
- Minimize to tray: hide window when clicking minimize
- Close to tray: hide window instead of quitting

Rust backend: TraySettingsState with SQLite persistence
Frontend: SettingsView tray subsection with toggles

Related to #15 (title bar settings)
2026-01-23 16:38:17 -06:00
vicrodh 6d5d264778 refactor(backend): rename module and APIs for compliance
- Renamed download_cache module to offline_cache
- Updated Tauri command names (e.g., cache_track_for_offline)
- Updated event names (offline:caching_started, etc.)
- Updated library database function names
- Updated frontend invoke calls to use new command names
2026-01-23 12:14:39 -06:00
vicrodh 712c7be4f7 fix(player): add delay after engine stop to prevent sink contention
Add 50ms delay after stopping the previous playback engine to allow
the rodio Sink to fully release its reference to the OutputStreamHandle.
This prevents "resource busy" errors when rapidly switching tracks,
especially when selecting from search while radio is playing.
2026-01-23 10:17:58 -06:00
vicrodh 1684df7752 fix(queue): complete album_id and artist_id propagation for track navigation
Ensure album_id and artist_id are passed through all queue track creation paths:
- trackActions.ts build functions (Qobuz, album, playlist, local tracks)
- Session persistence (both frontend and Rust backend)
- buildAlbumQueueTrack wrapper function
- Session restoration
2026-01-23 03:56:14 -06:00
vicrodh 2ea767f37c feat(radio): implement 50-track initial queue and auto-refill support
Radio improvements:
- Increase initial tracks from 15 to 50
- Add refill_radio_queue command (generates 20 more when queue low)
- Add get_queue_remaining command to check queue size
- Add create_infinite_radio command for "Keep Playing" feature
- Add InfiniteRadio autoplay mode based on last 5 tracks

Backend infrastructure ready for:
- Auto-refill when queue drops below 10 tracks
- Infinite playback when normal queue ends
2026-01-23 03:45:51 -06:00
vicrodh aabbe8ea67 fix(radio): pass album_id and artist_id for track navigation
Add album_id and artist_id fields to QueueTrack struct so that
radio tracks have proper navigation metadata. This enables
clicking artist/album in the main player to navigate correctly
when playing tracks from artist or track radio.

Changes:
- Add album_id/artist_id to Rust QueueTrack struct
- Update track_to_queue_track to extract IDs from Qobuz Track
- Add fields to frontend BackendQueueTrack interface
- Pass IDs in ArtistDetailView radio playback
- Pass IDs in playQueueTrack to playTrack service
2026-01-23 03:14:42 -06:00
vicrodh 9504294907 fix: remove track limit completely from library search
- Backend now supports limit=0 meaning "no limit"
- Frontend passes limit=0 to fetch all tracks
- Search now works across entire library regardless of size
- Virtualization handles rendering efficiently
2026-01-23 01:53:37 -06:00
vicrodh 58badcc64b fix: improve app identification in task managers
Set GTK_APPLICATION_ID for proper WM_CLASS and add desktop template
with StartupWMClass for correct process identification on Linux.
Also add window label for Tauri window configuration.
2026-01-23 01:50:27 -06:00
vicrodh 66d6ffb6ed feat: add canonical artist name normalization
- Add canonical_name column to artist_images table (migration)
- Store canonical names from Qobuz when fetching artist images
- Display canonical names (properly capitalized) in artist views
- Group and sort artists by canonical name when available
- Source priority: Qobuz -> Discogs -> Metadata

This ensures "Alice in chains" displays as "Alice In Chains" based on
Qobuz's authoritative naming.
2026-01-23 01:35:55 -06:00
vicrodh eec512a3f9 fix: address UI and performance issues in LocalLibrary
- Fix asset protocol scope for thumbnails in tauri.conf.json
- Homologate artist view controls to match album controls pattern
- Make artist grouping optional with dropdown (Off, A-Z)
- Change grid/list to single toggle button for artists
- Allow 3 lines for artist names in grid view
- Fix album card width to prevent title overflow
- Add throttling to artist image fetch to avoid rate limiting
2026-01-23 01:20:55 -06:00
vicrodh fc03bee9b1 feat(thumbnails): add backend thumbnail generation system 2026-01-23 01:07:16 -06:00
vicrodh b86effe4e9 perf(library): optimize artists and search queries
Extend SQL-based filtering to artists and search:
- Add get_artists_with_filter() method that filters source and
  network folders directly in SQL
- Replace library_get_artists N+1 loop that was calling
  db.search(artist.name, 10000) for EACH artist
- Add search_with_filter() method for optimized track search
- Replace library_search post-query filtering with SQL predicates

These changes eliminate thousands of redundant database queries
when browsing large libraries with network folders or Qobuz downloads.

Part of #13 - LocalLibrary performance optimization
2026-01-22 23:56:42 -06:00