Files
music-agregator/nix/sql/music/002_schema.sql
T
Alexander 908c37f73f Add NixOS module for full agregators stack with VM test
Module manages PostgreSQL (both databases + schema init), qBittorrent
(VPN-confined via VPN-Confinement), Jackett, metadata-agregator,
musicfs, and the main orchestrator. All services are independently
enableable with auto-wired inter-service configuration.

Includes NixOS VM test validating PostgreSQL setup, schema
initialization, service startup, and directory creation.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/claude-agent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 12:39:04 +02:00

167 lines
6.3 KiB
SQL

CREATE TYPE monitor_state AS ENUM ('monitored', 'unmonitored', 'excluded');
CREATE TABLE artists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
external_id VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(500) NOT NULL,
artist_type VARCHAR(50),
country VARCHAR(10),
genres TEXT[],
image_url TEXT,
monitor_state monitor_state NOT NULL DEFAULT 'unmonitored',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE albums (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
external_id VARCHAR(255) NOT NULL UNIQUE,
artist_id UUID NOT NULL REFERENCES artists(id) ON DELETE CASCADE,
title VARCHAR(500) NOT NULL,
album_type VARCHAR(50),
release_date DATE,
total_tracks INT,
total_discs INT DEFAULT 1,
label VARCHAR(255),
genres TEXT[],
cover_url TEXT,
monitor_state monitor_state NOT NULL DEFAULT 'unmonitored',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_albums_artist ON albums(artist_id);
CREATE INDEX idx_albums_monitored ON albums(monitor_state) WHERE monitor_state = 'monitored';
CREATE TABLE tracks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
external_id VARCHAR(255) NOT NULL UNIQUE,
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
title VARCHAR(500) NOT NULL,
duration_ms INT,
isrc VARCHAR(20),
disc_number INT DEFAULT 1,
track_number INT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_tracks_album ON tracks(album_id);
CREATE TABLE torrents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
info_hash VARCHAR(40) NOT NULL UNIQUE,
tracker VARCHAR(100) NOT NULL,
title TEXT NOT NULL,
format VARCHAR(20) NOT NULL,
quality VARCHAR(20),
source VARCHAR(20),
bit_depth INT,
sample_rate INT,
seeders INT DEFAULT 0,
peers INT DEFAULT 0,
size BIGINT,
track_count INT,
has_cover_art BOOLEAN DEFAULT FALSE,
has_cue_sheet BOOLEAN DEFAULT FALSE,
has_rip_log BOOLEAN DEFAULT FALSE,
download_link TEXT,
torrent_file BYTEA,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_torrents_album ON torrents(album_id);
CREATE INDEX idx_torrents_album_format ON torrents(album_id, format);
CREATE TYPE download_state AS ENUM (
'pending', 'downloading', 'completed', 'failed', 'seeding', 'paused'
);
CREATE TABLE downloads (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
torrent_id UUID NOT NULL REFERENCES torrents(id) ON DELETE CASCADE,
album_id UUID NOT NULL,
format VARCHAR(20) NOT NULL,
quality VARCHAR(20),
state download_state NOT NULL DEFAULT 'pending',
qbit_hash VARCHAR(64),
save_path TEXT,
error_message TEXT,
queued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_unique_active_torrent
ON downloads(torrent_id)
WHERE state NOT IN ('failed');
CREATE UNIQUE INDEX idx_unique_owned_album_quality
ON downloads(album_id, format, quality)
WHERE state IN ('completed', 'seeding');
CREATE INDEX idx_downloads_state ON downloads(state);
CREATE INDEX idx_downloads_album ON downloads(album_id);
CREATE TABLE download_files (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
download_id UUID NOT NULL REFERENCES downloads(id) ON DELETE CASCADE,
track_id UUID REFERENCES tracks(id),
file_path TEXT NOT NULL,
file_size BIGINT NOT NULL,
file_type VARCHAR(20) NOT NULL,
sha256_hash VARCHAR(64),
verified_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_download_files_download ON download_files(download_id);
CREATE INDEX idx_download_files_hash ON download_files(sha256_hash) WHERE sha256_hash IS NOT NULL;
CREATE TABLE album_releases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
download_id UUID NOT NULL REFERENCES downloads(id) ON DELETE CASCADE,
format VARCHAR(20) NOT NULL,
bit_depth INT,
sample_rate INT,
channels INT DEFAULT 2,
is_lossless BOOLEAN NOT NULL DEFAULT FALSE,
source VARCHAR(20),
total_size BIGINT NOT NULL DEFAULT 0,
total_duration_ms INT NOT NULL DEFAULT 0,
track_count INT NOT NULL DEFAULT 0,
has_cover_art BOOLEAN DEFAULT FALSE,
has_cue_sheet BOOLEAN DEFAULT FALSE,
has_rip_log BOOLEAN DEFAULT FALSE,
path TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_album_releases_album ON album_releases(album_id);
CREATE UNIQUE INDEX idx_album_releases_download ON album_releases(download_id);
CREATE TABLE track_releases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
album_release_id UUID NOT NULL REFERENCES album_releases(id) ON DELETE CASCADE,
track_id UUID REFERENCES tracks(id),
download_file_id UUID REFERENCES download_files(id),
title VARCHAR(500) NOT NULL,
track_number INT NOT NULL DEFAULT 1,
disc_number INT NOT NULL DEFAULT 1,
duration_ms INT,
format VARCHAR(20) NOT NULL,
bit_depth INT,
sample_rate INT,
channels INT DEFAULT 2,
bitrate_kbps INT,
file_size BIGINT NOT NULL DEFAULT 0,
file_path TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_track_releases_album_release ON track_releases(album_release_id);