Add performance watchdog to ImmersiveRenderer that monitors FPS after
a 3-second warmup period. If FPS stays below 15 for 5 consecutive
seconds, automatically degrades background mode (Full -> Lite -> Off),
persists the setting, and shows a toast notification.
ImmersiveBackground listens for the degradation event and switches
renderer in-place without requiring a full remount.
ListenBrainz similar-artists returned mainstream crossover artists (SOAD,
Led Zeppelin for Metallica) instead of genre-accurate results. Now uses
MusicBrainz tag search to find artists sharing the seed's primary genre tag
(e.g. "thrash metal" returns Megadeth, Slayer, Kreator, etc).
- Add search_artists_by_tag to MusicBrainz client
- Deterministic shuffle using seed MBID hash for variety across artists
- Secondary tag fallback when primary yields fewer than 5 results
- Rename label to "You May Also Like", limit results to 6
- Add Last.fm get_similar_artists method (unused, for future use)
- Add tags field to ArtistFullResponse model
- Tighten local history exclusion to play_count > 2
Display similarity score next to each discovery artist name. Update
DiscoveryArtist interface with similarityPercent field. Add i18n keys
for youMayAlsoLike section and sidebar layout improvements.
Separate isActiveTrack (current track) from isPlaying (actively playing)
so the equalizer bars freeze via animation-play-state when paused instead
of always animating. Show Play icon on hover when paused, Pause when playing.
Also fix AlbumDetailView which was missing the isPlaybackActive check.
The device name was only read on mount and on hover, so it stayed
as "No device" until the user hovered the badges area. Now the
1-second poll also reads get_audio_output_status (cheap in-memory
read) alongside hardware status, keeping the device name current.
Fall back to settings.output_device when the player hasn't reported
a device yet (before first playback). Previously showed "No device"
even though a device was configured in audio settings. Also change
the final fallback from "No device" to "System Default" since no
configured device means the system default is being used.
Add loadHome({ showSkeletons: false }) to the settings subscription
callback so visibility toggles, reordering, and limit changes are
reflected in real-time instead of requiring a page reload.
Use cachedSrc action for artist images in SearchView, HomeView,
ArtistDetailView, VirtualizedFavoritesArtistGrid, and
VirtualizedFavoritesArtistList. Use size-aware image selection
(small instead of large) for grid/card contexts. Add opacity
transition for smooth placeholder-to-image loading.
Image is hidden (opacity 0) until the cached URL resolves, preventing
broken image icons. The disc placeholder shows through while loading.
Adds a subtle 150ms fade-in transition when the image appears.
When navigating from album(1) to album(2), the view type stays 'album'
so the scroll reset effect didn't trigger. Now tracks currentNavItemId
changes too, and explicitly scrolls to top for forward navigation while
preserving scroll restore on back/forward.
Navigation history now tracks itemId per entry (album, artist, playlist,
label, musician, purchase-album) so back/forward restores the exact page.
Scroll positions are saved per view+itemId key and restored on back/forward.
The "other" group contains podcast episodes with release_type "album"
that should stay in Others, not be moved to Albums. Only "download"
group items should be re-categorized since download is a distribution
channel, not a content classification.
The Qobuz artist/page API returns release groups with type "epSingle"
(not "ep" or "single"), and groups like "download" and "other" contain
mixed items (singles, albums) that should be re-categorized by each
item's individual release_type field.
The Qobuz artist/page endpoint sometimes excludes the EP/single
release group from its response. When this happens, fetch them
explicitly via get_releases_grid as a non-blocking backfill.
Move session restore to run immediately after playback preferences
load, before network-heavy initializations (favorites, offline
status, recommendation scores). This eliminates the multi-second
"No track playing" gap on slow storage.
Also restore the saved playback position visually and on first play
(seek to saved position, no auto-play).
Add a new "System (Dark / Light)" theme option that toggles between
Dark and Light themes based on OS prefers-color-scheme, with a live
matchMedia listener for instant switching. Rename old "System" (auto
palette generation) to "Match Colors" for clarity.
Applies to main window, miniplayer, and bootstrap.
Closes#148
insert_qobuz_cached_track_direct was hardcoding track_number to 0,
causing all imported tracks to show wrong numbers. Now accepts
track_number and disc_number params, and both callers (backfill
downloads and offline cache sync) extract metadata from the audio
file via MetadataExtractor before inserting.
Also fix LocalLibraryView to use || instead of ?? for track_number
fallback, so 0 correctly falls back to index-based numbering.
Closes#147
Display parental_warning badge (E icon) next to track titles in all
views: TrackRow, QueuePanel, NowPlayingBar, ImmersivePlayer with all
13 visualizer panels, MiniPlayer surfaces, and AlbumDetailView.
Propagate parental_warning field through the full data chain from
Rust backend (qbz-models, queue, commands_v2, session_store, API
server) to frontend stores, adapters, and UI components. Badge uses
CSS mask technique with explicit.svg for theme compatibility.
Add artist grouping option for Label Releases view and remove
expensive loadAllAlbums() call. Use v2_get_label_page for richer
label image data. Add groupByArtist i18n keys to all 4 locales.
- Replace generic icon with circular label logo (matches LabelView)
- Fix double close button in search: single X that clears and closes
- Add sort options: newest, oldest, title A-Z/Z-A, artist A-Z
- Add Hi-Res filter toggle
- Load all albums automatically when sorting/filtering
- i18n: add labelReleases keys to all 4 locales
- Search bar now replaces title area instead of floating separately
API analysis: Qobuz label endpoints have no server-side sort/filter
params, so all sorting and filtering is done client-side after
paginated fetch.
Gapless now works on all backends, so the UI constraints that disabled
it for DAC Passthrough and ALSA Direct are removed. Only streaming-only
mode still disables gapless (no cache available). Removed experimental
badge. Cleaned up unused i18n keys.
Embeds quality in album folder name instead of subfolder, so each
quality variant appears as a separate album in the local library.
Adds download error toast and fixes format labels to bracket notation.
Closes#116
Replace binary blur toggle with a 3-option dropdown. Full mode uses
the existing WebGL2 shader, Lite mode renders a pre-blurred static
image animated with CSS transforms (compositor-driven, immune to
Wayland frame callback stutter), and Off mode shows a solid color.
Includes config migration from disableBlurBackground to backgroundMode,
updated composition profiles, and i18n for all 4 locales.
Closes#127
Detect when QBZ runs inside a Snap sandbox (via SNAP env var) and show
a one-time welcome modal with required plug connection commands. Adds a
Snap Sandbox section in Settings with copyable commands, mirroring the
existing Flatpak pattern.
Backend: snap.rs module, DB table snap_welcome_shown, V2 commands.
Frontend: updatesService, +page.svelte modal queue, SettingsView section.
When typing in the search bar, new results cause the virtual scroll
container to remount. The ResizeObserver was stale (watching the old
destroyed element) and containerWidth reset to 0, causing gridCols
to default to 1. Two fixes: cache the last known column count so
width=0 uses the previous layout, and reconnect the ResizeObserver
whenever the container element changes.
First Escape clears the search text, second Escape when input is
already empty removes focus from the input. Applied to both the
sidebar and titlebar search inputs for keyboard-driven navigation.
The CheckSquare button was next to the search bar instead of in the
header actions alongside Play/Shuffle/Save, inconsistent with DailyQ,
FavQ and TopQ views. Moved it to the header and removed orphaned CSS.
The broken views called onTrackAddToPlaylist in a loop (once per
selected track), causing each call to overwrite the previous track
IDs in the playlist modal. Only the last track survived. Now all 6
views collect IDs first and call onBulkAddToPlaylist once with the
full array.
Views fixed: ArtistDetailView, AlbumDetailView, LabelView,
FavoritesView, WeeklySuggestView, PlaylistDetailView.
When downloading a purchased album to ~/Music, the "Add to Local
Library" button was adding ~/Music (the entire music directory) instead
of the specific album folder like ~/Music/Artist/Album/.
After the first track downloads, extract the album-level folder from
the returned file path and update the stored destination. This ensures
only the downloaded album gets added to the library. Fixes#117.
Allow users to cancel an in-progress album download. The current track
finishes but no further tracks are started. A cancel button appears in
the progress bar during download, and a cancelled state shows how many
tracks completed. Closes#133.
Switch from CSS grid to CSS multi-column layout so categories pack
tightly top-to-bottom instead of creating large empty rows when
columns have uneven heights.