-- Core Entities CREATE TABLE artists ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, sort_name TEXT, artist_type TEXT, country TEXT, formed_date DATE, disbanded_date DATE, description TEXT, image_url TEXT, source TEXT NOT NULL, source_id TEXT, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE works ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, work_type TEXT, language TEXT, source TEXT NOT NULL, source_id TEXT, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE tracks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), work_id UUID REFERENCES works(id), title TEXT NOT NULL, duration_ms INT, isrc TEXT, explicit BOOLEAN DEFAULT false, source TEXT NOT NULL, source_id TEXT, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE labels ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, country TEXT, founded_date DATE, source TEXT NOT NULL, source_id TEXT, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE albums ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), label_id UUID REFERENCES labels(id), title TEXT NOT NULL, album_type TEXT, release_date DATE, upc TEXT, total_tracks INT, total_discs INT DEFAULT 1, cover_url TEXT, source TEXT NOT NULL, source_id TEXT, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE genres ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL UNIQUE, parent_id UUID REFERENCES genres(id) ); -- Relationships CREATE TABLE track_artists ( track_id UUID REFERENCES tracks(id) ON DELETE CASCADE, artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, role TEXT DEFAULT 'primary', position INT DEFAULT 0, PRIMARY KEY (track_id, artist_id, role) ); CREATE TABLE album_artists ( album_id UUID REFERENCES albums(id) ON DELETE CASCADE, artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, role TEXT DEFAULT 'primary', position INT DEFAULT 0, PRIMARY KEY (album_id, artist_id, role) ); CREATE TABLE album_tracks ( album_id UUID REFERENCES albums(id) ON DELETE CASCADE, track_id UUID REFERENCES tracks(id) ON DELETE CASCADE, disc_number INT DEFAULT 1, track_number INT NOT NULL, PRIMARY KEY (album_id, track_id) ); CREATE TABLE work_artists ( work_id UUID REFERENCES works(id) ON DELETE CASCADE, artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, role TEXT DEFAULT 'writer', PRIMARY KEY (work_id, artist_id, role) ); CREATE TABLE artist_genres ( artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, genre_id UUID REFERENCES genres(id) ON DELETE CASCADE, PRIMARY KEY (artist_id, genre_id) ); CREATE TABLE album_genres ( album_id UUID REFERENCES albums(id) ON DELETE CASCADE, genre_id UUID REFERENCES genres(id) ON DELETE CASCADE, PRIMARY KEY (album_id, genre_id) ); CREATE TABLE similar_artists ( artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, similar_artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, score REAL DEFAULT 0.5, PRIMARY KEY (artist_id, similar_artist_id) ); -- Content CREATE TABLE lyrics ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), track_id UUID REFERENCES tracks(id) ON DELETE CASCADE, content TEXT, synced_content JSONB, language TEXT, source TEXT NOT NULL, source_id TEXT, created_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE playlists ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, description TEXT, is_public BOOLEAN DEFAULT true, cover_url TEXT, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE playlist_tracks ( playlist_id UUID REFERENCES playlists(id) ON DELETE CASCADE, track_id UUID REFERENCES tracks(id) ON DELETE CASCADE, position INT NOT NULL, added_at TIMESTAMPTZ DEFAULT now(), PRIMARY KEY (playlist_id, track_id) ); -- External IDs CREATE TABLE artist_external_ids ( artist_id UUID REFERENCES artists(id) ON DELETE CASCADE, source TEXT NOT NULL, source_id TEXT NOT NULL, url TEXT, fetched_at TIMESTAMPTZ DEFAULT now(), PRIMARY KEY (artist_id, source, source_id) ); CREATE TABLE album_external_ids ( album_id UUID REFERENCES albums(id) ON DELETE CASCADE, source TEXT NOT NULL, source_id TEXT NOT NULL, url TEXT, fetched_at TIMESTAMPTZ DEFAULT now(), PRIMARY KEY (album_id, source, source_id) ); CREATE TABLE track_external_ids ( track_id UUID REFERENCES tracks(id) ON DELETE CASCADE, source TEXT NOT NULL, source_id TEXT NOT NULL, url TEXT, fetched_at TIMESTAMPTZ DEFAULT now(), PRIMARY KEY (track_id, source, source_id) ); -- Indexes CREATE INDEX idx_artists_name ON artists(name); CREATE INDEX idx_artists_source ON artists(source, source_id); CREATE INDEX idx_tracks_isrc ON tracks(isrc) WHERE isrc IS NOT NULL; CREATE INDEX idx_tracks_source ON tracks(source, source_id); CREATE INDEX idx_albums_upc ON albums(upc) WHERE upc IS NOT NULL; CREATE INDEX idx_albums_source ON albums(source, source_id); CREATE INDEX idx_albums_release_date ON albums(release_date); CREATE INDEX idx_genres_name ON genres(name); CREATE INDEX idx_lyrics_track_id ON lyrics(track_id); CREATE INDEX idx_playlist_tracks_position ON playlist_tracks(playlist_id, position);