- Fix playing indicator to consider isPlaybackActive (not just activeTrackId)
- Track now shows as playing only when actually playing, not when paused
- Change reorder controls from vertical to horizontal layout
- Homologate UI with PlaylistManagerView (buttons beside drag handle)
cc
- Add checkbox selection for tracks in custom order mode
- Add batch controls bar with Select All, Clear, Move Up/Down
- Implement moveSelectedUp/Down to move multiple tracks as group
- Refactor PlaylistManagerView to use savePlaylistOrder helper
- Add CSS styles for batch controls and checkboxes
cc
- Add ChevronUp/ChevronDown buttons alongside drag handle in custom mode
- Implement movePlaylistUp/movePlaylistDown functions
- Extract savePlaylistOrder helper for code reuse
- Support both grid and list view layouts
- Add CSS styles for reorder controls
cc
- Add custom order state management in PlaylistDetailView
- Load custom order when entering custom sort mode
- Initialize custom order from current tracks if not exists
- Add move up/down buttons for track reordering
- Update sorting logic to use customOrderMap
- Add CSS styles for reorder controls
cc
- Extend DisplayTrack interface with label, addedIndex, customPosition fields
- Add PlaylistTrack.album.label type for record label data
- Add three new sort options: Added Recently, Label, Custom Order
- Implement sorting logic for new options
- Label sorting places tracks without label (local) at the end
- Added Recently uses original index as proxy for date added
cc
- Add showGenre prop (default true) to AlbumCard
- Make genre prop optional
- Hide genre and favorite button in LocalLibrary albums
- VirtualizedAlbumList passes showGenre={false}, showFavorite={false}
- Add spacer to keep Play button centered when favorite is hidden
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
- Show spinner-inline animation on Save button while saving
- Different text: "Writing tags..." for direct mode, "Saving..." for sidecar
- Provides visual feedback during potentially slow file operations
- Store full RemoteAlbumSearchResult instead of provider_id only
- Apply uses provider from selected result, not from dropdown
- Prevents mismatch when user changes provider after searching
- 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
- Results now scroll horizontally with snap behavior
- Each card is 50% width (min 200px) showing:
- Title (up to 2 lines)
- Artist
- Details pills: year, track count, country, format
- Label + catalog number at bottom
- Cards have better visual hierarchy with background
- More metadata visible to identify editions/re-releases
- Remove invalid React/JSX component (CopyableCommand) from SettingsView
- Replace JSX usage with proper Svelte 5 markup
- Fix import: use openUrl instead of open from plugin-opener
The style tag was indented with 2 spaces, causing PostCSS to fail
parsing (it saw JavaScript as CSS). Svelte requires the style tag
to be at root level with no indentation.
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
The virtualized-active class was being applied even when viewing album
detail, causing overflow:hidden to block scrolling. Added !selectedAlbum
check to only apply virtualized mode for list views.
- FocusMode: replace #000, white with theme variables
- miniplayer: replace #6366f1, #fff, #818cf8 with accent/text variables
- AlbumDetailView: replace #666666 with --text-muted
- LoginView: use --danger and --warning variables for error/timeout boxes
- PlaylistModal: use .icon-inline utility class
Add to app.css:
- Warning color variables (--warning, --warning-bg, --warning-border)
- .icon-inline and .badge-warning utility classes
- 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
- Add onCastDisconnected callback to castStore
- Reset isPlaying state when cast disconnects
- Ensures play indicator stops in UI when DLNA disconnects
cpvsc
- Add cast-aware badge that shows protocol name (DLNA/CAST/AIR)
- Show protocol-specific SVG icons in tooltip (dlna.svg, cast_audio.svg)
- Hide DAC passthrough and exclusive mode badges when casting
- Stop hardware polling when casting to avoid misleading info
- Add purple theme styling for cast badge consistent with cast UI
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
Changes the menu action label from "Play later" to "Add to queue"
for better clarity and consistency with standard music player UX.
Updated in:
- AlbumMenu.svelte
- TrackMenu.svelte
- ArtistDetailView.svelte (tracks context menu)
Relates to #21
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.
Show "Looking up {name}..." toast when clicking on musician links
to provide feedback while the backend resolves the musician.
Also show error toast if lookup fails.
Groups/bands in the sidebar now use the musician resolution flow.
Since bands typically have Qobuz artist pages, they resolve as
confidence=3 (Confirmed) and navigate directly to the artist page.
Wire up onMusicianClick for Members & Former and Collaborators
sections in ArtistDetailView sidebar. Clicking a member now triggers
the musician resolution flow (modal for weak confidence, musician
page for contextual, or artist page for confirmed).
Uses the first role from MusicBrainz relationships or defaults to
"Band Member" / "Collaborator" as the role parameter.
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
AlbumCreditsModal:
- Add onMusicianClick prop
- Update handlePerformerClick to pass name and role
- Falls back to onPerformerSearch if onMusicianClick not provided
TrackInfoModal:
- Add onMusicianClick prop
- Add handlePerformerClick function
- Pass role from groupedCredits to handler
+page.svelte:
- Pass handleMusicianClick to both modals
Full page view for musicians with confidence level 2 (Contextual):
- Header with musician name, role, and avatar
- Bands & Projects section (from MusicBrainz)
- Appears On section with album grid (Qobuz only)
- Pagination with Load More button
- Album cards with artwork, title, artist, year, and role badge
- Click album to navigate to album detail
Informational modal for musicians with confidence level 0-1:
- Shows musician name and primary role
- Lists known bands/projects (from MusicBrainz)
- Displays explanatory copy based on confidence
- Provides CTA to Artist Page if applicable
- Does NOT show albums, tracks, or deep navigation
- Closes on Escape key or backdrop click
- Add mbAvailable state to track MusicBrainz availability
- Set mbAvailable=false when MB is disabled or API fails
- Wrap entire Relationships section with {#if mbAvailable}
- Only extract labels from albums categorized as 'albums' (Discography)
- Excludes EPs, live albums, compilations, tributes, and others
- Change icon from Tag to Disc (record label, not tag)
- Add labels field to ArtistDetail type
- Extract unique labels from artist albums in convertQobuzArtist
- Update appendArtistAlbums to merge new labels
- Display labels in sidebar with click navigation to label view
- Use Tag icon from lucide-svelte
- Remove ended visual distinction (all members same style)
- Match Lyrics sidebar dimensions (340px, same height)
- Update header structure to match Lyrics
- Use element-connect.svg icon
- Equal spacing between items in all sections
cc
- 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
- Deduplicate members that appear multiple times with different roles
- Combine roles and show in mouseover tooltip
- Apply to Members, Former Members, Member Of, Collaborators
cc
- Members: current band members
- Former Members: past members (with muted style)
- Member Of: groups artist belongs to
- Collaborators: collaboration relationships
cc
- Fixed position sidebar that slides in from right
- Labels section (placeholder)
- Similar Artists section (functional)
- MusicBrainz Relationships section (functional)
cc