-- Per-user audio playback resume points. -- -- One row per (user, audio file). The floating audio player writes the -- last-known position for a track so it can resume there next time the -- same file is played, on any device the user is signed in on. -- -- audio_key is the track URL with its query string / hash stripped -- (audio URLs may be signed or cache-busted, so the path is the stable -- identity). The schema is owned by the frontend; the backend only -- stores and round-trips it. position_seconds/duration_seconds mirror -- the HTMLAudioElement's currentTime/duration (floats). -- -- updated_at is supplied by the client, not defaulted to now(), so the -- upsert can resolve multi-tab / multi-device conflicts with a -- newest-wins rule (see AudioProgressRepo.Upsert) and so anonymous -- IndexedDB entries keep their original timestamp when synced on login. CREATE TABLE IF NOT EXISTS audio_progress ( user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, audio_key TEXT NOT NULL, position_seconds DOUBLE PRECISION NOT NULL DEFAULT 0, duration_seconds DOUBLE PRECISION, updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (user_id, audio_key), CHECK (position_seconds >= 0) );