"""One Watch later list per user, held by the database Revision ID: 0112_one_watch_later Revises: 0111_audit_request_id Create Date: 2026-10-20 Watch later was made on a user's first save by a read then an insert, so two first saves at once each made a list, and the reader then picked between them at random. A partial unique index holds "one per user" now. An instance that already has a second list keeps the oldest: the others' videos move into it (after its own, in their order; one it already holds is not added twice), a Home pin of one of them moves to it (unless it is pinned itself; of several, the first on the shelf), then they go — a pin left on them goes with them, as the kept list already holds one. """ import sqlalchemy as sa from alembic import op revision: str = "0112_one_watch_later" down_revision: str | None = "0111_audit_request_id" branch_labels: str | None = None depends_on: str | None = None _EXTRA = """ SELECT p.id, k.keep_id FROM playlists p JOIN ( SELECT user_id, min(id) AS keep_id FROM playlists WHERE kind = 'watch_later' GROUP BY user_id ) k ON k.user_id = p.user_id WHERE p.kind = 'watch_later' AND p.id <> k.keep_id """ def upgrade() -> None: op.execute( f""" INSERT INTO playlist_items (playlist_id, video_id, position, added_at) SELECT e.keep_id, i.video_id, (SELECT coalesce(max(position), -1) FROM playlist_items WHERE playlist_id = e.keep_id) + row_number() OVER (PARTITION BY e.keep_id ORDER BY i.playlist_id, i.position, i.id), i.added_at FROM playlist_items i JOIN ({_EXTRA}) e ON e.id = i.playlist_id ON CONFLICT ON CONSTRAINT uq_playlist_video DO NOTHING """ ) op.execute( f""" UPDATE comfort_pins c SET playlist_id = m.keep_id FROM ( SELECT DISTINCT ON (e.keep_id) p.id AS pin_id, e.keep_id FROM comfort_pins p JOIN ({_EXTRA}) e ON e.id = p.playlist_id WHERE NOT EXISTS (SELECT 1 FROM comfort_pins k WHERE k.playlist_id = e.keep_id) ORDER BY e.keep_id, p.position, p.id ) m WHERE c.id = m.pin_id """ ) op.execute(f"DELETE FROM playlists WHERE id IN (SELECT id FROM ({_EXTRA}) e)") op.create_index( "uq_playlist_watch_later", "playlists", ["user_id"], unique=True, postgresql_where=sa.text("kind = 'watch_later'"), ) def downgrade() -> None: op.drop_index("uq_playlist_watch_later", table_name="playlists")