From 7d11b729a511b2477ef014159a91fbacab6d2784 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 11 May 2026 15:53:27 +0200 Subject: [PATCH] Add DB migration for workflow_runs, album_events tables and cancelled download state Ultraworked with [Sisyphus](https://github.com/code-yeongyu/claude-agent) Co-authored-by: Sisyphus --- .../database/music-agregator/002_schema.sql | 112 ++++++++++++++++++ .../music-agregator/003_event_bus.sql | 33 ++++++ 2 files changed, 145 insertions(+) create mode 100644 containers/database/music-agregator/002_schema.sql create mode 100644 containers/database/music-agregator/003_event_bus.sql diff --git a/containers/database/music-agregator/002_schema.sql b/containers/database/music-agregator/002_schema.sql new file mode 100644 index 0000000..fd09b65 --- /dev/null +++ b/containers/database/music-agregator/002_schema.sql @@ -0,0 +1,112 @@ +CREATE TYPE monitor_state AS ENUM ('unmonitored', 'monitored', 'excluded'); +CREATE TYPE download_state AS ENUM ('pending', 'downloading', 'completed', 'failed', 'seeding'); + +CREATE TABLE artists ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + external_id VARCHAR(255) NOT NULL UNIQUE, + name VARCHAR(255) NOT NULL, + artist_type VARCHAR(50) NOT NULL, + country VARCHAR(10), + genres TEXT[] NOT NULL DEFAULT '{}', + 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 INDEX idx_artists_monitor_state ON artists(monitor_state); + +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(255) NOT NULL, + album_type VARCHAR(50) NOT NULL, + release_date DATE, + total_tracks INTEGER NOT NULL DEFAULT 0, + total_discs INTEGER NOT NULL DEFAULT 1, + label VARCHAR(255), + genres TEXT[] NOT NULL DEFAULT '{}', + 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_id ON albums(artist_id); +CREATE INDEX idx_albums_monitor_state ON albums(monitor_state); + +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(255) NOT NULL, + duration_ms INTEGER NOT NULL DEFAULT 0, + isrc VARCHAR(20), + disc_number INTEGER NOT NULL DEFAULT 1, + track_number INTEGER NOT NULL DEFAULT 1, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_tracks_album_id 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(64) NOT NULL UNIQUE, + tracker VARCHAR(100) NOT NULL, + title TEXT NOT NULL, + format VARCHAR(20) NOT NULL, + quality VARCHAR(20) NOT NULL, + source VARCHAR(50), + bit_depth INTEGER, + sample_rate INTEGER, + seeders INTEGER NOT NULL DEFAULT 0, + peers INTEGER NOT NULL DEFAULT 0, + size BIGINT NOT NULL DEFAULT 0, + track_count INTEGER NOT NULL DEFAULT 0, + has_cover_art BOOLEAN NOT NULL DEFAULT false, + has_cue_sheet BOOLEAN NOT NULL DEFAULT false, + has_rip_log BOOLEAN NOT NULL 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_id ON torrents(album_id); + +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 REFERENCES albums(id) ON DELETE CASCADE, + format VARCHAR(20) NOT NULL, + quality VARCHAR(20) NOT NULL, + 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 INDEX idx_downloads_album_id ON downloads(album_id); +CREATE INDEX idx_downloads_torrent_id ON downloads(torrent_id); +CREATE INDEX idx_downloads_state ON downloads(state); + +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) ON DELETE SET NULL, + file_path TEXT NOT NULL, + file_size BIGINT NOT NULL DEFAULT 0, + file_type VARCHAR(50) NOT NULL, + sha256_hash VARCHAR(64), + verified_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_download_files_download_id ON download_files(download_id); diff --git a/containers/database/music-agregator/003_event_bus.sql b/containers/database/music-agregator/003_event_bus.sql new file mode 100644 index 0000000..6e8ec7c --- /dev/null +++ b/containers/database/music-agregator/003_event_bus.sql @@ -0,0 +1,33 @@ +CREATE TABLE workflow_runs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE, + quality VARCHAR(20) NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'running', + error_message TEXT, + started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + completed_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + running_lock BOOLEAN GENERATED ALWAYS AS (CASE WHEN status = 'running' THEN TRUE ELSE NULL END) STORED, + CONSTRAINT idx_workflow_runs_active UNIQUE (album_id, quality, running_lock) +); + +CREATE INDEX idx_workflow_runs_status ON workflow_runs(status); + +CREATE TABLE album_events ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + seq BIGSERIAL NOT NULL, + workflow_run_id UUID NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE, + album_id UUID NOT NULL, + event_type VARCHAR(20) NOT NULL, + step VARCHAR(50) NOT NULL, + message TEXT NOT NULL, + data_json JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_album_events_workflow ON album_events(workflow_run_id); +CREATE INDEX idx_album_events_album ON album_events(album_id); +CREATE INDEX idx_album_events_seq ON album_events(seq); + +ALTER TYPE download_state ADD VALUE IF NOT EXISTS 'cancelled';