Files
vfxreview/TECHNICAL_ARCHITECTURE_REPORT_CURRENT_STATE.md
T
twotalesanimation cc89415a29 feat(pipeline): render queue, worker service and automated preview generation
One "Queue Export" click now renders the EXR sequence, then rebuilds the shot
headlessly with the studio slate/overlay template to produce the delivery MOV
and review MP4. Implements RenderPipeline2 phases 1-2 plus the preview stage.

Server:
- New models Export, RenderJob, ExportEvent, Machine, WorkerHeartbeat, plus
  Project.deliveryConfig and per-submission slate fields (Export.vfxScope,
  Export.submissionNote, inherited from the shot's previous export).
  Both migrations are purely additive; no existing column is touched.
- lib/render-pipeline: server-enforced state machine, transactional version
  increment with supersede, atomic FOR UPDATE SKIP LOCKED claim gated by
  machine availability windows, and a lease reaper run from instrumentation.ts.
- /api/ext/* endpoints for the panel and workers; session-auth mirrors under
  /api/render and /api/machines for the web UI.
- Pipeline pages: render queue, export detail, machine monitoring, plus an
  Exports tab on shot detail.

RenderWorker (.NET 8 Windows service, new):
- Registration, heartbeat as cancel channel, claim loop, aerender runner with
  progress parsing and stall watchdog, crash recovery and disk-spooled
  reporting that survives server downtime.
- Preview stage: headless AE assembles the preview comp into a throwaway AEP
  with both output modules queued, then a single aerender pass renders them.
  Preview jobs are not claimed while an interactive AE session is open, so an
  artist's project is never taken over.

AE panel: Queue Export with live status polling, urgent flag, retry, and the
VFX Scope / Submission Note fields. Every existing panel action is unchanged.

Preview chaining ships disabled behind SystemConfig preview.enabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 15:46:53 +02:00

22 KiB

Technical Architecture Report (Current State)

Date: 2026-07-31
Scope: Current implementation only (no redesign suggestions)


1. Database

Authoritative schema source: prisma/schema.prisma

Full Prisma Schema

The full schema is defined in prisma/schema.prisma.

  • Users/Auth
    • User
    • Account
    • Session
    • VerificationToken
    • ClientAccess
  • Projects/Episodes
    • Project
    • EpisodeDueDate
    • Client
  • Shots/Tasks/Versions/Reviews
    • Shot
    • ShotGroup
    • Task
    • Version
    • Comment
    • CommentReply
    • Annotation
    • Approval
    • ReviewSession
  • Files/Storage
    • FootagePlate
    • ShotReference
    • SystemConfig
  • Delivery/Export adjacent
    • Shot fields: highResKey, highResFilename, exrOutput, shotVersion
    • Version fields: fileUrl, fileName, proxyUrl, thumbnailUrl, posterUrl

Relationship summary

  • Client 1:N Project
  • Project 1:N Shot
  • Project 1:N Task
  • Project 1:N ReviewSession
  • Project 1:N EpisodeDueDate
  • Shot 1:N Task
  • Shot 1:N Version
  • Shot 1:N FootagePlate
  • Shot 1:N ShotReference
  • Task 1:N Version
  • Version 1:N Comment
  • Version 1:N Annotation
  • Version 1:N Approval
  • Comment 1:N CommentReply
  • User has many assigned/created entities across shots, tasks, versions, comments, approvals

Existing status enums

From prisma/schema.prisma:

  • ProjectStatus: ACTIVE, ON_HOLD, COMPLETED, ARCHIVED
  • ShotStatus: WAITING, IN_PROGRESS, INTERNAL_REVIEW, READY_FOR_CLIENT, CLIENT_REVIEW, REVISIONS, COMPLETE
  • ShotApprovalStatus: PENDING, INTERNALLY_APPROVED, CLIENT_APPROVED
  • TaskStatus: TODO, IN_PROGRESS, INTERNAL_REVIEW, CLIENT_REVIEW, CHANGES, DONE
  • ApprovalStatus: PENDING_REVIEW, APPROVED, REJECTED, NEEDS_CHANGES
  • ReviewStatus: PENDING, INTERNAL_APPROVED, CLIENT_APPROVED, NEEDS_CHANGES, FINAL_APPROVED

Notes:

  • There is no dedicated Delivery model.
  • There is no dedicated Export model.
  • Delivery/export state is represented by file pointers and shot/version metadata fields.

2. API

2.1 Shots

External shot APIs

  • GET /api/ext/projects

    • URL: /api/ext/projects
    • Method: GET
    • Purpose: List projects for pipeline tools
    • Request body: None
    • Response: projects[] with id, name, code, showId, projectType, status, dates, _count
    • Source: app/api/ext/projects/route.ts
  • GET /api/ext/projects/{projectCode}/episodes

    • URL: /api/ext/projects/{projectCode}/episodes
    • Method: GET
    • Purpose: List distinct episodes and optionally shot payloads per episode
    • Request body: None
    • Response: project + episodes[]; optional shots[] includes exrOutput/timecodes
    • Source: app/api/ext/projects/[projectCode]/episodes/route.ts
  • GET /api/ext/projects/{projectCode}/shots

  • GET /api/ext/shots

    • URL: /api/ext/shots
    • Method: GET
    • Purpose: Legacy listing by projectId
    • Request body: None
    • Response: shots[] + total
    • Source: app/api/ext/shots/route.ts
  • POST /api/ext/shots

    • URL: /api/ext/shots
    • Method: POST
    • Purpose: Create shot from external tool (JSON or multipart thumbnail)
    • Request body:
      • projectId, scene
      • optional episode, description, artistId, priority, fps, frameStart, frameEnd, dueDate, thumbnailUrl, shotGroupName, shotCode
      • optional thumbnail file (multipart)
    • Response: created shot object
    • Source: app/api/ext/shots/route.ts
  • GET /api/ext/shots/lookup

    • URL: /api/ext/shots/lookup
    • Method: GET
    • Purpose: Canonical shot lookup by shotCode (+ optional projectCode)
    • Request body: None
    • Response: shot object with project, artist, tasks, latest version, exrOutput, shotVersion, source/seq timecodes
    • Source: app/api/ext/shots/lookup/route.ts
  • GET /api/ext/shots/{shotId}

    • URL: /api/ext/shots/{shotId}
    • Method: GET
    • Purpose: Shot detail by DB id, or byCode mode
    • Request body: None
    • Response: full shot detail with tasks/latest version/counts
    • Source: app/api/ext/shots/[shotId]/route.ts
  • PATCH /api/ext/shots/{shotId}

    • URL: /api/ext/shots/{shotId}
    • Method: PATCH
    • Purpose: Update mutable shot field(s) from pipeline tools
    • Request body: shotVersion (v###) currently supported
    • Response: success + updated shot id/shotVersion
    • Source: app/api/ext/shots/[shotId]/route.ts

Internal shot APIs

  • GET /api/shots/{shotId}

    • URL: /api/shots/{shotId}
    • Method: GET
    • Purpose: Internal dashboard shot detail payload
    • Request body: None
    • Response: shot + tasks + artists + permissions flags
    • Source: app/api/shots/[shotId]/route.ts
  • GET /api/projects/{projectId}/episodes

2.2 Reviews

2.3 File uploads

  • POST /api/upload

    • Purpose: authenticated upload to Hetzner via app server
    • Body: multipart file (+ type)
    • Response: url, key
    • Source: app/api/upload/route.ts
  • POST /api/upload/local

  • POST /api/upload/presign

    • Purpose: direct browser->object-storage upload URL
    • Body: fileName, contentType, optional folder
    • Response: presignedUrl, key, url
    • Source: app/api/upload/presign/route.ts
  • GET/POST /api/uploadthing

  • POST /api/batch-upload/presign

  • POST /api/batch-upload/preview

    • Purpose: classify upload actions before upload
    • Body: projectId, fileNames[]
    • Response: items[] with statuses (new-version, rename-and-upload, create-task, update-highres, etc)
    • Source: app/api/batch-upload/preview/route.ts
  • POST /api/batch-upload/upload

    • Purpose: commit highres key or create version records
    • Body:
      • update-highres: action, shotId, projectId, key, fileName
      • version upload: action, shotId, projectId, file, task routing fields
    • Response: success + action result
    • Source: app/api/batch-upload/upload/route.ts

2.4 EXRs / Rendering / Delivery / Storage / Metadata

Notes:

  • No dedicated REST endpoint that runs ffmpeg in this repository.
  • No dedicated REST endpoint that performs EXR rendering on the server.
  • No dedicated deliveries API namespace.

3. Storage

Primary storage abstraction: lib/storage.ts

Provider modes

  • local
  • uploadthing
  • s3
  • r2
  • b2
  • minio

Dedicated high-res object storage path

  • Hetzner object storage helper methods are used for high-res and presigned direct uploads.
  • Config source precedence:
    • SystemConfig table keys
    • env fallback

Folder/key layout in object storage (current code paths)

  • videos/
  • image/
  • highres/
  • storage-test/

Local storage

  • LOCAL_UPLOAD_DIR (default ./uploads)
  • Served via /api/files catch-all route

File naming conventions

  • Key format: {folder}/{uuid}-{sanitized-file-name}
  • Sanitization done by sanitizeFileName to avoid URL/signature problems in object keys

EXR locations

Within web app/runtime:

  • EXR references are naming metadata (Shot.exrOutput) and file-serving pathing.

Within DCC tooling docs/scripts:

  • AE/Nuke workflows target shared export roots and per-shot folders.

Preview locations

  • Version media URLs stored in Version.fileUrl
  • Accessed through app player routes and /api/files routing where applicable

Thumbnail locations

  • Shot.thumbnailUrl
  • Version.thumbnailUrl
  • Can point to /api/files/{key} or external provider URL

Relevant sources:


4. Export Pipeline (Current)

AE panel

Documented in repo:

Implemented panel script also exists on the host AE installation (outside workspace), and behavior aligns with docs:

  • Episodes/shots discovery via ext APIs
  • Shot lookup API usage for metadata
  • Overlay/slate essential property updates (burn-in style overlays)
  • Queue EXR / Queue MP4 / Queue MOV render queue actions
  • Queue EXR (review convention)
  • Import exported EXR and import renders
  • Pull picture lock using seq timecodes
  • Increment shot version by PATCH call
  • Delivery prep that copies/renames EXR files to delivery folder convention

External API endpoints used by DCC tools

  • GET /api/ext/projects/{projectCode}/episodes
  • GET /api/ext/projects/{projectCode}/shots
  • GET /api/ext/shots/lookup
  • PATCH /api/ext/shots/{shotId}

Existing render scripts and pipelines

  • AE panel render queue automation (external script)
  • Nuke connector script creating write-node outputs and shot scripts:

ffmpeg scripts

  • None found in this repository.

Proxy generation

  • Version.proxyUrl field exists in schema.
  • No implemented proxy-generation worker/function found.

Thumbnail generation

  • Upload/assignment flows exist.
  • No server-side frame-extract thumbnail generator found.

Metadata extraction

Burn-in generation

  • Achieved via DCC overlay/slate layers and essential properties in AE/Nuke tool workflows.
  • No ffmpeg burn-in path found.

5. Review System

Current review workflow

Core status derivation:

Server action orchestration:

Internal review

  • Version upload:
    • marks previous versions non-latest
    • creates new latest version
    • moves Task to INTERNAL_REVIEW
    • recalculates Shot.status

Client review

  • Tokenized ReviewSession links
  • Optional password gate with signed cookie unlock
  • Client comment/annotation/approval endpoints
  • Share/unshare semantics via shot and version visibility fields

Task status flow

Observed statuses:

  • TODO
  • IN_PROGRESS
  • INTERNAL_REVIEW
  • CLIENT_REVIEW
  • CHANGES
  • DONE

Shot status flow

Derived in priority order:

  • REVISIONS (any task CHANGES)
  • COMPLETE (shotApprovalStatus CLIENT_APPROVED)
  • CLIENT_REVIEW / READY_FOR_CLIENT (internally approved + share flag)
  • IN_PROGRESS (task TODO/IN_PROGRESS)
  • INTERNAL_REVIEW (tasks exist)
  • WAITING (no tasks)

Approval process

  • Version-level approvals create Approval rows and update Version.approvalStatus
  • Shot-level client actions supported via client approve endpoint and shot actions

Reference doc in repo:


6. Authentication

External tools

  • /api/ext/* routes authenticate using API_SECRET_KEY via:
    • Authorization: Bearer
    • x-api-key header
  • tokenized route with ReviewSession lookup
  • optional password hash validation and signed unlock cookie

App users

  • NextAuth credentials provider
  • bcrypt password hash compare
  • JWT session strategy

Middleware behavior

  • Route classes allowed through middleware auth gate:
    • /api/ext/
    • /api/client/
    • /api/display/
    • /api/files/
    • /api/uploadthing
  • Per-route auth still enforced in handlers

Sources:


7. Existing Background Jobs

Cron jobs

  • None found.

Queues

  • None found.

Workers

  • None found.

Polling services

  • Display devices poll:
    • /api/display/events
    • /api/dashboard/stats

Docker containers

Scheduled tasks

  • Startup migration commands in container entrypoint:
  • AE panel Prep Delivery launches background PowerShell copy scripts on workstation (outside web app runtime)

8. After Effects Integration (Implemented Features)

Repo-level references:

Implemented capabilities observed/documented:

  • API calls
    • episodes/shots listing and shot lookup
    • shot version PATCH
  • Authentication
    • bearer API token in script
  • Comp discovery
    • shot code extraction from comp names and dropdown selection
  • Render queue integration
    • EXR Sequence
    • REVIEW_PREVIEW (MP4)
    • 4444 Tri (MOV)
    • EXR review sequence queue variant
  • Output path generation
    • shot/version naming conventions + export root paths
  • Shot lookup
    • uses /api/ext/shots/lookup for metadata and naming decisions
  • Upload features
    • no direct upload-to-web API flow in panel docs/script (render/output handled in DCC/filesystem)
  • Burn-in generation
    • overlay and slate essential properties
  • Review integration
    • preview comp build and version increment sync

9. Configuration

Environment variables

Primary reference: .env.example

Observed vars in code/docs include:

  • DATABASE_URL
  • NEXTAUTH_SECRET
  • NEXTAUTH_URL
  • NEXT_PUBLIC_APP_URL
  • NEXT_PUBLIC_APP_NAME
  • API_SECRET_KEY
  • AUTH_SECRET
  • STORAGE_PROVIDER
  • LOCAL_UPLOAD_DIR
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_REGION
  • AWS_BUCKET_NAME
  • R2_ACCESS_KEY_ID
  • R2_SECRET_ACCESS_KEY
  • R2_ACCOUNT_ID
  • R2_BUCKET_NAME
  • R2_PUBLIC_URL
  • B2_APPLICATION_KEY_ID
  • B2_APPLICATION_KEY
  • B2_BUCKET_NAME
  • B2_ENDPOINT
  • MINIO_ENDPOINT
  • MINIO_ACCESS_KEY
  • MINIO_SECRET_KEY
  • MINIO_BUCKET_NAME
  • HETZNER_ENDPOINT
  • HETZNER_ACCESS_KEY
  • HETZNER_SECRET_KEY
  • HETZNER_BUCKET_NAME
  • UPLOADTHING_SECRET
  • UPLOADTHING_APP_ID
  • EMAIL_FROM
  • EMAIL_SERVER_HOST
  • EMAIL_SERVER_PORT
  • EMAIL_SERVER_USER
  • EMAIL_SERVER_PASSWORD
  • SLACK_DEFAULT_WEBHOOK

Storage configuration

Render configuration

  • No server-side renderer configuration module found.
  • DCC render templates are documented in VFXReviewConnector.md.

ffmpeg configuration

  • None found in repository.

Object storage configuration

  • Implemented for AWS S3/R2/B2/MinIO + dedicated Hetzner helper path

10. Existing Utility Functions (Reusable)

Metadata extraction

File scanning

EXR sequence detection

MOV generation

  • No server-side MOV generation utility found.
  • MOV outputs are DCC render queue outputs in external scripts/docs.

Checksums

  • No checksum utility found.

Frame counting

Timecode extraction/conversion


11. High-Level Architecture Diagram

flowchart LR
  A[AE Panel / Nuke Connector\nExternal DCC tools] -->|API key auth| B[Next.js App Router APIs]
  B --> C[Prisma ORM]
  C --> D[(PostgreSQL)]

  B --> E[Storage Abstraction]
  E --> F[(Hetzner Object Storage)]
  E --> G[(S3/R2/B2/MinIO)]
  B --> H[(Local uploads dir)]

  I[Internal reviewers\nNextAuth session] --> B
  J[Client reviewers\nToken review sessions] --> B

  K[ESP32 display client] -->|x-display-key polling| B

  A --> L[Shared filesystem render roots\nEXR/MP4/MOV outputs]
  L -->|served/linked via app metadata| B

12. Files Inspected (Primary)


End of current-state report.