ALSA Direct playback thread now supports gapless via mpsc channel:
when current source ends, it checks for a queued next source and
transitions seamlessly without draining the PCM buffer.
Format mismatch still blocks gapless (bit-perfect has priority).
Gapless now defaults to enabled for all backends.
v2_set_audio_gapless_enabled, v2_set_audio_normalization_enabled, and
v2_set_audio_normalization_target now call player.reload_settings()
after writing to SQLite, so changes take effect immediately without
requiring an app restart.
Only write to gain atomic on initial measurement. Refinements now
update the cache only, preventing audible volume changes within a
single track. Inter-track normalization still works via cached values.
insert_track() now checks downloaded_purchases table to set source.
get_downloaded_purchase_track_ids() verifies file existence and
auto-removes stale entries. Closes#115
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
Arch's rust package defaults to rust-lld which causes ring crate's
native C/ASM symbols to be missing at link time. Force traditional
ld.bfd via RUSTFLAGS.
The bundled feature compiles sqlite3.c from source but fails to
link on Arch. Use system sqlite package instead, which is the
proper distro packaging approach. Also add missing deps: sqlite,
dbus, cmake, gcc.
MPRIS was declared as a plug (control other players) but QBZ needs to
register as an MPRIS media player (be controlled by media keys). Change
to a slot with name "qbz" so snapd exposes org.mpris.MediaPlayer2.qbz
on the session bus.
- Add Flathub badge and install command to README
- Rename Flatpak section to include both Flathub and GitHub Release
- Remove mpris from snap connect instructions (interface not available)
- Apply Flathub quality guidelines: developer id block, drop categories/keywords
- Expand feature list: local library, Plex, immersive/miniplayer modes,
artist blacklist, ListenBrainz, smart cache, customization
Incorporate feedback from Flathub reviewer (yakushabb):
- Use <developer id> block instead of deprecated <developer_name>
- Remove <categories> and <keywords> (pulled from .desktop file)
Expand feature list: local library, Plex integration, immersive and
miniplayer modes, artist blacklist, ListenBrainz, smart cache, and
customization options. Sync both metainfo copies.
The metainfo.xml had a fake full hash (cff32e5d47f3b44568af9...) that
returned 404 on raw.githubusercontent.com. Replace with the real commit
hash (cff32e5da7) where screenshots exist.
Normalize notification artwork to square PNG (max 512x512, <=4 MiB) before sending Icon::Bytes via ashpd portal, so album art renders reliably without legacy fallback.\n\ncx
The XDG portal notification spec's icon field doesn't support inline
album artwork the way freedesktop Notifications does with image-path.
Dual-path approach:
- Native (non-Flatpak): use org.freedesktop.Notifications D-Bus
interface directly with image-path hint for album artwork
- Flatpak (sandboxed): use ashpd portal (Flathub requirement)
Uses ashpd::is_sandboxed() for detection and ashpd's re-exported
zbus for the direct D-Bus call, avoiding extra dependencies.
The XDG portal was not displaying album artwork when passed as a
file:// URI (showed KDE default icon instead). Send artwork as
Icon::Bytes so the portal receives the image data directly.
Replace direct D-Bus notifications (notify-rust) with XDG Desktop
Portal notifications (ashpd) for Flathub compliance. Portal
notifications use the app's .desktop file identity and don't require
--talk-name=org.freedesktop.Notifications permission.
- Replace notify-rust dep with ashpd (linux-only, tokio+notification)
- Use NotificationProxy for portal-based notifications
- Use stable ID "track-now-playing" to replace instead of stacking
- Convert artwork path to file:// URI via ashpd::Uri
- Graceful fallback when portal is unavailable
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.
The packaging/snap/ copy already had the mpris plug but the root
snapcraft.yaml was missing it, preventing MPRIS media controls
from working in the Snap package.
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.
v2_purchases_get_all and v2_purchases_get_by_type only set the
downloaded boolean but never populated downloaded_format_ids, so
check marks disappeared after app restart. Now both use
get_downloaded_purchase_formats() and build the format map, matching
the behavior already present in v2_purchases_get_album.
Fixes#119
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.
The library scanner's album_root_dir() only knew to skip disc folders
(Disc 1, CD2, etc.) when resolving the album directory. Files stored
in quality subfolders like artist/album/FLAC 24-bit - 96 kHz/track.flac
caused each encoding variant to appear as a separate album card.
Add is_encoding_folder() to detect common audio format directory names
(FLAC, MP3, AAC, WAV, etc.) and make album_root_dir() skip past both
encoding and disc subdirectories, handling chained nesting too.
Fixes the workaround from #116.
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.