migrations: more database migrations for various database fixes
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I8567eec6980b2b5453687bcbd07a61206a6a6964
This commit is contained in:
parent
c4adc4e3e0
commit
56d44e120a
8 changed files with 418 additions and 0 deletions
15
migrations/postgres/V6__plugin_system.sql
Normal file
15
migrations/postgres/V6__plugin_system.sql
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
-- Plugin registry table
|
||||
CREATE TABLE plugin_registry (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
config_json TEXT,
|
||||
manifest_json TEXT,
|
||||
installed_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
);
|
||||
|
||||
-- Index for quick lookups
|
||||
CREATE INDEX idx_plugin_registry_enabled ON plugin_registry(enabled);
|
||||
CREATE INDEX idx_plugin_registry_name ON plugin_registry(name);
|
||||
35
migrations/postgres/V7__user_management.sql
Normal file
35
migrations/postgres/V7__user_management.sql
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
-- Users table
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role JSONB NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
);
|
||||
|
||||
-- User profiles table
|
||||
CREATE TABLE user_profiles (
|
||||
user_id UUID PRIMARY KEY,
|
||||
avatar_path TEXT,
|
||||
bio TEXT,
|
||||
preferences_json JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- User library access table
|
||||
CREATE TABLE user_libraries (
|
||||
user_id UUID NOT NULL,
|
||||
root_path TEXT NOT NULL,
|
||||
permission JSONB NOT NULL,
|
||||
granted_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
PRIMARY KEY (user_id, root_path),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Indexes for efficient lookups
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
CREATE INDEX idx_user_libraries_user_id ON user_libraries(user_id);
|
||||
CREATE INDEX idx_user_libraries_root_path ON user_libraries(root_path);
|
||||
131
migrations/postgres/V8__media_server_features.sql
Normal file
131
migrations/postgres/V8__media_server_features.sql
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
-- Ratings
|
||||
CREATE TABLE IF NOT EXISTS ratings (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
stars INTEGER NOT NULL CHECK (stars >= 1 AND stars <= 5),
|
||||
review_text TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(user_id, media_id)
|
||||
);
|
||||
|
||||
-- Comments
|
||||
CREATE TABLE IF NOT EXISTS comments (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
parent_comment_id UUID REFERENCES comments(id) ON DELETE CASCADE,
|
||||
text TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Favorites
|
||||
CREATE TABLE IF NOT EXISTS favorites (
|
||||
user_id UUID NOT NULL,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (user_id, media_id)
|
||||
);
|
||||
|
||||
-- Share links
|
||||
CREATE TABLE IF NOT EXISTS share_links (
|
||||
id UUID PRIMARY KEY,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
created_by UUID NOT NULL,
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT,
|
||||
expires_at TIMESTAMPTZ,
|
||||
view_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Playlists
|
||||
CREATE TABLE IF NOT EXISTS playlists (
|
||||
id UUID PRIMARY KEY,
|
||||
owner_id UUID NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
is_public BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_smart BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
filter_query TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Playlist items
|
||||
CREATE TABLE IF NOT EXISTS playlist_items (
|
||||
playlist_id UUID NOT NULL REFERENCES playlists(id) ON DELETE CASCADE,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
position INTEGER NOT NULL DEFAULT 0,
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (playlist_id, media_id)
|
||||
);
|
||||
|
||||
-- Usage events
|
||||
CREATE TABLE IF NOT EXISTS usage_events (
|
||||
id UUID PRIMARY KEY,
|
||||
media_id UUID REFERENCES media_items(id) ON DELETE SET NULL,
|
||||
user_id UUID,
|
||||
event_type TEXT NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
duration_secs DOUBLE PRECISION,
|
||||
context_json JSONB
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_events_media ON usage_events(media_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_events_user ON usage_events(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_events_timestamp ON usage_events(timestamp);
|
||||
|
||||
-- Watch history / progress
|
||||
CREATE TABLE IF NOT EXISTS watch_history (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
progress_secs DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
last_watched TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(user_id, media_id)
|
||||
);
|
||||
|
||||
-- Subtitles
|
||||
CREATE TABLE IF NOT EXISTS subtitles (
|
||||
id UUID PRIMARY KEY,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
language TEXT,
|
||||
format TEXT NOT NULL,
|
||||
file_path TEXT,
|
||||
is_embedded BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
track_index INTEGER,
|
||||
offset_ms INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_subtitles_media ON subtitles(media_id);
|
||||
|
||||
-- External metadata (enrichment)
|
||||
CREATE TABLE IF NOT EXISTS external_metadata (
|
||||
id UUID PRIMARY KEY,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
source TEXT NOT NULL,
|
||||
external_id TEXT,
|
||||
metadata_json JSONB NOT NULL DEFAULT '{}',
|
||||
confidence DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
||||
last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_external_metadata_media ON external_metadata(media_id);
|
||||
|
||||
-- Transcode sessions
|
||||
CREATE TABLE IF NOT EXISTS transcode_sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
user_id UUID,
|
||||
profile TEXT NOT NULL,
|
||||
cache_path TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
progress DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_transcode_sessions_media ON transcode_sessions(media_id);
|
||||
26
migrations/postgres/V9__fix_indexes_and_constraints.sql
Normal file
26
migrations/postgres/V9__fix_indexes_and_constraints.sql
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
-- Drop redundant indexes (already covered by UNIQUE constraints)
|
||||
DROP INDEX IF EXISTS idx_users_username;
|
||||
DROP INDEX IF EXISTS idx_user_libraries_user_id;
|
||||
|
||||
-- Add missing indexes for comments table
|
||||
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments(media_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments(parent_comment_id);
|
||||
|
||||
-- Remove duplicates before adding unique constraint
|
||||
DELETE FROM external_metadata e1
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM external_metadata e2
|
||||
WHERE e1.media_id = e2.media_id
|
||||
AND e1.source = e2.source
|
||||
AND e1.ctid < e2.ctid
|
||||
);
|
||||
|
||||
-- Add unique constraint for external_metadata (idempotent)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'uq_external_metadata_source'
|
||||
) THEN
|
||||
ALTER TABLE external_metadata ADD CONSTRAINT uq_external_metadata_source UNIQUE(media_id, source);
|
||||
END IF;
|
||||
END $$;
|
||||
15
migrations/sqlite/V6__plugin_system.sql
Normal file
15
migrations/sqlite/V6__plugin_system.sql
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
-- Plugin registry table
|
||||
CREATE TABLE plugin_registry (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
config_json TEXT,
|
||||
manifest_json TEXT,
|
||||
installed_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Index for quick lookups
|
||||
CREATE INDEX idx_plugin_registry_enabled ON plugin_registry(enabled);
|
||||
CREATE INDEX idx_plugin_registry_name ON plugin_registry(name);
|
||||
35
migrations/sqlite/V7__user_management.sql
Normal file
35
migrations/sqlite/V7__user_management.sql
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
-- Users table
|
||||
CREATE TABLE users (
|
||||
id TEXT PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- User profiles table
|
||||
CREATE TABLE user_profiles (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
avatar_path TEXT,
|
||||
bio TEXT,
|
||||
preferences_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- User library access table
|
||||
CREATE TABLE user_libraries (
|
||||
user_id TEXT NOT NULL,
|
||||
root_path TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
granted_at TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, root_path),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Indexes for efficient lookups
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
CREATE INDEX idx_user_libraries_user_id ON user_libraries(user_id);
|
||||
CREATE INDEX idx_user_libraries_root_path ON user_libraries(root_path);
|
||||
143
migrations/sqlite/V8__media_server_features.sql
Normal file
143
migrations/sqlite/V8__media_server_features.sql
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
-- Ratings
|
||||
CREATE TABLE IF NOT EXISTS ratings (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
media_id TEXT NOT NULL,
|
||||
stars INTEGER NOT NULL CHECK (stars >= 1 AND stars <= 5),
|
||||
review_text TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE(user_id, media_id),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Comments
|
||||
CREATE TABLE IF NOT EXISTS comments (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
media_id TEXT NOT NULL,
|
||||
parent_comment_id TEXT,
|
||||
text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Favorites
|
||||
CREATE TABLE IF NOT EXISTS favorites (
|
||||
user_id TEXT NOT NULL,
|
||||
media_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
PRIMARY KEY (user_id, media_id),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Share links
|
||||
CREATE TABLE IF NOT EXISTS share_links (
|
||||
id TEXT PRIMARY KEY,
|
||||
media_id TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT,
|
||||
expires_at TEXT,
|
||||
view_count INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Playlists
|
||||
CREATE TABLE IF NOT EXISTS playlists (
|
||||
id TEXT PRIMARY KEY,
|
||||
owner_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
is_public INTEGER NOT NULL DEFAULT 0,
|
||||
is_smart INTEGER NOT NULL DEFAULT 0,
|
||||
filter_query TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Playlist items
|
||||
CREATE TABLE IF NOT EXISTS playlist_items (
|
||||
playlist_id TEXT NOT NULL,
|
||||
media_id TEXT NOT NULL,
|
||||
position INTEGER NOT NULL DEFAULT 0,
|
||||
added_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
PRIMARY KEY (playlist_id, media_id),
|
||||
FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Usage events
|
||||
CREATE TABLE IF NOT EXISTS usage_events (
|
||||
id TEXT PRIMARY KEY,
|
||||
media_id TEXT,
|
||||
user_id TEXT,
|
||||
event_type TEXT NOT NULL,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
duration_secs REAL,
|
||||
context_json TEXT,
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_events_media ON usage_events(media_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_events_user ON usage_events(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_events_timestamp ON usage_events(timestamp);
|
||||
|
||||
-- Watch history / progress
|
||||
CREATE TABLE IF NOT EXISTS watch_history (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
media_id TEXT NOT NULL,
|
||||
progress_secs REAL NOT NULL DEFAULT 0,
|
||||
last_watched TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE(user_id, media_id),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Subtitles
|
||||
CREATE TABLE IF NOT EXISTS subtitles (
|
||||
id TEXT PRIMARY KEY,
|
||||
media_id TEXT NOT NULL,
|
||||
language TEXT,
|
||||
format TEXT NOT NULL,
|
||||
file_path TEXT,
|
||||
is_embedded INTEGER NOT NULL DEFAULT 0,
|
||||
track_index INTEGER,
|
||||
offset_ms INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_subtitles_media ON subtitles(media_id);
|
||||
|
||||
-- External metadata (enrichment)
|
||||
CREATE TABLE IF NOT EXISTS external_metadata (
|
||||
id TEXT PRIMARY KEY,
|
||||
media_id TEXT NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
external_id TEXT,
|
||||
metadata_json TEXT NOT NULL DEFAULT '{}',
|
||||
confidence REAL NOT NULL DEFAULT 0.0,
|
||||
last_updated TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_external_metadata_media ON external_metadata(media_id);
|
||||
|
||||
-- Transcode sessions
|
||||
CREATE TABLE IF NOT EXISTS transcode_sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
media_id TEXT NOT NULL,
|
||||
user_id TEXT,
|
||||
profile TEXT NOT NULL,
|
||||
cache_path TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
progress REAL NOT NULL DEFAULT 0.0,
|
||||
error_message TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
expires_at TEXT,
|
||||
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_transcode_sessions_media ON transcode_sessions(media_id);
|
||||
18
migrations/sqlite/V9__fix_indexes_and_constraints.sql
Normal file
18
migrations/sqlite/V9__fix_indexes_and_constraints.sql
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
-- Drop redundant indexes (already covered by UNIQUE constraints)
|
||||
DROP INDEX IF EXISTS idx_users_username;
|
||||
DROP INDEX IF EXISTS idx_user_libraries_user_id;
|
||||
|
||||
-- Add missing indexes for comments table
|
||||
CREATE INDEX IF NOT EXISTS idx_comments_media ON comments(media_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_comments_parent ON comments(parent_comment_id);
|
||||
|
||||
-- Remove duplicates before adding unique index (keep the first row)
|
||||
DELETE FROM external_metadata
|
||||
WHERE rowid NOT IN (
|
||||
SELECT MIN(rowid)
|
||||
FROM external_metadata
|
||||
GROUP BY media_id, source
|
||||
);
|
||||
|
||||
-- Add unique index for external_metadata to prevent duplicates
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_external_metadata_source ON external_metadata(media_id, source);
|
||||
Loading…
Add table
Add a link
Reference in a new issue