- Existing frontend: Complete Vite+React+TS frontend at
e:\VFXB\VFXB - Studiowith 50+ components, full design system, all UI screens — NO backend wired up yet - User decisions: Keep Vite/React frontend | New separate monorepo
e:\vfxb\| FastAPI heavy + simple BFF | MVP = NLP Edit Engine
Build the full backend infrastructure (FastAPI + Worker + DB + Storage + AI) in a new Turborepo monorepo at e:\vfxb\. Wire the existing Vite/React frontend to it. MVP priority is the NLP edit engine — user types English, AI cuts the video.
- Frontend: Existing Vite+React stays at
e:\VFXB\VFXB - Studio→ becomesapps/webin new monorepo (symlink or copy) - API Layer: FastAPI Python handles all endpoints. "Both" interpreted as: simple endpoints in FastAPI routers (auth, CRUD) + heavy async processing in Redis worker. No Next.js app needed.
- Auth: Supabase Auth (JWT) — frontend uses
@supabase/supabase-jsSDK, backend validates tokens - State: Zustand added to existing Vite frontend (replace prop-drilling + custom events pattern)
- Run
npx create-turbo@latest vfxb --package-manager pnpmate:\ - Move/copy existing
VFXB - Studiointovfxb/apps/web(keep Vite config intact) - Create
apps/api/(FastAPI) andapps/worker/(RQ processor) directories - Create
packages/types/for shared TS types consumed by frontend - Create root
.env.examplewith all required keys (Supabase, OpenAI, Anthropic, Google AI, AssemblyAI, Cloudflare R2, Redis, Stripe) - Create
docker-compose.ymlfor local dev: web:5173, api:8000, worker, redis:6379 - Create
turbo.jsonwith build/dev/test/lint pipelines
e:\vfxb\turbo.jsone:\vfxb\pnpm-workspace.yamle:\vfxb\.env.examplee:\vfxb\docker-compose.ymle:\vfxb\apps/web/(existing Studio codebase)e:\vfxb\apps/api/main.py(FastAPI entry)e:\vfxb\apps/worker/worker.py(RQ entry)
- Create Supabase project → enable Email + Google + GitHub OAuth
- Run SQL schema in Supabase editor: tables
users, videos, edits, chat_messages, exports, subscriptions - Enable RLS on all tables with per-user policies
- Create
apps/api/db/schema.sqlwith full DDL - Create
apps/api/db/client.py: Supabase Python client with service key - Implement FastAPI auth middleware:
apps/api/middleware/auth.py— validates Supabase JWT, extractsuser_id, injects into request state - Create FastAPI routers:
apps/api/routers/users.py(GET /me, PATCH /me) - Wire existing
AuthScreen.tsxto real Supabase Auth: install@supabase/supabase-js, replace mockhandleAuthenticatewith SupabasesignInWithPassword+signInWithOAuth - Create
apps/web/src/lib/supabase.ts: singleton Supabase client - Create
apps/web/src/store/auth.ts: Zustand auth store withuser,session,signIn,signOut - Protect routes in
App.tsxusing real session state
- FastAPI dependency:
async def get_current_user(token: str = Depends(oauth2_scheme))→ decode Supabase JWT - Frontend:
supabase.auth.onAuthStateChange()→ update Zustand store - RLS policy pattern:
CREATE POLICY "owner" ON videos FOR ALL USING (auth.uid() = user_id)
apps/api/db/schema.sqlapps/api/db/client.pyapps/api/middleware/auth.pyapps/api/routers/users.pyapps/web/src/lib/supabase.tsapps/web/src/store/auth.ts(new Zustand store)
- Create Cloudflare R2 buckets:
vfxb-videos+vfxb-exports - Configure R2 CORS for browser uploads, set lifecycle rules
- Implement
apps/api/routers/upload.py:POST /api/upload/presigned: validates user quota, creates video DB record (status=uploading), returns S3-presigned URL + video_idPOST /api/upload/complete: sets status=processing, enqueuesanalyze_videojobPOST /api/upload/from-url: yt-dlp download → R2 upload → video record
- Implement quota check: free=3 videos, pro=unlimited (read from
users.plan) - Wire
VFXBUploadScreen.tsxto real upload:- Replace mock progress with real TUS upload (
tus-js-client) - On complete: call
/api/upload/complete, navigate to studio view with real video_id
- Replace mock progress with real TUS upload (
- Create
apps/web/src/store/video.ts: Zustand video store (currentVideo, uploadProgress, isProcessing)
apps/api/routers/upload.pyapps/api/services/storage.py(R2 client wrapper)apps/web/src/store/video.tsapps/web/src/hooks/useVideoUpload.ts
- Set up Redis Queue:
apps/worker/queue.py— job types: analyze_video, apply_edit, export_video - Step A — Transcription (
steps/transcribe.py):- Submit audio to AssemblyAI with word timestamps + speaker diarization + filler detection
- Poll until complete, store in
videos.transcript+videos.analysis.words
- Step B — Visual Analysis (
steps/visual_analysis.py):ffmpeg -vf fps=1to extract frames- Sample every 5th frame → GPT-4o Vision →
{has_face, motion_level, engagement_potential...} - Store in
videos.analysis.visual_data
- Step C — Audio Analysis (
steps/audio_analysis.py):- librosa: detect silences >1.5s, dead zones, SNR, BPM
- Store
{silences: [...], avg_energy, background_noise_db}invideos.analysis
- Step D — Virality Scoring (
steps/virality_score.py):- Send all analysis to Claude 3.5 Sonnet with structured system prompt
- Returns
{overall_score, grade, factors, top_issues, predicted_retention_curve} - Store in
videos.score_breakdown,videos.virality_score, set status=analyzed
- Wire Dashboard to real data:
GET /api/videos?user_id=me→ replace mock stats with DB query - Wire
VideoIntelligenceCard+AIDirectorPanelto real analysis data from video store
apps/worker/queue.pyapps/worker/steps/transcribe.pyapps/worker/steps/visual_analysis.pyapps/worker/steps/audio_analysis.pyapps/worker/steps/virality_score.pyapps/api/routers/videos.py(CRUD + analysis results)
- NLP Parser (
apps/api/services/nlp_parser.py):- Takes user message + full video context (transcript, silences, analysis, score)
- Sends to Claude 3.5 Sonnet with VFXB system prompt
- Returns structured
{understood_intent, edits[], response_message, needs_confirmation}
- FFmpeg Executor (
apps/worker/services/ffmpeg_executor.py):- Implement:
cut_silence,trim_range,change_speed,add_captions,platform_export,color_grade,extract_best_clip - Each func: download from R2 → run FFmpeg → upload output to R2 → return URL
- Safety: validate all paths, sanitize filenames, enforce max duration
- Implement:
- Chat API (
apps/api/routers/chat.py):POST /api/chat/message: load video context → load last 10 messages → route to correct AI agent → parse edit intent → stream SSE response- SSE events:
{type: "text", content},{type: "edit_plan", plan},{type: "done"} POST /api/chat/confirm-edit: if confirmed → queue apply_edit jobGET /api/chat/history/:video_id: last 50 messages
- Edit Status WebSocket (
apps/api/routers/websocket.py):WS /ws/edit-status/:job_id→ polls Redis job status → pushes{status, progress, output_url}
- Wire Frontend (existing
BeautifulAIChat.tsx/ChatInput.tsx):- Add
apps/web/src/hooks/useChatStream.ts: EventSource wrapper for SSE - Add
apps/web/src/store/chat.ts: Zustand chat store (messages, isStreaming, pendingEdit) - Replace mock responses in
BeautifulAIChatwith real SSE stream - Show edit_plan cards with Confirm/Cancel in
ChatThread - Add progress bar message type during edit job
- Connect WebSocket for real-time edit progress
- Add
- Validate video ownership before any edit (user must own video)
- Sanitize all FFmpeg path arguments (no shell injection)
- Rate limit chat endpoint: 20 req/min per user (Redis sliding window)
apps/api/services/nlp_parser.pyapps/api/routers/chat.pyapps/api/routers/websocket.pyapps/worker/services/ffmpeg_executor.pyapps/web/src/store/chat.tsapps/web/src/hooks/useChatStream.tsapps/web/src/hooks/useEditStatus.ts
- Install Zustand in
apps/web: replace prop-drilling andwindow.dispatchEventpatterns - Create
apps/web/src/store/ui.ts(activeFeaturePanel, comparisonMode, beforeUrl, afterUrl) - Wire
VideoPreview.tsxto real R2 URLs from video store - Wire
Dashboard.tsxto real API data (stats, recent videos) - Wire
ExportShareModal.tsxto real export API + platform OAuth - Wire
EditHistoryPage.tsxtoGET /api/edits?video_id=... - Add skeleton loading states (already use shadcn/ui Skeleton)
- Add VITE_API_URL env var to Vite config, update all API calls
apps/web/src/store/ui.tsapps/web/vite.config.ts(proxy to backend in dev)apps/web/.env.example
- Create Stripe products: Free ($0), Pro ($29/mo or $19/mo yearly)
apps/api/routers/stripe.py:POST /api/stripe/create-checkout: creates Stripe Checkout sessionPOST /api/stripe/webhook: handlescheckout.session.completed,subscription.deleted,payment_failedGET /api/stripe/portal: opens Customer Portal
- Enforce plan limits in FastAPI dependency middleware
- Wire
UpgradePage.tsxto real Stripe checkout
apps/api/routers/stripe.pyapps/api/middleware/plan_check.py
- Autonomous Publishing Agent (
apps/api/services/autonomous_agent.py): multi-step job chain (analyze → fix → caption → export → YouTube API upload) - Creator DNA (
apps/api/services/creator_dna.py): extract style patterns after 3+ videos, inject into all future Claude prompts - Audience Simulation (
apps/api/services/simulation.py): Claude retention curve prediction, SVG chart in frontend - Platform Optimizer (
apps/worker/services/platform_optimizer.py): per-platform resize, pacing, captions - Real-time Collaboration (
apps/api/routers/collab.py): Supabase Realtime, invite system, presence indicators - Enterprise API (
apps/api/routers/enterprise.py): API key auth, rate limits per tier
apps/api/Dockerfile(python:3.12-slim + ffmpeg)apps/worker/Dockerfile(python:3.12-slim + ffmpeg + yt-dlp)- Deploy frontend to Vercel (or keep Vite → deploy to Cloudflare Pages)
- Deploy API + Worker to Railway
- GitHub Actions CI/CD: test → build → deploy
- Upload a real MP4 → verifying R2 storage + DB record created
- Analysis pipeline completes → virality score appears in UI with real data
- Type "remove silences" in chat → edit plan shown → confirm → FFmpeg job runs → new video appears with before/after comparison
- Auth flow: sign up → email verification → dashboard loads user's videos
- Stripe checkout: upgrade to Pro → plan updates in DB → video limit removed
pytest apps/api/tests/(FastAPI unit tests)- Rate limit test: 21 chat messages/min returns 429
- No Next.js needed for frontend (user keeps Vite). "Both" back-end choice = simple FastAPI routers + async workers
- New monorepo at
e:\vfxb\— existing VFXB Studio files move toapps/web - MVP = NLP edit engine (Phase 4) — Phases 0-3 are blockers and must run sequentially first
- FFmpeg runs only in worker container, never in the main API process
- All AI calls use streaming where possible (SSE for chat responses)