Add file fingerprinting for move-resilient media item identity
Computes a SHA-256 partial-content fingerprint (file size + first 64 KB) for movies, TV episodes, and mixed files during scans. When a file is moved or renamed within a library, the scan detects the fingerprint match, renames the media_items row in-place, and updates media_tags.media_key to match — so tags and NFO metadata survive the move transparently. - src/lib/fingerprint.ts: new computeFingerprint() using sync FS reads - src/lib/db.ts: fingerprint TEXT column + index migration - src/lib/tags.ts: reKeyMediaItem() to update media_tags on rename - src/lib/scanner.ts: replace clear+upsert with detectMoves/reconcileAndPrune for movies, TV episodes, and mixed files; games retain clear+upsert (v1) - TV scan restructured to a single filesystem pass (no double-scanning) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,15 +89,18 @@ function initDb(db: Database.Database): void {
|
||||
genres TEXT,
|
||||
metadata TEXT,
|
||||
file_path TEXT,
|
||||
fingerprint TEXT,
|
||||
scanned_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_items_library_id ON media_items(library_id);
|
||||
CREATE INDEX IF NOT EXISTS media_items_parent_key ON media_items(parent_key);
|
||||
CREATE INDEX IF NOT EXISTS media_items_fingerprint ON media_items(fingerprint);
|
||||
`)
|
||||
|
||||
migrateLibrariesType(db)
|
||||
migrateMediaItemsSchema(db)
|
||||
migrateMediaItemsFingerprint(db)
|
||||
seedAppSettings(db)
|
||||
}
|
||||
|
||||
@@ -162,6 +165,18 @@ function migrateMediaItemsSchema(db: Database.Database): void {
|
||||
`)
|
||||
}
|
||||
|
||||
function migrateMediaItemsFingerprint(db: Database.Database): void {
|
||||
const row = db
|
||||
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='media_items'")
|
||||
.get() as { sql: string } | undefined
|
||||
if (row && !row.sql.includes('fingerprint')) {
|
||||
db.exec(`
|
||||
ALTER TABLE media_items ADD COLUMN fingerprint TEXT;
|
||||
CREATE INDEX IF NOT EXISTS media_items_fingerprint ON media_items(fingerprint);
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
function migrateLibrariesType(db: Database.Database): void {
|
||||
const row = db
|
||||
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='libraries'")
|
||||
|
||||
Reference in New Issue
Block a user