-- +goose Up CREATE TYPE source_type AS ENUM ('nfs', 'local'); CREATE TYPE source_status AS ENUM ('pending', 'mounted', 'error', 'disabled'); CREATE TYPE tag_priority AS ENUM ('tags_first', 'path_first'); CREATE TABLE library_sources ( id uuid PRIMARY KEY, name text NOT NULL, type source_type NOT NULL, nfs_server text, nfs_export text, nfs_options text NOT NULL DEFAULT 'ro,nfsvers=4.1,soft,timeo=150,retrans=3', local_path text, tag_priority tag_priority NOT NULL DEFAULT 'tags_first', accepts_uploads boolean NOT NULL DEFAULT false, write_template text, scan_interval interval, status source_status NOT NULL DEFAULT 'pending', status_message text, last_scan_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE path_patterns ( id uuid PRIMARY KEY, source_id uuid NOT NULL REFERENCES library_sources(id) ON DELETE CASCADE, position int NOT NULL, regex text NOT NULL, is_enabled boolean NOT NULL DEFAULT true, UNIQUE (source_id, position) ); CREATE TABLE artists ( id uuid PRIMARY KEY, name text NOT NULL, sort_name text NOT NULL, name_norm text NOT NULL UNIQUE, image_path text, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE albums ( id uuid PRIMARY KEY, title text NOT NULL, title_norm text NOT NULL, album_artist_id uuid NOT NULL REFERENCES artists(id), year int, cover_path text, cover_colors text[], created_at timestamptz NOT NULL DEFAULT now(), UNIQUE (album_artist_id, title_norm) ); CREATE TABLE media_files ( id uuid PRIMARY KEY, source_id uuid NOT NULL REFERENCES library_sources(id) ON DELETE CASCADE, rel_path text NOT NULL, size_bytes bigint NOT NULL, mtime timestamptz NOT NULL, content_hash bytea, sha256 bytea, upload_file_id uuid, format text NOT NULL, codec text, bitrate int, sample_rate int, bit_depth int, channels int, duration_ms int NOT NULL, track_id uuid, missing_since timestamptz, scan_error text, UNIQUE (source_id, rel_path) ); CREATE INDEX media_files_content_hash_idx ON media_files (content_hash); CREATE INDEX media_files_sha256_idx ON media_files (sha256); CREATE INDEX media_files_track_idx ON media_files (track_id); CREATE TABLE tracks ( id uuid PRIMARY KEY, title text NOT NULL, title_norm text NOT NULL, album_id uuid NOT NULL REFERENCES albums(id), disc_no int, track_no int, year int, duration_ms int NOT NULL, primary_file_id uuid REFERENCES media_files(id) ON DELETE SET NULL, explicit boolean, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX tracks_album_idx ON tracks (album_id, disc_no, track_no); -- Дубли: тот же альбом, название и длительность ±2 с ищутся по (album_id, title_norm). CREATE INDEX tracks_dedup_idx ON tracks (album_id, title_norm); ALTER TABLE media_files ADD FOREIGN KEY (track_id) REFERENCES tracks(id) ON DELETE SET NULL; CREATE TABLE track_artists ( track_id uuid REFERENCES tracks(id) ON DELETE CASCADE, artist_id uuid REFERENCES artists(id) ON DELETE CASCADE, role text NOT NULL DEFAULT 'main', position int NOT NULL, PRIMARY KEY (track_id, artist_id, role) ); CREATE INDEX track_artists_artist_idx ON track_artists (artist_id); CREATE TABLE genres ( id serial PRIMARY KEY, name text NOT NULL, name_norm text NOT NULL UNIQUE, parent_id int REFERENCES genres(id) ); CREATE TABLE track_genres ( track_id uuid REFERENCES tracks(id) ON DELETE CASCADE, genre_id int REFERENCES genres(id), source text NOT NULL, weight real NOT NULL DEFAULT 1, PRIMARY KEY (track_id, genre_id, source) ); -- +goose Down DROP TABLE track_genres; DROP TABLE genres; DROP TABLE track_artists; ALTER TABLE media_files DROP CONSTRAINT media_files_track_id_fkey; DROP TABLE tracks; DROP TABLE media_files; DROP TABLE albums; DROP TABLE artists; DROP TABLE path_patterns; DROP TABLE library_sources; DROP TYPE tag_priority; DROP TYPE source_status; DROP TYPE source_type;