Self-hosted web interface and downloader for Qobuz.

refactor(library): back library index, provenance, and ownership with sqlite #33

Closed glerp.sullen.net
targeting v1 from glerp.sullen.net/gobuz:refactor/sqlite-library
Closed opened by and assigned to glerp.sullen.net
targeting v1 from glerp.sullen.net/gobuz:refactor/sqlite-library

Rebuilds internal/library atop store, audiotag, and events and aligns the v1 spine (upstream/v1) around fail-fast entry validation, canonicalized invariants, and single-layer boundaries across 4 reviewable CLs:

  1. refactor(models): use explicit identifier names on music domain models — Replaces ambiguous .ID / .Path fields on multi-source music domain and view structs (Release, LibraryRelease, LibraryTrack, Track, Artist, ReleaseCard, TrackProgress, downloader progress/error structs) with explicit identifiers (QobuzReleaseID, QobuzTrackID, QobuzArtistID, MBReleaseID, MBReleaseGroupID, MBRecordingID, LocalTrackID, ReleaseKey, FilePath, FolderPath).
  2. refactor(web): rename /downloads routes and package to /library — Renames /downloads routes, templates, CSS, middleware quiet-log paths, and internal/web/downloads to internal/web/library (/library).
  3. refactor(library): back library index, provenance, and ownership with sqlite — Replaces RAM/directory-based library scanning with a track-first SQLite-cached index (library_tracks, library_sidecar_covers), 4-tier deterministic ReleaseKey precedence (qobuz: → mb: → upc: → tag:), release_provenance, and multi-user release_owners.
  4. refactor: enforce entry validation, canonical invariants, and single-layer boundaries — Constrains the valid state space up front, propagates canonical invariants once at entry boundaries, and splits cross-layer responsibilities across library (library.go, fs.go, scan.go, store.go, snapshot.go, cover.go, match.go, thumbnail.go) and the v1 spine:
    • Constrained folder & sidecar contracts: Resolves root symlinks and enforces disjoint folders once at startup (Config.ResolveFolders()), ignores intra-folder symlinks, aborts Scan before mutating SQLite if any root folder is inaccessible, constrains sidecar covers to configured cover_names stems (defaulting to ["cover"]), and omits bulk /library delete-all in favor of explicit per-release deletion.
    • Entry-boundary canonicalization & validation: Validates config.ini settings, format templates, CLI arguments, db_path (LoadDBPath()), UUIDs/ISRCs/GTINs, and image headers/dimensions (audiotag.ValidateImage) once on entry so downstream Library, Downloader, and web handlers trust validated invariants without redundant re-cleaning or re-parsing.
    • Single-layer orchestration & Goosefoot coordination: Eliminates hidden DB writes from constructors/getters (auth.NewService, auth.GetSession), removes path math from web/library, coordinates atomic Goosefoot tagging eligibility and reservation via Library.InspectTagTarget / TryStartTagging (409 Conflict against concurrent tagging or deletion), detaches post-import SQLite reconciliation from SSE client disconnects, and wires selective ThumbnailCache invalidation via events.Bus.
Commits 0