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);
|
||||
5
internal/db/migrations/002_quality_upgrade_fix.sql
Normal file
5
internal/db/migrations/002_quality_upgrade_fix.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE INDEX IF NOT EXISTS idx_media_quality_upgrade
|
||||
ON media (monitored, media_type)
|
||||
WHERE monitored = true AND deleted_at IS NULL
|
||||
AND current_quality IS NOT NULL
|
||||
AND desired_quality IS NOT NULL;
|
||||
14
internal/db/migrations/003_download_clients.sql
Normal file
14
internal/db/migrations/003_download_clients.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS url TEXT;
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS api_key TEXT;
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS category TEXT NOT NULL DEFAULT 'umm';
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS priority INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS protocol TEXT NOT NULL DEFAULT 'nzb';
|
||||
|
||||
UPDATE download_clients SET url = 'http://' || host || ':' || port::text WHERE url IS NULL;
|
||||
|
||||
ALTER TABLE download_clients ALTER COLUMN url SET NOT NULL;
|
||||
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS host;
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS port;
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS username;
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS password;
|
||||
15
internal/db/migrations/004_naming_templates.sql
Normal file
15
internal/db/migrations/004_naming_templates.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE naming_templates (
|
||||
id SERIAL PRIMARY KEY,
|
||||
media_type MEDIA_TYPE NOT NULL UNIQUE,
|
||||
template TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
INSERT INTO naming_templates (media_type, template) VALUES
|
||||
('movie', '{{sanitize .Title}} ({{.Year}})/{{sanitize .Title}} ({{.Year}}) - {{.Quality}}.{{.Ext}}'),
|
||||
('series', '{{sanitize .Title}}/Season {{printf "%02d" .Season}}/{{sanitize .Title}} - S{{printf "%02d" .Season}}E{{printf "%02d" .Episode}} - {{.Quality}}.{{.Ext}}'),
|
||||
('music', '{{sanitize .Artist}}/{{sanitize .Album}}/{{printf "%02d" .Track}} - {{sanitize .Title}}.{{.Ext}}'),
|
||||
('audiobook', '{{sanitize .Author}}/{{sanitize .Title}}/{{sanitize .Title}} - Ch{{printf "%02d" .Chapter}}.{{.Ext}}'),
|
||||
('podcast', '{{sanitize .Title}}/{{sanitize .Title}} - {{.Date}}.{{.Ext}}'),
|
||||
('book', '{{sanitize .Author}}/{{sanitize .Title}} ({{.Year}})/{{sanitize .Title}} ({{.Year}}).{{.Ext}}');
|
||||
12
internal/db/migrations/005_metadata_cache.sql
Normal file
12
internal/db/migrations/005_metadata_cache.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE metadata_cache (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
provider TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
media_type TEXT NOT NULL,
|
||||
data JSONB NOT NULL,
|
||||
cached_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_metadata_cache_lookup ON metadata_cache (provider, provider_id);
|
||||
CREATE INDEX idx_metadata_cache_expired ON metadata_cache (expires_at) WHERE expires_at < NOW();
|
||||
1
internal/db/migrations/006_queue_download_id.sql
Normal file
1
internal/db/migrations/006_queue_download_id.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE download_queue ADD COLUMN IF NOT EXISTS download_id TEXT;
|
||||
32
internal/db/migrations/007_users_requests.sql
Normal file
32
internal/db/migrations/007_users_requests.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Users table for API key authentication
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT NOT NULL DEFAULT '',
|
||||
role TEXT NOT NULL DEFAULT 'user',
|
||||
api_key TEXT UNIQUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_api_key ON users (api_key) WHERE api_key IS NOT NULL;
|
||||
|
||||
-- Requests table for media request workflow
|
||||
CREATE TABLE IF NOT EXISTS requests (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
media_id BIGINT,
|
||||
media_type TEXT NOT NULL DEFAULT '',
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
requested_by BIGINT NOT NULL REFERENCES users(id),
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
quality_profile_id INTEGER REFERENCES quality_profiles(id),
|
||||
root_folder_id INTEGER REFERENCES root_folders(id),
|
||||
notes TEXT DEFAULT '',
|
||||
reviewed_by BIGINT REFERENCES users(id),
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_requests_status ON requests (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_requests_requested_by ON requests (requested_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_requests_media ON requests (media_id, media_type);
|
||||
24
internal/db/migrations/008_activity_events.sql
Normal file
24
internal/db/migrations/008_activity_events.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
-- Activity events table for unified event logging
|
||||
CREATE TYPE EVENT_TYPE AS ENUM (
|
||||
'grab', 'import', 'download_complete', 'download_failed',
|
||||
'quality_upgrade', 'safety_block', 'error', 'info'
|
||||
);
|
||||
|
||||
CREATE TABLE activity_events (
|
||||
id BIGSERIAL,
|
||||
event_type EVENT_TYPE NOT NULL,
|
||||
media_id BIGINT,
|
||||
media_type MEDIA_TYPE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
data JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (id, created_at)
|
||||
) PARTITION BY RANGE (created_at);
|
||||
|
||||
CREATE TABLE activity_events_current PARTITION OF activity_events
|
||||
FOR VALUES FROM (CURRENT_DATE - INTERVAL '30 days') TO (MAXVALUE);
|
||||
|
||||
CREATE INDEX idx_activity_type ON activity_events (event_type, created_at DESC);
|
||||
CREATE INDEX idx_activity_media ON activity_events (media_id, media_type, created_at DESC) WHERE media_id IS NOT NULL;
|
||||
CREATE INDEX idx_activity_created ON activity_events (created_at DESC);
|
||||
45
internal/db/migrations/009_notifications.sql
Normal file
45
internal/db/migrations/009_notifications.sql
Normal file
@@ -0,0 +1,45 @@
|
||||
-- Notification system schema
|
||||
CREATE TYPE NOTIFICATION_CHANNEL_TYPE AS ENUM ('webhook', 'telegram');
|
||||
CREATE TYPE NOTIFICATION_STATUS AS ENUM ('pending', 'delivering', 'delivered', 'failed', 'dead');
|
||||
|
||||
CREATE TABLE notification_channels (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
type NOTIFICATION_CHANNEL_TYPE NOT NULL,
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
config JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE notification_subscriptions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
channel_id BIGINT NOT NULL REFERENCES notification_channels(id) ON DELETE CASCADE,
|
||||
event_type EVENT_TYPE NOT NULL,
|
||||
UNIQUE(channel_id, event_type)
|
||||
);
|
||||
|
||||
CREATE TABLE notification_queue (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
channel_id BIGINT NOT NULL REFERENCES notification_channels(id) ON DELETE CASCADE,
|
||||
event_type EVENT_TYPE NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
message JSONB NOT NULL DEFAULT '{}',
|
||||
status NOTIFICATION_STATUS DEFAULT 'pending',
|
||||
attempts INT DEFAULT 0,
|
||||
max_attempts INT DEFAULT 5,
|
||||
last_error TEXT,
|
||||
next_retry_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
delivered_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE notification_state (
|
||||
id INT PRIMARY KEY DEFAULT 1 CHECK (id = 1),
|
||||
last_event_id BIGINT DEFAULT 0,
|
||||
last_event_created_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_notification_queue_status ON notification_queue (status, next_retry_at);
|
||||
|
||||
INSERT INTO notification_state (id) VALUES (1) ON CONFLICT DO NOTHING;
|
||||
7
internal/db/migrations/010_media_release_date.sql
Normal file
7
internal/db/migrations/010_media_release_date.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- Add release_date column for calendar view
|
||||
ALTER TABLE media ADD COLUMN IF NOT EXISTS release_date TIMESTAMPTZ;
|
||||
|
||||
-- Partial index for efficient calendar range queries on monitored, non-deleted items
|
||||
CREATE INDEX IF NOT EXISTS idx_media_release_date_monitored
|
||||
ON media (release_date)
|
||||
WHERE release_date IS NOT NULL AND monitored = true AND deleted_at IS NULL;
|
||||
15
internal/db/migrations/011_performance_indexes.sql
Normal file
15
internal/db/migrations/011_performance_indexes.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- Add has_files column to avoid correlated subquery per row
|
||||
ALTER TABLE media ADD COLUMN IF NOT EXISTS has_files BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- Backfill from existing data
|
||||
UPDATE media SET has_files = EXISTS (SELECT 1 FROM media_files mf WHERE mf.media_id = media.id AND mf.deleted_at IS NULL);
|
||||
|
||||
-- Add index for the upgrade detection query
|
||||
CREATE INDEX IF NOT EXISTS idx_media_upgrade_candidates
|
||||
ON media (media_type) WHERE monitored = true AND has_files = true
|
||||
AND current_quality IS NOT NULL AND desired_quality IS NOT NULL
|
||||
AND current_quality::text != desired_quality::text;
|
||||
|
||||
-- Add trigram index for substring title search
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
CREATE INDEX IF NOT EXISTS idx_media_title_trgm ON media USING gin (title gin_trgm_ops);
|
||||
14
internal/db/migrations/012_subtitle_cache.sql
Normal file
14
internal/db/migrations/012_subtitle_cache.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
-- Subtitle cache table to avoid filesystem glob on every detail request
|
||||
CREATE TABLE IF NOT EXISTS media_subtitles (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
media_file_id BIGINT NOT NULL REFERENCES media_files(id) ON DELETE CASCADE,
|
||||
file_name TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
language_code TEXT NOT NULL,
|
||||
hi BOOLEAN NOT NULL DEFAULT false,
|
||||
forced BOOLEAN NOT NULL DEFAULT false,
|
||||
source TEXT NOT NULL DEFAULT 'downloaded',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_media_subtitles_file ON media_subtitles (media_file_id);
|
||||
21
internal/db/migrations/013_fix_download_clients_schema.sql
Normal file
21
internal/db/migrations/013_fix_download_clients_schema.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
-- Fix: migration 003 was tracked but ALTER TABLE statements did not persist.
|
||||
-- Re-apply the schema changes to download_clients.
|
||||
|
||||
-- Add missing columns
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS url TEXT;
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS api_key TEXT;
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS category TEXT NOT NULL DEFAULT 'umm';
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS priority INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE download_clients ADD COLUMN IF NOT EXISTS protocol TEXT NOT NULL DEFAULT 'nzb';
|
||||
|
||||
-- Migrate existing rows: combine host+port into url
|
||||
UPDATE download_clients SET url = 'http://' || host || ':' || port::text WHERE url IS NULL;
|
||||
|
||||
-- Now url should be populated — make it NOT NULL
|
||||
ALTER TABLE download_clients ALTER COLUMN url SET NOT NULL;
|
||||
|
||||
-- Drop old columns
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS host;
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS port;
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS username;
|
||||
ALTER TABLE download_clients DROP COLUMN IF EXISTS password;
|
||||
Reference in New Issue
Block a user