The scan never treated an .iso as audio; images only entered the library
through Open > disc image. Each root's scan now walks its .iso files,
asks qbz_disc::sacd::is_sacd_image for the SACDMTOC signature (PVD plus
Master TOC sector, four 2 KB reads at most, no directory parsing) so a DVD
or data ISO leaves silently, skips images already imported with the same
size and mtime without any I/O, and adopts the rest through the
generation-based import. The row builder moved from sacd_qt into
qbz_library::sacd_scan and the manual open is now its client, with the
translated fallback labels. Parser and import rejections are scan errors.
cc
rusqlite 0.40 removed ToSql/FromSql for u64 (SQLite integers are i64):
every u64 column now reads/writes through an explicit i64 cast. It also
rejects a multi-statement execute() instead of silently running only the
first statement — which is what catalog.rs's editions/logical_albums
prune had been doing all along; it is an execute_batch now, so the
logical_albums prune finally runs.
cc
Stream each registered root into bounded extraction batches, persist fingerprint and checkpoint generations, and prune only after a complete reachable pass. Add local watcher hints plus separate periodic local and network reconciliation.
Preserve CUE identities, retry unreadable files, and keep cancellation, traversal errors, offline roots, and overlapping roots from authorizing destructive cleanup.
cx
Every 0.23.x release of lofty is yanked on crates.io, so the version
requirement only resolves from the committed Cargo.lock. A fresh resolve
of qbz-library or qbz-offline-cache as a dependency fails outright.
0.24.0 needs no code changes across the 19 call sites.
Scanner accepts .dsf/.dff; new AudioFormat::Dsd; metadata extraction
via qbz-dsd (embedded ID3, artwork, bit_depth=1 with the DSD bit rate
as sample_rate so badges derive DSD64/128/256). Ephemeral folder open
now sorts tracks by album/disc/track/title and re-keys ids so the
displayed order matches the play-queue order (was readdir order).
cc
New album_grouping module exports the SQL CASE-WHEN expression for the
metadata group key shared by both new queries. The expression has three
priorities: (1) usable album tag => album|artist key, (2) fallback to
existing folder-based album_group_key, (3) orphan bucket
'__unknown_album__'.
get_albums_metadata_grouped runs the grouped query and returns
LocalAlbum rows with directory_path empty and source_folders populated
with the comma-separated list of contributing folders. Pinned 'Unknown
Album' to the bottom via ORDER BY.
get_album_tracks_metadata fetches all tracks belonging to a metadata
group, ordered by disc/track number.
Adds idx_local_tracks_album_lookup index on (album, album_artist,
artist) to keep the GROUP BY index-backed at 16k+ track libraries.
LocalAlbum gains an optional source_folders field (None for folder-
grouped rows; Some(comma-separated) for metadata-grouped rows). The
existing folder-grouped query continues to populate it as None so no
downstream consumer breaks.
Hidden filter (album_settings.hidden) does not apply to metadata-
grouped rows in this iteration; revisiting requires extending
album_settings to support metadata keys.
cc
- Imports reorganized: use lofty::prelude::*, lofty::tag::*, lofty::picture::*
- Picture::new_unchecked -> Picture::unchecked().pic_type().mime_type().build()
- set_year/remove_year -> set_date/remove_date with Timestamp
- ItemKey::Unknown("ISRC") -> ItemKey::Isrc
- save_to_path(path) -> save_to_path(path, WriteOptions::default())
- get_string/remove_key now take ItemKey by value
- Bump qbz-library crate lofty dep to 0.23
cc
Create qbz-library crate with:
- LibraryScanner: directory scanning for audio files
- MetadataExtractor: audio metadata extraction via lofty
- LibraryDatabase: SQLite persistence with WAL mode
- CueParser: CUE sheet parsing for single-file albums
- Thumbnails: artwork extraction and thumbnail generation
- Tag sidecar: album metadata override files
src-tauri re-exports from qbz-library for backwards compatibility.
Enables TUI/headless clients to manage local libraries without Tauri.