- 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.
Add automatic ListenBrainz scrobbling when tracks play:
- Now-playing submission on track start
- Scrobble after 50% or 4min (whichever is shorter)
- MusicBrainz data enrichment for enhanced scrobbles
- Offline queue support (flush when back online)
- ISRC field added to PlayingTrack interface
Scrobbling uses Stage 1 MusicBrainz resolution to include:
- Recording MBID
- Release MBID
- Artist MBIDs
cc
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
- Stop playback immediately when starting a Qobuz track to prevent
previous track from continuing during download
- Show buffering toast while downloading Qobuz audio
- Skip buffering UI for local tracks since they load instantly
- Add is_local field to QueueTrack for prefetch optimization
- Update prefetch to skip local tracks (no network needed)
- Update all queue track builders to include is_local flag
Research findings:
- Last.fm API supports batch scrobbling (up to 50 tracks)
- Timestamps can be backdated up to 14 days
Implementation:
- Add scrobble_queue table to offline database
- Add queue_scrobble, get_queued_scrobbles, mark_scrobbles_sent commands
- Queue scrobbles when offline instead of skipping
- Flush queue automatically when transitioning to online
- Skip scrobbles older than 14 days (Last.fm limit)
- Cleanup sent scrobbles after 7 days
- Add offline indicator icon in right section when offline
- Disable lyrics button with tooltip when offline
- Skip Last.fm scrobbling when in offline mode
- Use offline-small.svg for compact indicator
- Split "Notifications" setting into "In-app Toasts" and "System Notifications"
- In-app toasts control the toast messages within the app
- System notifications control desktop notifications on track change
- Both settings persist to localStorage and load on app startup
- Migrates old 'qbz-notifications-enabled' key to new 'qbz-toasts-enabled'
feat(library): add stop scanning button
- Add library_stop_scan command in Rust backend
- Add cancel flag (AtomicBool) to LibraryState
- Check cancel flag during scan loop for both CUE and audio files
- Add "Cancelled" status to ScanStatus enum
- Add stop button in LocalLibraryView scan progress UI
- Button appears red on hover for clear visibility
cc
Frontend:
- Add castStore for managing cast state (connect, disconnect, play, pause, etc)
- Update CastPicker to use castStore, show connected device banner with disconnect
- Wire isCastConnected to NowPlayingBar and ExpandedPlayer for visual indicator
- Modify playbackService to cast when connected instead of local playback
DLNA Backend:
- Implement full AVTransport SOAP client with:
- SetAVTransportURI with DIDL-Lite metadata
- Play, Pause, Stop, Seek actions
- RenderingControl SetVolume
- Add dlna_play_track command that fetches stream, downloads audio, and serves via media server
- Share media server between Chromecast and DLNA
- Add 50ms delay after setMinSize before setSize to prevent race condition
- Remove setDecorations(true) on restore - app uses custom title bar (CSD)
- Keep decorations: false throughout miniplayer mode transitions
- Add 'buffering' toast type that doesn't auto-hide
- Add shimmer animation to "Buffering" text
- Add spinning loader icon for buffering state
- Toast shows "Buffering... [track name]" format
- Dismiss buffering toast when track loads or on error
- Remove callback pattern, use toastStore directly in services
fix(artwork): show empty placeholder when no artwork available
- NowPlayingBar shows empty placeholder instead of Play icon
- Miniplayer shows empty placeholder instead of Play icon
- Prevents showing incorrect CD placeholder on session restore
- Move drag handler to artwork-section and track-info only, not parent
- Buttons in window-controls and controls-row now receive clicks
- Enable window resizing in miniplayer mode
- Reduce cover art width from 180px to 110px (~60% of original)
- Changed dimensions to 400x200
- Reduced shadow area padding from 8px to 4px
- Added 6px margin around album art
- Fixed TypeError on qState.tracks.length with null checks
- Increased font sizes and control button sizes for larger window
- Added core:window:allow-set-min-size permission
- Call setMinSize(300x140) before setSize to allow smaller window
- Restore original minSize (800x600) when exiting miniplayer
- Added more console logs for debugging
- Use localStorage to persist original window state across navigation
- Add 4px margin frame around the player with rounded corners
- Darker background (#0f0f12) visible in the margin
- Box shadow for depth effect
- Removed isMiniplayerMode guards that prevented restore
- Added startPolling/stopPolling for seekbar to update properly
- Removed border-radius to fix white corners issue
- Added queue button with track count badge
- Simplified CSS to solid background color
- MiniPlayer now resizes the main window instead of creating a separate floating window
- Cider-inspired UI with large album art, track info, full playback controls
- Progress bar with current/remaining time display
- Shuffle, repeat, volume controls integrated
- Pin/restore buttons to control always-on-top and return to main view
- Fixed tray icon to use transparent PNG from static/icons
- Create miniplayerService.ts for window management
- Add /miniplayer route with compact player UI
- Sync playback state from main window via events
- MiniPlayer features:
- Draggable window (decorations disabled)
- Always-on-top toggle (pin button)
- Album art, track title, artist
- Play/Pause, Next, Previous controls
- Progress bar
- Close button (hides window)
- Add PictureInPicture2 button to NowPlayingBar
- State synced via emitTo('miniplayer', ...) events
Quality Badge Fix:
- Extended BackendQueueTrack with hires, bit_depth, sample_rate fields
- Updated all queue track builder functions to pass quality data
- Fixed quality badge showing CD instead of Hi-Res for playlist tracks
- Extended session persistence to save/restore quality info
Queue Drag-to-Reorder:
- Added move_track function to Rust QueueManager
- Added move_queue_track Tauri command
- Implemented HTML5 drag-and-drop in QueuePanel
- Visual feedback with drag-over indicator and dragging opacity
- Disabled drag when search filter is active
- Prevented text selection during drag operations
Backend (Rust):
- Create session_store module with SQLite database
- Store queue tracks, current index, position, volume, shuffle/repeat
- Add Tauri commands: save_session_state, load_session_state, etc.
- Use WAL mode for better concurrent access
Frontend (TypeScript):
- Create sessionService.ts with save/load functions
- Restore session after login (queue, track info, volume, modes)
- Save session on beforeunload and visibility change
- Clear session on logout
- Debounced position saving during playback
Users can now close the app and resume where they left off.
Move core playback operations to centralized service:
- playTrack() - unified track playback with all side effects
- MPRIS metadata updates
- System notifications
- Last.fm scrobbling and now playing
- Favorite status checking and toggling
Simplifies handlers: handlePlaylistTrackPlay, handleLocalTrackPlay, toggleFavorite.
Reduces +page.svelte from 1675 to 1525 lines.