adapter-sqlite: all repo implementations + wire fn + migrations copy

This commit is contained in:
2026-07-12 02:34:12 +02:00
parent 1428f264bb
commit e8179d1f53
29 changed files with 1987 additions and 2 deletions

View File

@@ -0,0 +1,11 @@
-- Create users table
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY NOT NULL,
subject TEXT NOT NULL,
email TEXT NOT NULL,
password_hash TEXT,
created_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_subject ON users(subject);
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email ON users(email);

View File

@@ -0,0 +1,59 @@
-- Channels: user-defined broadcast channels with their schedule template
CREATE TABLE IF NOT EXISTS channels (
id TEXT PRIMARY KEY NOT NULL,
owner_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
timezone TEXT NOT NULL DEFAULT 'UTC',
-- JSON-encoded ScheduleConfig (the shareable/exportable template)
schedule_config TEXT NOT NULL DEFAULT '{"blocks":[]}',
-- JSON-encoded RecyclePolicy
recycle_policy TEXT NOT NULL DEFAULT '{"cooldown_days":30,"cooldown_generations":null,"min_available_ratio":0.2}',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_channels_owner ON channels(owner_id);
-- Generated 48-hour schedules (resolved from a channel's template)
CREATE TABLE IF NOT EXISTS generated_schedules (
id TEXT PRIMARY KEY NOT NULL,
channel_id TEXT NOT NULL,
valid_from TEXT NOT NULL,
valid_until TEXT NOT NULL,
generation INTEGER NOT NULL,
FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE
);
-- Composite index supports both "find active at time T" and "find latest"
CREATE INDEX IF NOT EXISTS idx_schedules_channel_valid
ON generated_schedules(channel_id, valid_from DESC);
-- Individual scheduled slots within a generated schedule.
-- The MediaItem snapshot is stored as JSON so the EPG survives library changes.
CREATE TABLE IF NOT EXISTS scheduled_slots (
id TEXT PRIMARY KEY NOT NULL,
schedule_id TEXT NOT NULL,
start_at TEXT NOT NULL,
end_at TEXT NOT NULL,
-- JSON-encoded MediaItem (metadata snapshot at generation time)
item TEXT NOT NULL,
source_block_id TEXT NOT NULL,
FOREIGN KEY (schedule_id) REFERENCES generated_schedules(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_slots_schedule_start
ON scheduled_slots(schedule_id, start_at);
-- Playback history for the recycle policy engine
CREATE TABLE IF NOT EXISTS playback_records (
id TEXT PRIMARY KEY NOT NULL,
channel_id TEXT NOT NULL,
item_id TEXT NOT NULL,
played_at TEXT NOT NULL,
generation INTEGER NOT NULL,
FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_playback_channel_date
ON playback_records(channel_id, played_at DESC);

View File

@@ -0,0 +1 @@
ALTER TABLE channels ADD COLUMN auto_schedule INTEGER NOT NULL DEFAULT 0;

View File

@@ -0,0 +1,2 @@
ALTER TABLE channels ADD COLUMN access_mode TEXT NOT NULL DEFAULT 'public';
ALTER TABLE channels ADD COLUMN access_password_hash TEXT;

View File

@@ -0,0 +1,3 @@
ALTER TABLE channels ADD COLUMN logo TEXT;
ALTER TABLE channels ADD COLUMN logo_position TEXT NOT NULL DEFAULT 'top_right';
ALTER TABLE channels ADD COLUMN logo_opacity REAL NOT NULL DEFAULT 1.0;

View File

@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS local_files_index (
id TEXT PRIMARY KEY,
rel_path TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
duration_secs INTEGER NOT NULL DEFAULT 0,
year INTEGER,
tags TEXT NOT NULL DEFAULT '[]',
top_dir TEXT NOT NULL DEFAULT '',
scanned_at TEXT NOT NULL
);

View File

@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS transcode_settings (
id INTEGER PRIMARY KEY CHECK (id = 1),
cleanup_ttl_hours INTEGER NOT NULL DEFAULT 24
);
INSERT OR IGNORE INTO transcode_settings (id, cleanup_ttl_hours) VALUES (1, 24);

View File

@@ -0,0 +1,2 @@
ALTER TABLE channels ADD COLUMN webhook_url TEXT;
ALTER TABLE channels ADD COLUMN webhook_poll_interval_secs INTEGER NOT NULL DEFAULT 5;

View File

@@ -0,0 +1,2 @@
ALTER TABLE channels ADD COLUMN webhook_body_template TEXT;
ALTER TABLE channels ADD COLUMN webhook_headers TEXT;

View File

@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS activity_log (
id TEXT PRIMARY KEY NOT NULL,
timestamp TEXT NOT NULL,
event_type TEXT NOT NULL,
detail TEXT NOT NULL,
channel_id TEXT
);
CREATE INDEX IF NOT EXISTS idx_activity_log_timestamp ON activity_log(timestamp DESC);

View File

@@ -0,0 +1,8 @@
ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0;
CREATE TABLE provider_configs (
provider_type TEXT PRIMARY KEY,
config_json TEXT NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1,
updated_at TEXT NOT NULL
);

View File

@@ -0,0 +1,12 @@
CREATE TABLE channel_config_snapshots (
id TEXT PRIMARY KEY NOT NULL,
channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
config_json TEXT NOT NULL,
version_num INTEGER NOT NULL,
label TEXT,
created_at TEXT NOT NULL,
UNIQUE (channel_id, version_num)
);
CREATE INDEX idx_config_snapshots_channel
ON channel_config_snapshots(channel_id, version_num DESC);

View File

@@ -0,0 +1,17 @@
-- Recreate provider_configs with per-instance id as PK
CREATE TABLE provider_configs_new (
id TEXT PRIMARY KEY,
provider_type TEXT NOT NULL,
config_json TEXT NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1,
updated_at TEXT NOT NULL
);
INSERT INTO provider_configs_new (id, provider_type, config_json, enabled, updated_at)
SELECT provider_type, provider_type, config_json, enabled, updated_at
FROM provider_configs;
DROP TABLE provider_configs;
ALTER TABLE provider_configs_new RENAME TO provider_configs;
-- Scope local_files_index entries by provider instance
ALTER TABLE local_files_index ADD COLUMN provider_id TEXT NOT NULL DEFAULT 'local';
CREATE INDEX IF NOT EXISTS idx_local_files_provider ON local_files_index(provider_id);

View File

@@ -0,0 +1,37 @@
CREATE TABLE IF NOT EXISTS library_items (
id TEXT PRIMARY KEY,
provider_id TEXT NOT NULL,
external_id TEXT NOT NULL,
title TEXT NOT NULL,
content_type TEXT NOT NULL,
duration_secs INTEGER NOT NULL DEFAULT 0,
series_name TEXT,
season_number INTEGER,
episode_number INTEGER,
year INTEGER,
genres TEXT NOT NULL DEFAULT '[]',
tags TEXT NOT NULL DEFAULT '[]',
collection_id TEXT,
collection_name TEXT,
collection_type TEXT,
thumbnail_url TEXT,
synced_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_library_items_provider ON library_items(provider_id);
CREATE INDEX IF NOT EXISTS idx_library_items_content_type ON library_items(content_type);
CREATE INDEX IF NOT EXISTS idx_library_items_series ON library_items(series_name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_library_items_provider_external ON library_items(provider_id, external_id);
CREATE TABLE IF NOT EXISTS library_sync_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
provider_id TEXT NOT NULL,
started_at TEXT NOT NULL,
finished_at TEXT,
items_found INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'running',
error_msg TEXT
);
CREATE INDEX IF NOT EXISTS idx_library_sync_log_provider ON library_sync_log(provider_id);
CREATE INDEX IF NOT EXISTS idx_library_sync_log_provider_started ON library_sync_log(provider_id, started_at DESC);

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS app_settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
INSERT OR IGNORE INTO app_settings(key, value) VALUES ('library_sync_interval_hours', '6');