diff --git a/API-Surface.md b/API-Surface.md index 2e9655f..3d47540 100644 --- a/API-Surface.md +++ b/API-Surface.md @@ -1,8 +1,8 @@ # API Surface -71 API endpoints grouped by domain. All served by FastAPI under `/api/v1/`. +74 API endpoints grouped by domain. All served by FastAPI under `/api/v1/`. -## Public Endpoints (10) +## Public Endpoints (11) | Method | Path | Response Shape | Notes | |--------|------|---------------|-------| @@ -16,6 +16,7 @@ | GET | `/api/v1/techniques/{slug}` | 22-field object | Full technique detail with relations | | GET | `/api/v1/techniques/{slug}/versions` | `{items, total}` | Version history | | GET | `/api/v1/techniques/{slug}/versions/{n}` | Version detail | Single version | +| GET | `/api/v1/public/shorts/{share_token}` | `{format, dimensions, duration, creator_name, highlight_title, download_url}` | Unauthenticated. Returns metadata + presigned MinIO URL. 404 for missing/non-complete shorts (M024/S01) | ### Technique Detail Fields (22) @@ -161,10 +162,17 @@ All under prefix `/api/v1/admin/pipeline/`. | Method | Path | Purpose | |--------|------|---------| -| POST | `/api/v1/admin/shorts/{highlight_id}/generate` | Queue short generation for approved highlight (3 presets) — 202 response | +| POST | `/api/v1/admin/shorts/{highlight_id}/generate` | Queue short generation for approved highlight (3 presets). Accepts `captions` boolean param (M024/S04) — 202 response | | GET | `/api/v1/admin/shorts/{highlight_id}` | List generated shorts for a highlight with per-preset status | | GET | `/api/v1/admin/shorts/{short_id}/download` | Presigned MinIO download URL for completed short | +### Shorts Template Admin (2) — M024/S04 + +| Method | Path | Purpose | +|--------|------|---------| +| GET | `/api/v1/admin/creators/{id}/shorts-template` | Returns JSONB template config for creator | +| PUT | `/api/v1/admin/creators/{id}/shorts-template` | Updates template config with validation (hex color, duration 1.0–5.0) | + ## Other Endpoints (2) | Method | Path | Notes | diff --git a/Chat-Engine.md b/Chat-Engine.md index bcb5927..64450a9 100644 --- a/Chat-Engine.md +++ b/Chat-Engine.md @@ -36,7 +36,7 @@ The chat endpoint returns a `text/event-stream` response with four event types i | Event | Payload | When | |-------|---------|------| -| `sources` | `[{title, slug, creator_name, summary}]` | First — citation metadata for link rendering | +| `sources` | `[{title, slug, creator_name, summary, source_video_id, start_time, end_time, video_filename}]` | First — citation metadata for link rendering. Video fields added in M024/S05 for timestamp badges. | | `token` | `string` (text chunk) | Repeated — streamed LLM completion tokens | | `done` | `{cascade_tier, conversation_id}` | Once — signals completion, includes retrieval tier and conversation ID | | `error` | `{message: string}` | On failure — emitted if LLM errors mid-stream | @@ -67,7 +67,9 @@ The LLM is instructed to reference sources using numbered citations `[N]` in its - `[1]` → links to `/techniques/:slug` for the corresponding source - Multiple citations supported: `[1][3]` or `[1,3]` -- Citation regex: `/\[(\d+)\]/g` parsed locally in both ChatPage and ChatWidget +- Citation regex: `/\[(\d+)\]/g` parsed by shared `parseChatCitations()` utility (M024/S05) +- **Timestamp badges** — when `start_time` is defined, source cards show a badge linking to `/watch/:id?t=N` (M024/S05) +- **Video filename** — displayed as subtle metadata on source cards (M024/S05) ## API Endpoint @@ -207,6 +209,22 @@ Linear: `temperature = 0.3 + weight * 0.2` If the creator has no `personality_profile` (null JSONB), the system falls back to pure encyclopedic mode regardless of weight value. DB errors during profile fetch are caught and logged — never crash the stream. +## Citation Metadata Propagation (M024/S05) + +End-to-end video metadata flow from search results through SSE to frontend source cards. + +### Backend + +- `search_service.py` — `_enrich_qdrant_results()` and `_keyword_search_and()` now batch-fetch `SourceVideo` filenames and include `source_video_id`, `start_time`, `end_time`, `video_filename` in result dicts. Non-key_moment types get empty/None values for uniform dict shape. +- `chat_service.py` — `_build_sources()` passes all four video fields through to SSE source events. + +### Frontend + +- `ChatSource` interface (in `api/chat.ts`) extended with `source_video_id`, `start_time`, `end_time`, `video_filename` fields. +- `utils/chatCitations.tsx` — shared `parseChatCitations()` replaces duplicate implementations in ChatPage and ChatWidget. Accepts CSS module styles as `Record`. +- `utils/formatTime.ts` — shared hour-aware time formatter used across timestamp badges and player controls. +- Source cards now show: timestamp badge (links to `/watch/:id?t=N` when `start_time` defined) and video filename metadata. + ## Key Files - `backend/chat_service.py` — ChatService with history load/save, retrieve-prompt-stream pipeline @@ -217,6 +235,8 @@ If the creator has no `personality_profile` (null JSONB), the system falls back - `frontend/src/pages/ChatPage.module.css` — Conversation bubble layout styles - `frontend/src/components/ChatWidget.tsx` — Floating chat widget component - `frontend/src/components/ChatWidget.module.css` — Widget styles (38 custom property refs) +- `frontend/src/utils/chatCitations.tsx` — Shared citation parser (M024/S05) +- `frontend/src/utils/formatTime.ts` — Shared time formatter (M024/S05) ## Design Decisions @@ -226,7 +246,7 @@ If the creator has no `personality_profile` (null JSONB), the system falls back - **Client-side suggested questions** — Generated from technique titles/categories without API call - **5-tier interpolation** — Progressive field inclusion replaces 3-tier step function (D044 supersedes D043) - **5-tier interpolation** — Progressive field inclusion replaces 3-tier step function (D044 supersedes D043) -- **Citation parsing duplicated** — ChatPage and ChatWidget each parse citations independently (extracted utility deferred) +- **Shared citation parsing** — `parseChatCitations()` in `utils/chatCitations.tsx` replaces duplicate implementations (M024/S05) - **Standalone ASGI test client** — Tests use mocked DB to avoid PostgreSQL dependency --- diff --git a/Data-Model.md b/Data-Model.md index 8e01a95..0194975 100644 --- a/Data-Model.md +++ b/Data-Model.md @@ -41,6 +41,7 @@ HighlightCandidate (1) ──→ (N) GeneratedShort | social_links | JSONB | Platform → URL mapping | | featured | Boolean | For homepage spotlight | | personality_profile | JSONB | LLM-extracted personality data (M022/S06). See [[Personality-Profiles]] | +| shorts_template | JSONB | Nullable — intro/outro card config with `parse_template_config` normalizer (M024/S04) | ### SourceVideo @@ -238,6 +239,9 @@ Append-only versioned record of per-field consent changes. | 023 | Add personality_profile JSONB to creators (M022/S06) | | 024 | Add posts and post_attachments tables (M023/S01) | | 025 | Add generated_shorts table with FormatPreset and ShortStatus enums (M023/S03) | +| 026 | Add share_token to generated_shorts + backfill existing complete shorts + unique index (M024/S01) | +| 027 | Add captions_enabled boolean to generated_shorts (M024/S04) | +| 028 | Add shorts_template JSONB to creators (M024/S04) | ## Schema Notes @@ -288,5 +292,7 @@ Append-only versioned record of per-field consent changes. | file_size | Integer | Nullable — bytes | | duration_secs | Float | Nullable | | error_message | Text | Nullable — on failure | +| share_token | String(16) | Nullable, unique-indexed — generated via `secrets.token_urlsafe(8)` at pipeline completion (M024/S01) | +| captions_enabled | Boolean | Default false — indicates successful ASS subtitle generation (M024/S04) | | created_at | Timestamp | | | updated_at | Timestamp | | diff --git a/Frontend.md b/Frontend.md index 3f9308a..294899c 100644 --- a/Frontend.md +++ b/Frontend.md @@ -23,6 +23,8 @@ React 18 + TypeScript + Vite SPA. No UI library, no state management library, no | `/creator/posts` | PostsList | Creator JWT | Creator post management — status badges, edit/delete (M023/S01) | | `/creator/posts/new` | PostEditor | Creator JWT | Tiptap rich text editor with file attachments (M023/S01) | | `/creator/posts/:postId/edit` | PostEditor | Creator JWT | Edit existing post (M023/S01) | +| `/shorts/:token` | ShortPlayer | Public | Public short player outside ProtectedRoute — fetches via unauthenticated API (M024/S01) | +| `/embed/:videoId` | EmbedPlayer | Public | Chrome-free embed player, registered before AppShell catch-all (M024/S03) | | `*` | → Redirect `/` | — | SPA fallback | *Admin routes have no authentication gate. @@ -111,7 +113,7 @@ Creator post management at `/creator/posts`. - **SidebarNav integration** — accessible from creator dashboard - **Files:** `PostsList.tsx`, `PostsList.module.css` -### Shorts UI (M023/S03) +### Shorts UI (M023/S03, M024/S01, S04) Shorts generation controls in HighlightQueue. @@ -119,8 +121,32 @@ Shorts generation controls in HighlightQueue. - **Per-preset status badges** — color-coded pending/processing/complete/failed with pulsing animation - **Download links** — open presigned MinIO URLs in new tab - **5s polling** — while any shorts are processing, auto-stops when all settle +- **Share link + embed code copy buttons** — 🔗 and 📋 icons on completed shorts with `share_token` (M024/S01) +- **Collapsible template config panel** — intro/outro text, duration sliders, show/hide toggles, color picker, font selection (M024/S04) +- **Per-highlight captions toggle** — checkbox to enable ASS subtitle generation (M024/S04) - **Files:** `HighlightQueue.tsx`, `HighlightQueue.module.css` (updated) +### ShortPlayer (M024/S01) + +Public short video player at `/shorts/:token`. + +- **Unauthenticated** — fetches via `fetchPublicShort()` (raw `fetch()`, no auth token injection) +- **Video rendering** — `