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);