veza/veza-backend-api/migrations/040_streaming_core.sql
2025-12-16 11:23:49 -05:00

205 lines
No EOL
7.7 KiB
SQL

-- 040_streaming_core.sql
-- Core Streaming Entities: Tracks, Playlists (Aligned with ORIGIN)
-- === TRACKS ===
CREATE TABLE public.tracks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
creator_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
file_id UUID REFERENCES public.files(id) ON DELETE RESTRICT, -- NULL temporairement avant création fichier
-- Track Info
title VARCHAR(255) NOT NULL,
description TEXT,
artist VARCHAR(255),
album VARCHAR(255),
genre VARCHAR(100),
year INTEGER DEFAULT 0, -- Release year (for Go model compatibility)
-- Audio Properties
duration INTEGER NOT NULL, -- seconds
bpm INTEGER,
musical_key VARCHAR(10),
-- Visibility
visibility public.visibility NOT NULL DEFAULT 'public',
is_downloadable BOOLEAN NOT NULL DEFAULT false,
-- Media
cover_art_file_id UUID REFERENCES public.files(id) ON DELETE SET NULL,
waveform_data JSONB,
-- Counts (denormalized)
play_count INTEGER NOT NULL DEFAULT 0,
like_count INTEGER NOT NULL DEFAULT 0,
comment_count INTEGER NOT NULL DEFAULT 0,
download_count INTEGER NOT NULL DEFAULT 0,
-- Legacy/Go Compatibility fields (Denormalized or Mapped)
user_id UUID, -- Maps to creator_id
file_path VARCHAR(500), -- Maps to files.url or storage_path
file_size BIGINT, -- Maps to files.file_size
format VARCHAR(10),
bitrate INTEGER,
sample_rate INTEGER,
waveform_path VARCHAR(500), -- Legacy
cover_art_path VARCHAR(500), -- Legacy
status VARCHAR(20) DEFAULT 'uploading', -- Legacy status
status_message TEXT,
stream_status VARCHAR(20) DEFAULT 'pending', -- Legacy stream status (pending, processing, ready, error)
stream_manifest_url VARCHAR(500), -- Legacy stream manifest URL
is_public BOOLEAN DEFAULT true, -- Maps to visibility='public'
-- Timestamps
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
CONSTRAINT chk_tracks_duration_positive CHECK (duration >= 0) -- Permet 0 temporairement avant extraction métadonnées
);
-- Indexes
CREATE INDEX idx_tracks_creator_id ON public.tracks(creator_id);
CREATE INDEX idx_tracks_genre ON public.tracks(genre);
CREATE INDEX idx_tracks_visibility ON public.tracks(visibility);
CREATE INDEX idx_tracks_published_at_desc ON public.tracks(published_at DESC) WHERE published_at IS NOT NULL;
CREATE INDEX idx_tracks_play_count_desc ON public.tracks(play_count DESC);
CREATE INDEX idx_tracks_created_at_desc ON public.tracks(created_at DESC);
CREATE INDEX idx_tracks_search_gin ON public.tracks USING GIN(to_tsvector('english', title || ' ' || COALESCE(artist, '') || ' ' || COALESCE(album, '')));
-- === TRACK VERSIONS (Legacy/Go Support) ===
-- Origin doesn't strictly specify this, but code implies it. Keeping minimal.
CREATE TABLE public.track_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE,
version_number INTEGER NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL,
changelog TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX idx_track_versions_track_id ON public.track_versions(track_id);
CREATE INDEX idx_track_versions_created_at ON public.track_versions(created_at DESC);
-- === PLAYLISTS ===
CREATE TABLE public.playlists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
-- Playlist Info
name VARCHAR(255) NOT NULL,
description TEXT,
-- Media
cover_url TEXT,
-- Properties
visibility public.visibility NOT NULL DEFAULT 'public',
is_collaborative BOOLEAN NOT NULL DEFAULT false,
-- Counts
track_count INTEGER NOT NULL DEFAULT 0,
duration_seconds INTEGER NOT NULL DEFAULT 0,
follower_count INTEGER NOT NULL DEFAULT 0,
-- Legacy
title VARCHAR(200), -- Maps to name
is_public BOOLEAN DEFAULT true, -- Maps to visibility
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
-- Indexes
CREATE INDEX idx_playlists_user_id ON public.playlists(user_id);
CREATE INDEX idx_playlists_visibility ON public.playlists(visibility);
CREATE INDEX idx_playlists_created_at_desc ON public.playlists(created_at DESC);
-- === PLAYLIST TRACKS (Junction) ===
CREATE TABLE public.playlist_tracks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
playlist_id UUID NOT NULL REFERENCES public.playlists(id) ON DELETE CASCADE,
track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE,
-- Order
position INTEGER NOT NULL,
-- Metadata
added_by UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_playlist_tracks_playlist_track UNIQUE (playlist_id, track_id)
);
CREATE INDEX idx_playlist_tracks_playlist_id_position ON public.playlist_tracks(playlist_id, position);
CREATE INDEX idx_playlist_tracks_track_id ON public.playlist_tracks(track_id);
CREATE INDEX idx_playlist_tracks_added_by ON public.playlist_tracks(added_by);
-- === PLAYLIST COLLABORATORS (Legacy/Lab) ===
CREATE TABLE public.playlist_collaborators (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
playlist_id UUID NOT NULL REFERENCES public.playlists(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
permission public.playlist_permission NOT NULL DEFAULT 'read',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX idx_playlist_collaborators_playlist_id ON public.playlist_collaborators(playlist_id);
CREATE INDEX idx_playlist_collaborators_user_id ON public.playlist_collaborators(user_id);
-- === PLAYLIST FOLLOWS (Legacy/Lab - likely covered by 'follows' or custom logic) ===
CREATE TABLE public.playlist_follows (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
playlist_id UUID NOT NULL REFERENCES public.playlists(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX idx_playlist_follows_playlist_id ON public.playlist_follows(playlist_id);
CREATE INDEX idx_playlist_follows_user_id ON public.playlist_follows(user_id);
-- === QUEUES ===
CREATE TABLE public.queues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE UNIQUE,
-- Current Track
current_track_id UUID REFERENCES public.tracks(id) ON DELETE SET NULL,
current_position INTEGER NOT NULL DEFAULT 0,
-- Playback State
is_playing BOOLEAN NOT NULL DEFAULT false,
shuffle BOOLEAN NOT NULL DEFAULT false,
repeat_mode VARCHAR(20) NOT NULL DEFAULT 'off',
volume INTEGER NOT NULL DEFAULT 100,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_queues_user_id ON public.queues(user_id);
-- === QUEUE ITEMS ===
CREATE TABLE public.queue_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
queue_id UUID NOT NULL REFERENCES public.queues(id) ON DELETE CASCADE,
track_id UUID NOT NULL REFERENCES public.tracks(id) ON DELETE CASCADE,
position INTEGER NOT NULL,
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_queue_items_queue_id_position ON public.queue_items(queue_id, position);