initial commit

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I4a6b498153eccd5407510dd541b7f4816a6a6964
This commit is contained in:
raf 2026-01-30 22:05:46 +03:00
commit 6a73d11c4b
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
124 changed files with 34856 additions and 0 deletions

View file

@ -0,0 +1,73 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE TABLE IF NOT EXISTS root_dirs (
path TEXT PRIMARY KEY NOT NULL
);
CREATE TABLE IF NOT EXISTS media_items (
id UUID PRIMARY KEY NOT NULL,
path TEXT NOT NULL UNIQUE,
file_name TEXT NOT NULL,
media_type TEXT NOT NULL,
content_hash TEXT NOT NULL UNIQUE,
file_size BIGINT NOT NULL,
title TEXT,
artist TEXT,
album TEXT,
genre TEXT,
year INTEGER,
duration_secs DOUBLE PRECISION,
description TEXT,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS tags (
id UUID PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
parent_id UUID REFERENCES tags(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_name_parent ON tags(name, COALESCE(parent_id, '00000000-0000-0000-0000-000000000000'));
CREATE TABLE IF NOT EXISTS media_tags (
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (media_id, tag_id)
);
CREATE TABLE IF NOT EXISTS collections (
id UUID PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT,
kind TEXT NOT NULL,
filter_query TEXT,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS collection_members (
collection_id UUID NOT NULL REFERENCES collections(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,
PRIMARY KEY (collection_id, media_id)
);
CREATE TABLE IF NOT EXISTS audit_log (
id UUID PRIMARY KEY NOT NULL,
media_id UUID REFERENCES media_items(id) ON DELETE SET NULL,
action TEXT NOT NULL,
details TEXT,
timestamp TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS custom_fields (
media_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
field_name TEXT NOT NULL,
field_type TEXT NOT NULL,
field_value TEXT NOT NULL,
PRIMARY KEY (media_id, field_name)
);

View file

@ -0,0 +1,11 @@
ALTER TABLE media_items ADD COLUMN IF NOT EXISTS search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', COALESCE(title, '')), 'A') ||
setweight(to_tsvector('english', COALESCE(artist, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(album, '')), 'B') ||
setweight(to_tsvector('english', COALESCE(genre, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(description, '')), 'C') ||
setweight(to_tsvector('english', COALESCE(file_name, '')), 'D')
) STORED;
CREATE INDEX IF NOT EXISTS idx_media_search ON media_items USING GIN(search_vector);

View file

@ -0,0 +1,8 @@
CREATE INDEX IF NOT EXISTS idx_audit_media_id ON audit_log(media_id);
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action);
CREATE INDEX IF NOT EXISTS idx_media_content_hash ON media_items(content_hash);
CREATE INDEX IF NOT EXISTS idx_media_media_type ON media_items(media_type);
CREATE INDEX IF NOT EXISTS idx_media_created_at ON media_items(created_at);
CREATE INDEX IF NOT EXISTS idx_media_title_trgm ON media_items USING GIN(title gin_trgm_ops);
CREATE INDEX IF NOT EXISTS idx_media_artist_trgm ON media_items USING GIN(artist gin_trgm_ops);

View file

@ -0,0 +1 @@
ALTER TABLE media_items ADD COLUMN thumbnail_path TEXT;

View file

@ -0,0 +1,77 @@
CREATE TABLE IF NOT EXISTS root_dirs (
path TEXT PRIMARY KEY NOT NULL
);
CREATE TABLE IF NOT EXISTS media_items (
id TEXT PRIMARY KEY NOT NULL,
path TEXT NOT NULL UNIQUE,
file_name TEXT NOT NULL,
media_type TEXT NOT NULL,
content_hash TEXT NOT NULL UNIQUE,
file_size INTEGER NOT NULL,
title TEXT,
artist TEXT,
album TEXT,
genre TEXT,
year INTEGER,
duration_secs REAL,
description TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tags (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
parent_id TEXT,
created_at TEXT NOT NULL,
FOREIGN KEY (parent_id) REFERENCES tags(id) ON DELETE SET NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_name_parent ON tags(name, parent_id);
CREATE TABLE IF NOT EXISTS media_tags (
media_id TEXT NOT NULL,
tag_id TEXT NOT NULL,
PRIMARY KEY (media_id, tag_id),
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS collections (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT,
kind TEXT NOT NULL,
filter_query TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS collection_members (
collection_id TEXT NOT NULL,
media_id TEXT NOT NULL,
position INTEGER NOT NULL DEFAULT 0,
added_at TEXT NOT NULL,
PRIMARY KEY (collection_id, media_id),
FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE,
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS audit_log (
id TEXT PRIMARY KEY NOT NULL,
media_id TEXT,
action TEXT NOT NULL,
details TEXT,
timestamp TEXT NOT NULL,
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS custom_fields (
media_id TEXT NOT NULL,
field_name TEXT NOT NULL,
field_type TEXT NOT NULL,
field_value TEXT NOT NULL,
PRIMARY KEY (media_id, field_name),
FOREIGN KEY (media_id) REFERENCES media_items(id) ON DELETE CASCADE
);

View file

@ -0,0 +1,27 @@
CREATE VIRTUAL TABLE IF NOT EXISTS media_fts USING fts5(
title,
artist,
album,
genre,
description,
file_name,
content='media_items',
content_rowid='rowid'
);
CREATE TRIGGER IF NOT EXISTS media_fts_insert AFTER INSERT ON media_items BEGIN
INSERT INTO media_fts(rowid, title, artist, album, genre, description, file_name)
VALUES (new.rowid, new.title, new.artist, new.album, new.genre, new.description, new.file_name);
END;
CREATE TRIGGER IF NOT EXISTS media_fts_update AFTER UPDATE ON media_items BEGIN
INSERT INTO media_fts(media_fts, rowid, title, artist, album, genre, description, file_name)
VALUES ('delete', old.rowid, old.title, old.artist, old.album, old.genre, old.description, old.file_name);
INSERT INTO media_fts(rowid, title, artist, album, genre, description, file_name)
VALUES (new.rowid, new.title, new.artist, new.album, new.genre, new.description, new.file_name);
END;
CREATE TRIGGER IF NOT EXISTS media_fts_delete AFTER DELETE ON media_items BEGIN
INSERT INTO media_fts(media_fts, rowid, title, artist, album, genre, description, file_name)
VALUES ('delete', old.rowid, old.title, old.artist, old.album, old.genre, old.description, old.file_name);
END;

View file

@ -0,0 +1,6 @@
CREATE INDEX IF NOT EXISTS idx_audit_media_id ON audit_log(media_id);
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action);
CREATE INDEX IF NOT EXISTS idx_media_content_hash ON media_items(content_hash);
CREATE INDEX IF NOT EXISTS idx_media_media_type ON media_items(media_type);
CREATE INDEX IF NOT EXISTS idx_media_created_at ON media_items(created_at);

View file

@ -0,0 +1 @@
ALTER TABLE media_items ADD COLUMN thumbnail_path TEXT;