From 5f4b960dc1ba361defd4663adc80031d6dd638ba Mon Sep 17 00:00:00 2001 From: jlightner Date: Sat, 4 Apr 2026 10:33:00 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Added=20share=5Ftoken=20column=20with?= =?UTF-8?q?=20migration=20026,=20wired=20token=20generati=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "backend/models.py" - "alembic/versions/026_add_share_token.py" - "backend/pipeline/stages.py" - "backend/routers/shorts.py" - "backend/routers/shorts_public.py" - "backend/main.py" GSD-Task: S01/T01 --- .gsd/completed-units-M023.json | 1 + .gsd/milestones/M024/slices/S01/S01-PLAN.md | 38 +- .../M024/slices/S01/S01-RESEARCH.md | 68 + .../M024/slices/S01/tasks/T01-PLAN.md | 40 + .../M024/slices/S01/tasks/T01-SUMMARY.md | 88 + .../M024/slices/S01/tasks/T02-PLAN.md | 48 + .gsd/reports/M023-2026-04-04T10-23-24.html | 16069 ++++++++++++++++ .gsd/reports/index.html | 69 +- .gsd/reports/reports.json | 16 + alembic/versions/026_add_share_token.py | 45 + backend/main.py | 3 +- backend/models.py | 3 + backend/pipeline/stages.py | 2 + backend/routers/shorts.py | 2 + backend/routers/shorts_public.py | 95 + 15 files changed, 16564 insertions(+), 23 deletions(-) create mode 100644 .gsd/completed-units-M023.json create mode 100644 .gsd/milestones/M024/slices/S01/S01-RESEARCH.md create mode 100644 .gsd/milestones/M024/slices/S01/tasks/T01-PLAN.md create mode 100644 .gsd/milestones/M024/slices/S01/tasks/T01-SUMMARY.md create mode 100644 .gsd/milestones/M024/slices/S01/tasks/T02-PLAN.md create mode 100644 .gsd/reports/M023-2026-04-04T10-23-24.html create mode 100644 alembic/versions/026_add_share_token.py create mode 100644 backend/routers/shorts_public.py diff --git a/.gsd/completed-units-M023.json b/.gsd/completed-units-M023.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/.gsd/completed-units-M023.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/.gsd/milestones/M024/slices/S01/S01-PLAN.md b/.gsd/milestones/M024/slices/S01/S01-PLAN.md index 8befe8f..a6d2044 100644 --- a/.gsd/milestones/M024/slices/S01/S01-PLAN.md +++ b/.gsd/milestones/M024/slices/S01/S01-PLAN.md @@ -1,6 +1,42 @@ # S01: [A] Shorts Publishing Flow -**Goal:** Build the shorts publishing flow from approval to shareable output +**Goal:** Creator approves a short → it renders → gets a shareable URL and embed code **Demo:** After this: Creator approves a short → it renders → gets a shareable URL and embed code ## Tasks +- [x] **T01: Added share_token column with migration 026, wired token generation into pipeline completion, and created public unauthenticated endpoint GET /api/v1/public/shorts/{share_token}** — Add a `share_token` column (String, nullable, unique indexed) to the `GeneratedShort` model. Create Alembic migration 026. Wire token generation into `stage_generate_shorts` at the point where status is set to `complete`. Add `share_token` to the existing `GeneratedShortResponse` so the admin UI can see it. Backfill existing complete shorts with tokens via a one-time migration data step. Create a new public (unauthenticated) API endpoint `GET /api/v1/shorts/public/{share_token}` that resolves the token to video metadata (format_preset, dimensions, duration, creator name, highlight title) plus a fresh MinIO presigned download URL. Register the new public router in `main.py`. + +## Steps + +1. Add `share_token: Mapped[str | None]` column to `GeneratedShort` in `backend/models.py`. Use `String(16)`, nullable, with a unique index. +2. Create `alembic/versions/026_add_share_token.py` migration: add column, create unique index, backfill existing `complete` rows with `secrets.token_urlsafe(8)` tokens. +3. In `backend/pipeline/stages.py` `stage_generate_shorts`, after setting `short.status = ShortStatus.complete`, generate and set `short.share_token = secrets.token_urlsafe(8)`. +4. Add `share_token: str | None = None` to `GeneratedShortResponse` in `backend/routers/shorts.py` and include it in the response construction. +5. Create `backend/routers/shorts_public.py` with a single endpoint: `GET /public/shorts/{share_token}`. Query `GeneratedShort` by share_token, join to `HighlightCandidate` → `KeyMoment` → `SourceVideo` to get metadata. Return presigned URL via `minio_client.generate_download_url`. No auth dependency. +6. Register the new router in `backend/main.py`: `app.include_router(shorts_public.router, prefix="/api/v1")`. +7. Verify: `alembic upgrade head` succeeds. Python can import the new router without errors. + - Estimate: 1.5h + - Files: backend/models.py, alembic/versions/026_add_share_token.py, backend/pipeline/stages.py, backend/routers/shorts.py, backend/routers/shorts_public.py, backend/main.py + - Verify: cd backend && python -c "from routers.shorts_public import router; print('import ok')" && cd .. && echo 'alembic migration file exists:' && test -f alembic/versions/026_add_share_token.py && echo 'OK' +- [ ] **T02: Public shorts page and share/embed buttons on HighlightQueue** — Create the `/shorts/:token` public frontend page with a video player and metadata display. Add share URL copy and embed snippet copy buttons to the HighlightQueue page for completed shorts. Create the frontend API client for the public endpoint. + +## Steps + +1. Add `fetchPublicShort` function to `frontend/src/api/shorts.ts` — `GET /api/v1/public/shorts/{token}`, no auth header. Define `PublicShortResponse` type with fields: `video_url`, `format_preset`, `width`, `height`, `duration_secs`, `creator_name`, `highlight_title`, `share_token`. +2. Create `frontend/src/pages/ShortPlayer.tsx` — a lightweight public page. Uses `useParams<{ token: string }>()`. Fetches public short data on mount. Renders a `