53fc8dc65b1cf4954e893b1f82a7a14d328e99c4
Deploy / deploy (push) Successful in 6m1s
A shot is routinely re-exported for delivery after it has already been client-approved — most often an unscaled/un-punched-in pass. Every pipeline-created Version defaulted to approvalStatus PENDING_REVIEW, which is exactly what the dashboard counts, the shot page badge and the playlist read as "awaiting review" — so a re-export made an approved shot look like new unreviewed work to the whole team, with nothing actually different about its approval. approvalStatus is now inherited from the task's previous latest version rather than left at the create default. A task with no prior version (its first export) still gets PENDING_REVIEW, which is correct. Separately, every pipeline version was stamped with notes: "Pipeline render vXXX (internal only)" — and notes is the artist's message to reviewers, rendered verbatim in the client portal and the review player. Nothing enforces that a pipeline version stays unshared once created (see the gap noted in RenderPipeline2.md), so that internal-sounding string could reach a client if the shot were re-shared after a re-export. notes is left unset; what a version is and where it came from belongs on the Export and its timeline, which is where a human would actually go looking. RenderPipeline2.md §9 documents both, plus the share-flow gap that let notes reach a client in the first place: shareWithClient promotes whichever Version is isLatest with no awareness that a pipeline version is meant to stay internal permanently. Not fixed here — closing it needs a way to identify a pipeline-origin version at the share call sites, and Export.versionId is currently a one-way scalar with no inverse relation on Version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FeedBack — VFX Review & Approval Platform
Frame-accurate video review, annotation drawing, and approval tracking for boutique VFX and animation studios.
Features
- Dead-simple video upload — drag & drop MP4/MOV, up to 2 GB, version-tracked
- Frame-accurate scrubbing — canvas-based timeline ruler with click-and-drag seek
- Timestamp comments — comments anchored to exact frame numbers, threaded replies, resolve/unresolve
- Annotation drawing — freehand, arrow, rectangle, circle tools with color + stroke width picker
- Approval workflow — Approve / Reject / Needs Changes with notes, full history
- Client review links — shareable token-based review URLs for external clients (no login required)
- Role-based access — Admin / Producer / Supervisor / Artist / Client roles
- Slack notifications — webhook alerts for uploads and approvals
- Self-hostable — Docker Compose with PostgreSQL + MinIO (S3-compatible) included
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 15 App Router (Turbopack) |
| Language | TypeScript 5 strict |
| Styling | TailwindCSS 3 + shadcn/ui (zinc, dark-only) |
| Auth | NextAuth.js v5 (Credentials + JWT) |
| Database | PostgreSQL 16 via Prisma 6 |
| File Storage | UploadThing / S3 / R2 / B2 / MinIO / local |
| State | Zustand 5 (player) + TanStack Query 5 (server) |
| Annotations | HTML5 Canvas API (normalized coordinates) |
| Video Playback | HTML5 <video> with custom controls |
| Container | Docker multi-stage build |
Quick Start (Docker)
# 1. Clone and configure
cp .env.example .env
# Edit .env — set AUTH_SECRET, DATABASE_URL, storage provider
# 2. Start services (PostgreSQL + MinIO + App)
docker compose up -d
# 3. Run migrations and seed
docker compose exec app npx prisma migrate deploy
docker compose exec app npx tsx prisma/seed.ts
# 4. Open http://localhost:3000
Demo accounts (after seed):
| Password | Role | |
|---|---|---|
| admin@vfxreview.local | admin123 | Admin |
| producer@vfxreview.local | producer123 | Producer |
| supervisor@vfxreview.local | supervisor123 | Supervisor |
| artist@vfxreview.local | artist123 | Artist |
| client@studio.com | client123 | Client |
Local Development
# Install dependencies
npm install
# Set up database (Postgres must be running)
npx prisma migrate dev
npx tsx prisma/seed.ts
# Start dev server
npm run dev
Open http://localhost:3000.
Environment Variables
See .env.example for full reference. Key variables:
# Required
DATABASE_URL="postgresql://..."
AUTH_SECRET="your-32-char-secret"
NEXTAUTH_URL="http://localhost:3000"
# Storage (choose one)
STORAGE_PROVIDER="local" # local | uploadthing | s3 | r2 | b2 | minio
# UploadThing (if STORAGE_PROVIDER=uploadthing)
UPLOADTHING_SECRET="sk_live_..."
UPLOADTHING_APP_ID="..."
# AWS S3 / R2 / B2 compatible
S3_ACCESS_KEY_ID=""
S3_SECRET_ACCESS_KEY=""
S3_BUCKET_NAME=""
S3_REGION=""
S3_ENDPOINT="" # for R2/B2/MinIO
# Slack (optional)
SLACK_BOT_TOKEN=""
# Email (optional, nodemailer)
SMTP_HOST=""
SMTP_PORT="587"
SMTP_USER=""
SMTP_PASS=""
Project Structure
app/
(auth)/login/ — Sign-in page
(dashboard)/ — Protected dashboard layout
dashboard/ — Home stats + shot queue
projects/ — Project list + detail pages
settings/ — User profile
review/[versionId]/ — Full-screen review player
client/[token]/ — Unauthenticated client review link
api/
auth/[...nextauth]/ — NextAuth route handler
uploadthing/ — UploadThing route handler
notifications/ — Notification CRUD
projects/ — Projects REST endpoint
components/
player/ — ReviewPlayer, FrameTimeline, PlaybackControls
annotations/ — AnnotationCanvas, AnnotationTools
comments/ — CommentPanel (threads + replies)
versions/ — VersionUpload, VersionList
shots/ — ShotCard, NewShotDialog
projects/ — ProjectCard, NewProjectDialog
dashboard/ — StatsCards, ShotQueue, RecentActivity
layout/ — Sidebar, Header, NotificationBell, Providers
ui/ — shadcn/ui base components
actions/ — Server Actions (projects, shots, versions, comments, annotations, approvals)
lib/ — db, auth, utils, frame-utils, storage, slack, notifications, uploadthing
hooks/ — use-review-player (Zustand), use-annotations, use-frame-comments
prisma/ — schema.prisma, seed.ts
Review Player Keyboard Shortcuts
| Key | Action |
|---|---|
Space / K |
Play / Pause |
J |
Reverse playback |
L |
Forward playback |
← / → |
Step one frame |
F |
Toggle fullscreen |
Documentation
| Doc | Covers |
|---|---|
| RenderPipeline2.md | Render pipeline specification — normative design, state machine, API contracts |
| DELIVERY_WORKFLOW.md | Building and shipping delivery packages (how-to) |
| VFXReviewConnector.md | After Effects panel — setup, Shot Builder, QC, diagnostics |
| RenderWorker/README.md | Render worker service — packaging, install, validation, delivery builds |
| BlenderPanel/README.md | Blender add-on — queueing a .blend on the farm |
| EXT_API_REFERENCE.md | External API (/api/ext/*) used by the panel and workers |
| Shot task workflow.md | Shot and task lifecycle in the web app |
License
MIT
Description
Languages
TypeScript
78.7%
C#
10.5%
JavaScript
7.5%
Python
2.2%
PowerShell
0.5%
Other
0.5%