Skip to content

JaduCutV2 Module

Backend for the JaduCutV2 timeline editor. Persists the slim JaduCutV2StoryVideo overlay (timeline elements + settings + exports) per story, handles media asset upload/list/delete, and orchestrates export jobs dispatched to the Python backend.

Story content (scenes, shots, dialogues, timing) remains the single source of truth in storyvideos and is not duplicated here.

Design contract

  • storyvideos = SOT for story content (scenes, shots, dialogues, timing, media).
  • jaduCutV2StoryVideos = editor-only overlay: timeline elements, settings, export refs.
  • projectId on the overlay is a one-time mirror of storyvideos.projectId — set at create time and structurally immutable. PUT /jaduCutV2/storyVideo/:storyId does not accept projectId in the body.

Folder layout

src/jaduCutV2/
├── jaduCutV2.router.ts       Express router — mounted at /jaduCutV2 in src/router.ts
├── jaduCutV2.controller.ts   Request handlers + error shaping
├── jaduCutV2.service.ts      Business logic + MIME/size policy + Python dispatch
└── jaduCutV2.validator.ts    Zod schemas for every request

Related files outside this folder:

src/models/jaduCutV2StoryVideo.model.ts       Overlay Mongo collection
src/models/jaduCutV2UploadedAssets.model.ts   Uploaded-asset metadata
src/models/jaduCutV2ExportJobs.model.ts       Export job state
src/shared/jaduCutV2Types.ts                  Wire contract + overlay shapes + enums

API endpoints

All endpoints are mounted under /jaduCutV2. Auth middleware on the mount: JaduAuth.authorizeRequest([], JADU_AUTH_STUDIO_APP_ID) followed by AuthMiddleware.jaduAuthToReqAuth.

Overlay (JaduCutV2StoryVideo)

Method Path Purpose Notes
POST /storyVideo Create overlay for a story 409 if one already exists for storyId. projectId set once, then immutable.
PUT /storyVideo/:storyId Update timelineElements + settings 404 if missing. projectId / exports NOT accepted in body.
GET /storyVideo?storyId=xxx Fetch overlay by storyId Miss → { isExistingJaduCutV2StoryVideo: false }. Hit → { isExistingJaduCutV2StoryVideo: true, ...doc }.
GET /storyVideoList Paginated list Query: page, pageSize, search (case-insensitive regex on storyId).
DELETE /storyVideo/:storyId Soft-delete overlay Sets isDeleted: true; 404 if missing.

Assets (legacy-compatible)

Method Path Purpose Notes
POST /uploadFile Upload a media asset to B2 multer memory storage, 50 MB multer cap; per-type caps enforced in service.
GET /listAssets Paginated asset list for caller Optional assetType filter (IMAGE / AUDIO / VIDEO).
POST /deleteAsset Delete asset by id for caller Scoped to caller's userId.

Export

Method Path Purpose Notes
POST /storyVideo/export Queue an export job Uniqueness enforced per-story (not per-project). After the response is sent, the service fires a fire-and-forget POST to the Python backend.
GET /storyVideo/export/:exportId Fetch job status Scoped to caller's userId.

Wire contract

All request/response and overlay shapes are defined in src/shared/jaduCutV2Types.ts. The FE mirrors this file under studio-frontend/jaduCutV2/types/. The BE file is intentionally self-contained — nested shapes like timeRange: { start: number; end: number } are inlined rather than imported, so BE ↔ FE wire parity is easy to audit without chasing imports.

Endpoint Request type Response payload
POST /storyVideo CreateJaduCutV2StoryVideoRequest { jaduCutV2StoryVideo: JaduCutV2StoryVideoData }
PUT /storyVideo/:storyId UpdateJaduCutV2StoryVideoRequest { jaduCutV2StoryVideo: JaduCutV2StoryVideoData }
GET /storyVideo query storyId GetJaduCutV2StoryVideoResponse
GET /storyVideoList query page, pageSize, search JaduCutV2StoryVideoListResponse

Data model

Collection Model Purpose
jaduCutV2StoryVideos jaduCutV2StoryVideo.model.ts Overlay doc keyed by storyId. Fields: projectId, timelineElements, settings, exports, version, createdAt, updatedAt, isDeleted.
jaduCutV2UploadedAssets jaduCutV2UploadedAssets.model.ts User-uploaded media metadata: userId, assetType, assetUrlPath.
jaduCutV2ExportJobs jaduCutV2ExportJobs.model.ts Export job state: exportId, projectId, storyId, userId, status.

Overlay document shape

interface JaduCutV2StoryVideoData {
  projectId: string;       // Mirror of storyvideos.projectId. Set once at create; immutable.
  storyId: string;
  version: string;
  timelineElements: JaduCutV2TimelineElements;  // effects + transitions + textClips
  settings: JaduCutV2Settings;                  // timelineScale + dimensions + fps
  exports: JaduCutV2ExportRef[];                // server-managed; not overwritten by PUT
  createdAt?: Date;
  updatedAt?: Date;
  isDeleted?: boolean;
}

JaduCutV2TimelineElements, JaduCutV2Settings, and JaduCutV2ExportRef are defined in src/shared/jaduCutV2Types.ts.

Custom audio tracks (customTracks[])

Partial mutations under /storyVideo/:storyId/customTracks/.... The bulk PUT /storyVideo/:storyId does not accept customTracks.

Concern Implementation
Same-lane overlap customTracksOverlap.utils.ts — rejected with 400 on add-to-lane, placement update, and move (FE also blocks before API).
Phase 1 FE add flow Global +POST .../tracks/clips (atomic new lane + clip). POST .../tracks/:trackId/clips (B) exists but is not called from the UI.
Lane reorder PATCH .../reorder — no UI in phase 1.
Tests tests/jaduCutV2/customTracks.test.ts, customTracksOverlap.utils.test.ts

Concurrency and uniqueness

  • A partial unique index on jaduCutV2StoryVideos.storyId (where isDeleted != true) is expected on the collection. The model performs a pre-check to return a friendly 409; if two concurrent creates slip past the pre-check, the second insertOne fails with MongoDB E11000, which the model maps to the same 409 so callers see uniform behavior. Index creation is a DBA / migration concern outside this module.
  • POST /storyVideo/export rejects with InvalidOperationError (400) when an active job already exists for the story. Uniqueness is per-story, not per-project, because a project can contain many stories.

Asset upload policy

Defined in jaduCutV2.service.ts — the code is the source of truth; the table below is a reference snapshot.

Asset type Size limit Accepted MIME types
IMAGE 20 MB image/jpeg, image/jpg, image/png, image/gif, image/webp, image/svg+xml
AUDIO 50 MB audio/mpeg, audio/mp3, audio/wav, audio/ogg, audio/aac, audio/flac
VIDEO 500 MB video/mp4, video/avi, video/mov, video/wmv, video/flv, video/webm

Multer enforces a 50 MB hard cap at the framework layer; the service enforces the per-type cap after MIME classification.

Interop

  • Workbench — story-ops (reorder/resize/delete shot, dialogue changes) run on storyvideos via the workbench module. This module never mutates the overlay in response to story changes; the FE reconstructs timeline state via its own diff dispatcher when the story updates.
  • Python backend — export dispatch is a fire-and-forget POST ${PYTHON_BACKEND_BASE_URL}/jaduCutV2/dispatchExportJob with X-API-KEY: process.env.PYTHON_BACKEND_API_KEY, called after the HTTP response has already been sent.

Frontend counterpart

See jaduCutV2/README.md in studio-frontend for the diff-based rebuild/patch dispatcher, clip identity system, and store layout that consume these APIs.