Files
netfelix-audio-fix/server/db/schema.ts
T
felixfoertsch 05a1345750
Build and Push Docker Image / build (push) Successful in 2m31s
rip subtitle manager → sibling project, keep extraction only
subtitle management (list/detail pages, /api/subtitles, subtitle_files
table, SubtitleFile types, predictExtractedFiles, nav link) moved to a
new sibling project at ~/Developer/netfelix-subtitles-manager/ where
it'll be rebuilt standalone later. this project now owns audio fixing
+ subtitle extraction only.

extraction still runs end to end: analyzeItem still marks every subtitle
stream as "remove from container", buildExtractionOutputs still wires
the -map 0:s:N + sidecar outputs into the ffmpeg command, and execute.ts
still flips review_plans.subs_extracted so verify.ts can check desired
state — just derived from the streams directly instead of by writing a
row per file to the now-gone subtitle_files table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:50:52 +02:00

144 lines
4.7 KiB
TypeScript

export const SCHEMA = `
PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY NOT NULL,
value TEXT
);
CREATE TABLE IF NOT EXISTS media_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
jellyfin_id TEXT NOT NULL UNIQUE,
type TEXT NOT NULL,
name TEXT NOT NULL,
original_title TEXT,
series_name TEXT,
series_jellyfin_id TEXT,
season_number INTEGER,
episode_number INTEGER,
year INTEGER,
file_path TEXT NOT NULL,
file_size INTEGER,
container TEXT,
runtime_ticks INTEGER,
date_last_refreshed TEXT,
original_language TEXT,
orig_lang_source TEXT,
needs_review INTEGER NOT NULL DEFAULT 1,
imdb_id TEXT,
tmdb_id TEXT,
tvdb_id TEXT,
jellyfin_raw TEXT,
external_raw TEXT,
scan_status TEXT NOT NULL DEFAULT 'pending',
scan_error TEXT,
last_scanned_at TEXT,
ingest_source TEXT NOT NULL DEFAULT 'scan',
last_executed_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS media_streams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_id INTEGER NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
stream_index INTEGER NOT NULL,
type TEXT NOT NULL,
codec TEXT,
profile TEXT,
language TEXT,
language_display TEXT,
title TEXT,
is_default INTEGER NOT NULL DEFAULT 0,
is_forced INTEGER NOT NULL DEFAULT 0,
is_hearing_impaired INTEGER NOT NULL DEFAULT 0,
channels INTEGER,
channel_layout TEXT,
bit_rate INTEGER,
sample_rate INTEGER,
bit_depth INTEGER,
UNIQUE(item_id, stream_index)
);
CREATE TABLE IF NOT EXISTS review_plans (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_id INTEGER NOT NULL UNIQUE REFERENCES media_items(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'pending',
is_noop INTEGER NOT NULL DEFAULT 0,
auto_class TEXT,
sorted INTEGER NOT NULL DEFAULT 0,
apple_compat TEXT,
job_type TEXT NOT NULL DEFAULT 'copy',
subs_extracted INTEGER NOT NULL DEFAULT 0,
notes TEXT,
reviewed_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS stream_decisions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
plan_id INTEGER NOT NULL REFERENCES review_plans(id) ON DELETE CASCADE,
stream_id INTEGER NOT NULL REFERENCES media_streams(id) ON DELETE CASCADE,
action TEXT NOT NULL,
target_index INTEGER,
custom_title TEXT,
transcode_codec TEXT,
UNIQUE(plan_id, stream_id)
);
CREATE TABLE IF NOT EXISTS jobs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_id INTEGER NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
command TEXT NOT NULL,
job_type TEXT NOT NULL DEFAULT 'copy',
status TEXT NOT NULL DEFAULT 'pending',
output TEXT,
exit_code INTEGER,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
started_at TEXT,
completed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_review_plans_status ON review_plans(status);
CREATE INDEX IF NOT EXISTS idx_review_plans_is_noop ON review_plans(is_noop);
CREATE INDEX IF NOT EXISTS idx_stream_decisions_plan_id ON stream_decisions(plan_id);
CREATE INDEX IF NOT EXISTS idx_media_items_series_jf ON media_items(series_jellyfin_id);
CREATE INDEX IF NOT EXISTS idx_media_items_series_name ON media_items(series_name);
CREATE INDEX IF NOT EXISTS idx_media_items_type ON media_items(type);
CREATE INDEX IF NOT EXISTS idx_media_streams_item_id ON media_streams(item_id);
CREATE INDEX IF NOT EXISTS idx_media_streams_type ON media_streams(type);
CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
CREATE INDEX IF NOT EXISTS idx_jobs_item_id ON jobs(item_id);
`;
export const DEFAULT_CONFIG: Record<string, string> = {
setup_complete: "0",
jellyfin_url: "",
jellyfin_api_key: "",
jellyfin_user_id: "",
radarr_url: "",
radarr_api_key: "",
radarr_enabled: "0",
sonarr_url: "",
sonarr_api_key: "",
sonarr_enabled: "0",
audio_languages: "[]",
auto_processing: "0",
auto_process_queue: "0",
scan_running: "0",
job_sleep_seconds: "0",
scan_schedule_enabled: "0",
scan_schedule_start: "01:00",
scan_schedule_end: "07:00",
process_schedule_enabled: "0",
process_schedule_start: "01:00",
process_schedule_end: "07:00",
mqtt_enabled: "0",
mqtt_url: "",
mqtt_topic: "jellyfin/events",
mqtt_username: "",
mqtt_password: "",
};