Sync from /srv/compose/unified-media-manager
This commit is contained in:
253
internal/db/migrations/001_init.sql
Normal file
253
internal/db/migrations/001_init.sql
Normal file
@@ -0,0 +1,253 @@
|
||||
-- Custom types
|
||||
CREATE TYPE MEDIA_TYPE AS ENUM (
|
||||
'movie', 'series', 'episode', 'music', 'album',
|
||||
'audiobook', 'podcast', 'photo', 'other'
|
||||
);
|
||||
|
||||
CREATE TYPE MEDIA_STATUS AS ENUM (
|
||||
'unavailable', 'searching', 'downloading', 'importing',
|
||||
'available', 'upgrading', 'failed'
|
||||
);
|
||||
|
||||
CREATE TYPE QUEUE_STATUS AS ENUM (
|
||||
'pending', 'downloading', 'imported', 'failed',
|
||||
'blacklisted', 'cancelled'
|
||||
);
|
||||
|
||||
-- Quality profiles
|
||||
CREATE TABLE quality_profiles (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
media_types MEDIA_TYPE[] NOT NULL,
|
||||
cutoff_quality JSONB NOT NULL,
|
||||
allowed_qualities JSONB NOT NULL DEFAULT '[]',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Root folders
|
||||
CREATE TABLE root_folders (
|
||||
id SERIAL PRIMARY KEY,
|
||||
path TEXT NOT NULL UNIQUE,
|
||||
media_type MEDIA_TYPE NOT NULL,
|
||||
free_space BIGINT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Tags
|
||||
CREATE TABLE tags (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
color TEXT DEFAULT '#6366f1'
|
||||
);
|
||||
|
||||
-- Scheduled tasks
|
||||
CREATE TABLE scheduled_tasks (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
cron_expr TEXT NOT NULL,
|
||||
last_run_at TIMESTAMPTZ,
|
||||
next_run_at TIMESTAMPTZ,
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
retention_days INTEGER DEFAULT 7
|
||||
);
|
||||
|
||||
-- Indexers
|
||||
CREATE TABLE indexers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
implementation TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
api_key TEXT,
|
||||
categories JSONB DEFAULT '[]',
|
||||
settings JSONB DEFAULT '{}',
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
priority INTEGER DEFAULT 0,
|
||||
last_success_at TIMESTAMPTZ,
|
||||
failure_count INTEGER DEFAULT 0,
|
||||
disabled_until TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Download clients
|
||||
CREATE TABLE download_clients (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
implementation TEXT NOT NULL,
|
||||
host TEXT NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
username TEXT,
|
||||
password TEXT,
|
||||
settings JSONB DEFAULT '{}',
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Unified media table (partitioned by type)
|
||||
CREATE TABLE media (
|
||||
id BIGSERIAL,
|
||||
media_type MEDIA_TYPE NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
sort_title TEXT NOT NULL,
|
||||
original_title TEXT,
|
||||
overview TEXT,
|
||||
year INTEGER,
|
||||
status MEDIA_STATUS NOT NULL DEFAULT 'unavailable',
|
||||
monitored BOOLEAN NOT NULL DEFAULT false,
|
||||
external_ids JSONB NOT NULL DEFAULT '{}',
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
images JSONB NOT NULL DEFAULT '[]',
|
||||
quality_profile_id INTEGER REFERENCES quality_profiles(id),
|
||||
root_folder_id INTEGER REFERENCES root_folders(id),
|
||||
current_quality JSONB,
|
||||
desired_quality JSONB,
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_search_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
PRIMARY KEY (id, media_type)
|
||||
) PARTITION BY LIST (media_type);
|
||||
|
||||
CREATE TABLE media_movie PARTITION OF media FOR VALUES IN ('movie');
|
||||
CREATE TABLE media_series PARTITION OF media FOR VALUES IN ('series');
|
||||
CREATE TABLE media_episode PARTITION OF media FOR VALUES IN ('episode');
|
||||
CREATE TABLE media_music PARTITION OF media FOR VALUES IN ('music');
|
||||
CREATE TABLE media_album PARTITION OF media FOR VALUES IN ('album');
|
||||
CREATE TABLE media_audiobook PARTITION OF media FOR VALUES IN ('audiobook');
|
||||
CREATE TABLE media_podcast PARTITION OF media FOR VALUES IN ('podcast');
|
||||
CREATE TABLE media_photo PARTITION OF media FOR VALUES IN ('photo');
|
||||
CREATE TABLE media_other PARTITION OF media FOR VALUES IN ('other');
|
||||
|
||||
-- Media indexes
|
||||
CREATE INDEX idx_media_title ON media USING gin (to_tsvector('english', coalesce(title, '')));
|
||||
CREATE INDEX idx_media_monitored ON media (monitored) WHERE monitored = true;
|
||||
CREATE INDEX idx_media_status ON media (status, media_type);
|
||||
CREATE INDEX idx_media_external_ids ON media USING gin (external_ids);
|
||||
|
||||
-- Media relations (series->episodes, album->tracks)
|
||||
CREATE TABLE media_relations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
parent_id BIGINT NOT NULL,
|
||||
child_id BIGINT NOT NULL,
|
||||
relation TEXT NOT NULL,
|
||||
position INTEGER,
|
||||
season INTEGER,
|
||||
UNIQUE(parent_id, child_id, relation)
|
||||
);
|
||||
|
||||
-- Media tags
|
||||
CREATE TABLE media_tags (
|
||||
media_id BIGINT NOT NULL,
|
||||
media_type MEDIA_TYPE NOT NULL,
|
||||
tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (media_id, media_type, tag_id)
|
||||
);
|
||||
|
||||
-- Unified file tracking
|
||||
CREATE TABLE media_files (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
media_id BIGINT NOT NULL,
|
||||
media_type MEDIA_TYPE NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
original_path TEXT,
|
||||
file_name TEXT NOT NULL,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
quality JSONB NOT NULL DEFAULT '{}',
|
||||
codec TEXT,
|
||||
resolution TEXT,
|
||||
source TEXT,
|
||||
is_hardlinked BOOLEAN DEFAULT false,
|
||||
checksum TEXT,
|
||||
transcode_status TEXT DEFAULT 'none',
|
||||
transcode_preset TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
UNIQUE(media_id, media_type, path)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_media_files_media ON media_files (media_id, media_type);
|
||||
CREATE INDEX idx_media_files_transcode ON media_files (transcode_status) WHERE transcode_status != 'done';
|
||||
|
||||
-- Download queue
|
||||
CREATE TABLE download_queue (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
media_id BIGINT NOT NULL,
|
||||
media_type MEDIA_TYPE NOT NULL,
|
||||
release_title TEXT NOT NULL,
|
||||
release_url TEXT,
|
||||
indexer TEXT NOT NULL,
|
||||
download_client TEXT NOT NULL,
|
||||
quality JSONB NOT NULL DEFAULT '{}',
|
||||
size BIGINT,
|
||||
protocol TEXT NOT NULL DEFAULT 'torrent',
|
||||
status QUEUE_STATUS NOT NULL DEFAULT 'pending',
|
||||
progress REAL DEFAULT 0,
|
||||
error_message TEXT,
|
||||
batch_id UUID,
|
||||
priority INTEGER DEFAULT 0,
|
||||
retry_count INTEGER DEFAULT 0,
|
||||
max_retries INTEGER DEFAULT 3,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_queue_status ON download_queue (status, priority DESC);
|
||||
CREATE INDEX idx_queue_batch ON download_queue (batch_id) WHERE batch_id IS NOT NULL;
|
||||
CREATE INDEX idx_queue_media ON download_queue (media_id, media_type);
|
||||
|
||||
-- Blocklist
|
||||
CREATE TABLE blocklist (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
release_title TEXT NOT NULL,
|
||||
source_title TEXT,
|
||||
quality JSONB DEFAULT '{}',
|
||||
indexer TEXT,
|
||||
protocol TEXT DEFAULT 'torrent',
|
||||
torrent_hash TEXT,
|
||||
size BIGINT,
|
||||
message TEXT,
|
||||
media_id BIGINT,
|
||||
media_type MEDIA_TYPE,
|
||||
block_reason TEXT DEFAULT 'manual',
|
||||
auto_expires_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_blocklist_media ON blocklist (media_id, media_type);
|
||||
CREATE INDEX idx_blocklist_expires ON blocklist (auto_expires_at) WHERE auto_expires_at IS NOT NULL;
|
||||
|
||||
-- Task execution log
|
||||
CREATE TABLE task_executions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
task_id INTEGER NOT NULL REFERENCES scheduled_tasks(id) ON DELETE CASCADE,
|
||||
status TEXT NOT NULL DEFAULT 'running',
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ended_at TIMESTAMPTZ,
|
||||
duration_ms INTEGER,
|
||||
result JSONB,
|
||||
error TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_task_exec_task ON task_executions (task_id, started_at DESC);
|
||||
|
||||
-- Download history (partitioned for auto-cleanup)
|
||||
CREATE TABLE download_history (
|
||||
id BIGSERIAL,
|
||||
media_id BIGINT NOT NULL,
|
||||
media_type MEDIA_TYPE NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
release_title TEXT,
|
||||
quality JSONB DEFAULT '{}',
|
||||
indexer TEXT,
|
||||
client TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (id, created_at)
|
||||
) PARTITION BY RANGE (created_at);
|
||||
|
||||
CREATE TABLE download_history_current PARTITION OF download_history
|
||||
FOR VALUES FROM (CURRENT_DATE - INTERVAL '90 days') TO (MAXVALUE);
|
||||
Reference in New Issue
Block a user