rewrite from monolithic hono jsx to react 19 spa with tanstack router + hono json api backend. add scan, review, execute, nodes, and setup pages. multi-stage dockerfile (node for vite build, bun for runtime). previously, server/ and src/shared/lib/ were silently excluded by global gitignore patterns (/server/ from emacs, lib/ from python). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.1 KiB
TypeScript
130 lines
4.1 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 nodes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
host TEXT NOT NULL,
|
|
port INTEGER NOT NULL DEFAULT 22,
|
|
username TEXT NOT NULL,
|
|
private_key TEXT NOT NULL,
|
|
ffmpeg_path TEXT NOT NULL DEFAULT 'ffmpeg',
|
|
work_dir TEXT NOT NULL DEFAULT '/tmp',
|
|
status TEXT NOT NULL DEFAULT 'unknown',
|
|
last_checked_at TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
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,
|
|
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,
|
|
original_language TEXT,
|
|
orig_lang_source TEXT,
|
|
needs_review INTEGER NOT NULL DEFAULT 1,
|
|
imdb_id TEXT,
|
|
tmdb_id TEXT,
|
|
tvdb_id TEXT,
|
|
scan_status TEXT NOT NULL DEFAULT 'pending',
|
|
scan_error TEXT,
|
|
last_scanned_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,
|
|
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,
|
|
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,
|
|
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,
|
|
UNIQUE(plan_id, stream_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS subtitle_files (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
item_id INTEGER NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
|
file_path TEXT NOT NULL UNIQUE,
|
|
language TEXT,
|
|
codec TEXT,
|
|
is_forced INTEGER NOT NULL DEFAULT 0,
|
|
is_hearing_impaired INTEGER NOT NULL DEFAULT 0,
|
|
file_size INTEGER,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
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,
|
|
node_id INTEGER REFERENCES nodes(id) ON DELETE SET NULL,
|
|
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
|
|
);
|
|
`;
|
|
|
|
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',
|
|
subtitle_languages: JSON.stringify(['eng', 'deu', 'spa']),
|
|
scan_running: '0',
|
|
movies_path: '',
|
|
series_path: '',
|
|
};
|