migrate existing sqlite dbs to the new columns, don't force a nuke on deploy
All checks were successful
Build and Push Docker Image / build (push) Successful in 52s
All checks were successful
Build and Push Docker Image / build (push) Successful in 52s
upgraded containers (like the unraid one with a persistent ./data volume) kept their old media_items/media_streams tables, so upsertJellyfinItem hit "table media_items has no column named original_title" on first scan after the canonical-language rewrite. sqlite has no native add-column-if-not-exists so we try/catch each alter, same pattern we had before i deleted the shims. also backports the older alters (stream_decisions.custom_title, review_plans subs_extracted/confidence/apple_compat/job_type) so pre-rewrite installs still converge without a wipe. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,11 +51,50 @@ export function getDb(): Database {
|
||||
if (_db) return _db;
|
||||
_db = new Database(dbPath, { create: true });
|
||||
_db.exec(SCHEMA);
|
||||
applyMigrations(_db);
|
||||
seedDefaults(_db);
|
||||
|
||||
return _db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add columns that landed after the initial schema. `CREATE TABLE IF NOT
|
||||
* EXISTS` above skips existing tables, so upgraded installs need per-column
|
||||
* ALTERs to pick up new fields. Each call is wrapped in try/catch because
|
||||
* SQLite has no native `ADD COLUMN IF NOT EXISTS` — the error is benign.
|
||||
*
|
||||
* Do NOT remove entries here just because the schema block also declares the
|
||||
* column. Fresh installs get it from SCHEMA; existing deployments (e.g. the
|
||||
* Unraid container with a persistent volume) only get it from this function.
|
||||
*/
|
||||
function applyMigrations(db: Database): void {
|
||||
const addColumn = (table: string, column: string, type: string) => {
|
||||
try {
|
||||
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
||||
} catch {
|
||||
/* already exists */
|
||||
}
|
||||
};
|
||||
|
||||
// 2026-04-13: canonical-language + full-jellyfin-capture rewrite
|
||||
addColumn("media_items", "original_title", "TEXT");
|
||||
addColumn("media_items", "runtime_ticks", "INTEGER");
|
||||
addColumn("media_items", "date_last_refreshed", "TEXT");
|
||||
addColumn("media_items", "jellyfin_raw", "TEXT");
|
||||
addColumn("media_items", "external_raw", "TEXT");
|
||||
addColumn("media_items", "last_executed_at", "TEXT");
|
||||
addColumn("media_streams", "profile", "TEXT");
|
||||
addColumn("media_streams", "bit_depth", "INTEGER");
|
||||
|
||||
// Earlier migrations kept here so installs that predate the big rewrite
|
||||
// still converge to the current schema.
|
||||
addColumn("stream_decisions", "custom_title", "TEXT");
|
||||
addColumn("stream_decisions", "transcode_codec", "TEXT");
|
||||
addColumn("review_plans", "subs_extracted", "INTEGER NOT NULL DEFAULT 0");
|
||||
addColumn("review_plans", "confidence", "TEXT NOT NULL DEFAULT 'low'");
|
||||
addColumn("review_plans", "apple_compat", "TEXT");
|
||||
addColumn("review_plans", "job_type", "TEXT NOT NULL DEFAULT 'copy'");
|
||||
}
|
||||
|
||||
function seedDefaults(db: Database): void {
|
||||
const insert = db.prepare("INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)");
|
||||
for (const [key, value] of Object.entries(DEFAULT_CONFIG)) {
|
||||
|
||||
Reference in New Issue
Block a user