- Rename milestone from 'Features & Extensibility' to 'Multi-Library Support' - Replace old phases 10-14 (TAG/SMRT/GAP/MB/LAYOUT/PLUG) with 20 multi-library requirements (LIB/LSCAN/VIEW/PLAY/DATA) - Move deferred features to Future Requirements - Update research files with multi-library research (Stack, Features, Architecture, Pitfalls) - Architecture: hybrid model (library_id on audio_files only), libraries in DB, sequential scanning, phantom tracks
4.3 KiB
Pitfalls Research: Multi-Library Support
Researched: 2026-03-08 Confidence: HIGH
Critical Pitfalls
P1: SQLite ALTER TABLE ADD COLUMN with NOT NULL Requires DEFAULT
SQLite requires NOT NULL columns added via ALTER TABLE to have a default. Create libraries table and insert default library BEFORE adding library_id to audio_files. Use DEFAULT {id} where id is the auto-created library's ID.
Phase: Schema & Migration
P2: Table Rebuild for CASCADE to SET NULL Must Audit ALL Tables
Both playlist_tracks AND queue_tracks have ON DELETE CASCADE on audio_file_id. Decision: playlist_tracks -> SET NULL (phantom support), queue_tracks -> keep CASCADE (queue is ephemeral). Must explicitly document this choice.
Phase: Schema & Migration
P3: FTS5 Contentless Table Cannot Delete Individual Rows
After removing a library with 10K tracks, 10K stale FTS5 entries remain. Current JOIN filtering handles this, but FTS5 scoring is affected. Consider migrating to contentless_delete=1 (SQLite 3.43.0+). Alternative: full rebuild after library removal.
Phase: Schema & Migration
P4: Orphan Cleanup Through Entity Graph Is Complex
Reference-counting deletes must handle shared entities. Two libraries with same artist — removing one must not delete the artist if the other still references it. Use NOT IN (SELECT ...) or LEFT JOIN ... WHERE ... IS NULL pattern. Single transaction required.
Phase: Backend API / Library CRUD
P5: Existing User Migration Must Be Seamless
First launch after update: migration 6 reads TOML DirectoryPath, creates library row, backfills audio_files.library_id. Test on real user database snapshot, not just fresh DB.
Phase: Schema & Migration
Moderate Pitfalls
P6: Frontend Memory Pressure with Multiple Large Libraries
libraryStore.eagerFetch() loads ALL data. 150K tracks x ~500 bytes = 75MB. Use backend filtering (pass library_id to queries). When "All Libraries" is selected, this is unavoidable for now — pagination is a future optimization.
Phase: Frontend
P7: Scan Coordination — No Concurrent Scans
Single scanActive bool, single entity cache, single writer SQLite. Enforce one-scan-at-a-time globally with scan coordinator. Track which library is scanning for UI display.
Phase: Backend Scan Pipeline
P8: Phantom Track Resolution with Multiple Library Roots
LibraryDirProvider returns single string. M3U8 path resolution checks one root. With multi-library, try all library roots for phantom resolution. Store phantom_file_path as absolute path to avoid ambiguity.
Phase: Frontend / Playlist Integration
P9: Queue and Now-Playing During Library Removal
If currently playing track belongs to removed library: stop playback, advance to next non-removed track. Check queue and player state before proceeding with removal.
Phase: Backend API / Library CRUD
P10: Cross-Library Entity Deduplication
Same artist in two libraries -> one artists row (UNIQUE constraint handles this). Removing one library's audio_files must NOT delete shared artist. Reference-counting cleanup handles this correctly.
Phase: Backend API / Library CRUD
Minor Pitfalls
P11: Config Migration — TOML to DB Split Creates Two Sources of Truth
Move ALL library-related config to DB. TOML only for app-level settings (theme, shortcuts, window). TOML [Library] section is migration source only.
Phase: Schema & Migration
P12: Library Filter State Interacting with Everything
Single filter state in libraryStore. All data-fetching functions accept the filter. Trigger invalidate+refetch on filter change.
Phase: Frontend
P13: Scan-While-Remove Race Condition
Before removing a library, cancel any active scan on it and wait for completion. Serialize scan and remove operations.
Phase: Backend API
P14: Cover Art Files Not Library-Scoped
Cover art stored by content hash (shared). Removing a library: only delete cover_art DB rows that are truly orphaned (no remaining release_groups reference them). Then delete corresponding files.
Phase: Backend API
P15: Testing Gaps
Create "two-library fixture" test helper. Test: add two libraries with overlapping artists -> remove one -> verify other is intact. Test migration on pre-multi-library DB snapshot.
Phase: All phases (accompanying tests)