Spaces:
Paused
Paused
Julian Bilcke
commited on
Commit
Β·
5fb05e6
1
Parent(s):
652f343
debugging the api..
Browse files- .gitignore +4 -4
- src/database/constants.mts +3 -0
- src/index.mts +11 -11
- src/types.mts +9 -4
- src/utils/parseShotRequest.mts +85 -0
- src/{services/requestToTask.mts β utils/parseVideoRequest.mts} +17 -53
.gitignore
CHANGED
|
@@ -3,7 +3,7 @@ node_modules
|
|
| 3 |
*.bin
|
| 4 |
.DS_Store
|
| 5 |
.venv
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
| 3 |
*.bin
|
| 4 |
.DS_Store
|
| 5 |
.venv
|
| 6 |
+
./database/completed/*.json
|
| 7 |
+
./database/pending/*.json
|
| 8 |
+
*.mp4
|
| 9 |
+
sandbox
|
src/database/constants.mts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
| 1 |
|
| 2 |
export const pendingTasksDirFilePath = './database/pending/'
|
| 3 |
export const completedTasksDirFilePath = './database/completed/'
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
|
| 2 |
export const pendingTasksDirFilePath = './database/pending/'
|
| 3 |
export const completedTasksDirFilePath = './database/completed/'
|
| 4 |
+
|
| 5 |
+
export const shotFormatVersion = 1
|
| 6 |
+
export const sequenceFormatVersion = 1
|
src/index.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { createReadStream, promises as fs } from "fs"
|
|
| 3 |
import express from "express"
|
| 4 |
|
| 5 |
import { VideoTask, VideoSequenceRequest } from "./types.mts"
|
| 6 |
-
import {
|
| 7 |
import { savePendingTask } from "./database/savePendingTask.mts"
|
| 8 |
import { getTask } from "./database/getTask.mts"
|
| 9 |
import { main } from "./main.mts"
|
|
@@ -31,7 +31,7 @@ app.post("/", async (req, res) => {
|
|
| 31 |
|
| 32 |
console.log(`creating task from request..`)
|
| 33 |
try {
|
| 34 |
-
task = await
|
| 35 |
} catch (err) {
|
| 36 |
console.error(`failed to create task: ${task}`)
|
| 37 |
res.status(400)
|
|
@@ -79,18 +79,9 @@ app.get("/video/:id\.mp4", async (req, res) => {
|
|
| 79 |
}
|
| 80 |
|
| 81 |
let task: VideoTask = null
|
| 82 |
-
|
| 83 |
try {
|
| 84 |
task = await getTask(req.params.id)
|
| 85 |
console.log("returning result to user..")
|
| 86 |
-
|
| 87 |
-
const filePath = task.finalFilePath || task.tmpFilePath || ''
|
| 88 |
-
if (!filePath) {
|
| 89 |
-
res.status(400)
|
| 90 |
-
res.write(JSON.stringify({ error: "video exists, but cannot be previewed yet" }))
|
| 91 |
-
res.end()
|
| 92 |
-
return
|
| 93 |
-
}
|
| 94 |
} catch (err) {
|
| 95 |
res.status(404)
|
| 96 |
res.write(JSON.stringify({ error: "this video doesn't exist" }))
|
|
@@ -98,6 +89,15 @@ app.get("/video/:id\.mp4", async (req, res) => {
|
|
| 98 |
return
|
| 99 |
}
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
// file path exists, let's try to read it
|
| 102 |
try {
|
| 103 |
// do we need this?
|
|
|
|
| 3 |
import express from "express"
|
| 4 |
|
| 5 |
import { VideoTask, VideoSequenceRequest } from "./types.mts"
|
| 6 |
+
import { parseVideoRequest } from "./utils/parseVideoRequest.mts"
|
| 7 |
import { savePendingTask } from "./database/savePendingTask.mts"
|
| 8 |
import { getTask } from "./database/getTask.mts"
|
| 9 |
import { main } from "./main.mts"
|
|
|
|
| 31 |
|
| 32 |
console.log(`creating task from request..`)
|
| 33 |
try {
|
| 34 |
+
task = await parseVideoRequest(request)
|
| 35 |
} catch (err) {
|
| 36 |
console.error(`failed to create task: ${task}`)
|
| 37 |
res.status(400)
|
|
|
|
| 79 |
}
|
| 80 |
|
| 81 |
let task: VideoTask = null
|
|
|
|
| 82 |
try {
|
| 83 |
task = await getTask(req.params.id)
|
| 84 |
console.log("returning result to user..")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
} catch (err) {
|
| 86 |
res.status(404)
|
| 87 |
res.write(JSON.stringify({ error: "this video doesn't exist" }))
|
|
|
|
| 89 |
return
|
| 90 |
}
|
| 91 |
|
| 92 |
+
|
| 93 |
+
const filePath = task.finalFilePath || task.tmpFilePath || ''
|
| 94 |
+
if (!filePath) {
|
| 95 |
+
res.status(400)
|
| 96 |
+
res.write(JSON.stringify({ error: "video exists, but cannot be previewed yet" }))
|
| 97 |
+
res.end()
|
| 98 |
+
return
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
// file path exists, let's try to read it
|
| 102 |
try {
|
| 103 |
// do we need this?
|
src/types.mts
CHANGED
|
@@ -124,8 +124,6 @@ export interface VideoShotMeta {
|
|
| 124 |
actorDialoguePrompt: string
|
| 125 |
|
| 126 |
seed: number
|
| 127 |
-
upscale: boolean
|
| 128 |
-
|
| 129 |
noise: boolean // add movie noise
|
| 130 |
|
| 131 |
durationMs: number // in milliseconds
|
|
@@ -133,7 +131,7 @@ export interface VideoShotMeta {
|
|
| 133 |
|
| 134 |
fps: number // 8, 12, 24, 30, 60
|
| 135 |
|
| 136 |
-
resolution:
|
| 137 |
|
| 138 |
introTransition: VideoTransition
|
| 139 |
introDurationMs: number // in milliseconds
|
|
@@ -151,6 +149,10 @@ export interface VideoShotMeta {
|
|
| 151 |
|
| 152 |
|
| 153 |
export interface VideoShotData {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
hasGeneratedVideo: boolean
|
| 155 |
hasUpscaledVideo: boolean
|
| 156 |
hasGeneratedBackgroundAudio: boolean
|
|
@@ -194,7 +196,6 @@ export interface VideoSequenceMeta {
|
|
| 194 |
actorDialoguePrompt: string
|
| 195 |
|
| 196 |
seed: number
|
| 197 |
-
upscale: boolean
|
| 198 |
|
| 199 |
noise: boolean // add movie noise
|
| 200 |
|
|
@@ -210,6 +211,10 @@ export interface VideoSequenceMeta {
|
|
| 210 |
|
| 211 |
|
| 212 |
export interface VideoSequenceData {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
nbCompletedShots: number
|
| 214 |
nbTotalShots: number
|
| 215 |
progressPercent: number
|
|
|
|
| 124 |
actorDialoguePrompt: string
|
| 125 |
|
| 126 |
seed: number
|
|
|
|
|
|
|
| 127 |
noise: boolean // add movie noise
|
| 128 |
|
| 129 |
durationMs: number // in milliseconds
|
|
|
|
| 131 |
|
| 132 |
fps: number // 8, 12, 24, 30, 60
|
| 133 |
|
| 134 |
+
resolution: string // {width}x{height} (256, 512, 576, 720, 1080)
|
| 135 |
|
| 136 |
introTransition: VideoTransition
|
| 137 |
introDurationMs: number // in milliseconds
|
|
|
|
| 149 |
|
| 150 |
|
| 151 |
export interface VideoShotData {
|
| 152 |
+
|
| 153 |
+
// used to check compatibility
|
| 154 |
+
version: number
|
| 155 |
+
|
| 156 |
hasGeneratedVideo: boolean
|
| 157 |
hasUpscaledVideo: boolean
|
| 158 |
hasGeneratedBackgroundAudio: boolean
|
|
|
|
| 196 |
actorDialoguePrompt: string
|
| 197 |
|
| 198 |
seed: number
|
|
|
|
| 199 |
|
| 200 |
noise: boolean // add movie noise
|
| 201 |
|
|
|
|
| 211 |
|
| 212 |
|
| 213 |
export interface VideoSequenceData {
|
| 214 |
+
|
| 215 |
+
// used to check compatibility
|
| 216 |
+
version: number
|
| 217 |
+
|
| 218 |
nbCompletedShots: number
|
| 219 |
nbTotalShots: number
|
| 220 |
progressPercent: number
|
src/utils/parseShotRequest.mts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { v4 as uuidv4 } from "uuid"
|
| 2 |
+
|
| 3 |
+
// convert a request (which might be invalid)
|
| 4 |
+
|
| 5 |
+
import { VideoSequence, VideoShot, VideoShotMeta } from "../types.mts"
|
| 6 |
+
import { generateSeed } from "../services/generateSeed.mts"
|
| 7 |
+
import { getValidNumber } from "./getValidNumber.mts"
|
| 8 |
+
import { shotFormatVersion } from "../database/constants.mts"
|
| 9 |
+
|
| 10 |
+
export const parseShotRequest = async (sequence: VideoSequence, maybeShotMeta: VideoShotMeta): Promise<VideoShot> => {
|
| 11 |
+
|
| 12 |
+
const shot: VideoShot = {
|
| 13 |
+
id: maybeShotMeta.id || uuidv4(),
|
| 14 |
+
|
| 15 |
+
shotPrompt: `${maybeShotMeta.shotPrompt || ""}`,
|
| 16 |
+
|
| 17 |
+
// describe the background audio (crowd, birds, wind, sea etc..)
|
| 18 |
+
backgroundAudioPrompt: `${maybeShotMeta.backgroundAudioPrompt || ""}`,
|
| 19 |
+
|
| 20 |
+
// describe the foreground audio (cars revving, footsteps, objects breaking, explosion etc)
|
| 21 |
+
foregroundAudioPrompt: `${maybeShotMeta.foregroundAudioPrompt || ""}`,
|
| 22 |
+
|
| 23 |
+
// describe the main actor visible in the shot (optional)
|
| 24 |
+
actorPrompt: `${maybeShotMeta.actorPrompt || ""}`,
|
| 25 |
+
|
| 26 |
+
// describe the main actor voice (man, woman, old, young, amused, annoyed.. etc)
|
| 27 |
+
actorVoicePrompt: `${maybeShotMeta.actorVoicePrompt || ""}`,
|
| 28 |
+
|
| 29 |
+
// describe the main actor dialogue line
|
| 30 |
+
actorDialoguePrompt: `${maybeShotMeta.actorDialoguePrompt || ""}`,
|
| 31 |
+
|
| 32 |
+
// a video sequence SHOULD NOT HAVE a consistent seed, to avoid weird geometry similarities
|
| 33 |
+
seed: getValidNumber(maybeShotMeta.seed, 0, 4294967295, generateSeed()),
|
| 34 |
+
|
| 35 |
+
// a video sequence SHOULD HAVE a consistent grain
|
| 36 |
+
noise: sequence.noise,
|
| 37 |
+
|
| 38 |
+
// a video sequence CAN HAVE inconsistent scene duration, like in any real movie
|
| 39 |
+
durationMs: getValidNumber(maybeShotMeta.durationMs, 0, 6000, 3000),
|
| 40 |
+
|
| 41 |
+
// a video sequence CAN HAVE inconsistent iteration steps
|
| 42 |
+
steps: getValidNumber(maybeShotMeta.steps || sequence.steps, 1, 60, 35),
|
| 43 |
+
|
| 44 |
+
// a video sequence MUST HAVE consistent frames per second
|
| 45 |
+
fps: getValidNumber(sequence.fps, 8, 60, 24),
|
| 46 |
+
|
| 47 |
+
// a video sequence MUST HAVE a consistent resolution
|
| 48 |
+
resolution: sequence.resolution,
|
| 49 |
+
|
| 50 |
+
// a video sequence CAN HAVE intro transitions for each shot
|
| 51 |
+
introTransition: 'fade',
|
| 52 |
+
introDurationMs: 500,
|
| 53 |
+
|
| 54 |
+
// for internal use
|
| 55 |
+
|
| 56 |
+
version: shotFormatVersion,
|
| 57 |
+
hasGeneratedVideo: false,
|
| 58 |
+
hasUpscaledVideo: false,
|
| 59 |
+
hasGeneratedBackgroundAudio: false,
|
| 60 |
+
hasGeneratedForegroundAudio: false,
|
| 61 |
+
hasGeneratedActor: false,
|
| 62 |
+
hasInterpolatedVideo: false,
|
| 63 |
+
hasAddedAudio: false,
|
| 64 |
+
hasPostProcessedVideo: false,
|
| 65 |
+
nbCompletedSteps: 0,
|
| 66 |
+
|
| 67 |
+
// 0. in queue
|
| 68 |
+
// 1. generate with Zeroscope
|
| 69 |
+
// 2. upscale with Zeroscope XL
|
| 70 |
+
// 3. interpolate with FILE
|
| 71 |
+
// 4. generate audio background
|
| 72 |
+
// 5. generate audio foreground
|
| 73 |
+
// 6. add audio to video
|
| 74 |
+
// 7. post-processing
|
| 75 |
+
nbTotalSteps: 7,
|
| 76 |
+
progressPercent: 0,
|
| 77 |
+
completedAt: '',
|
| 78 |
+
completed: false,
|
| 79 |
+
error: '',
|
| 80 |
+
tmpFilePath: '',
|
| 81 |
+
finalFilePath: '',
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
return shot
|
| 85 |
+
}
|
src/{services/requestToTask.mts β utils/parseVideoRequest.mts}
RENAMED
|
@@ -3,12 +3,14 @@ import { v4 as uuidv4 } from "uuid"
|
|
| 3 |
// convert a request (which might be invalid)
|
| 4 |
|
| 5 |
import { VideoSequenceRequest, VideoTask } from "../types.mts"
|
| 6 |
-
import { generateSeed } from "
|
| 7 |
-
import { getValidNumber } from "
|
| 8 |
-
import { getValidResolution } from "
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
export const
|
| 12 |
|
| 13 |
const task: VideoTask = {
|
| 14 |
// ------------ VideoSequenceMeta -------------
|
|
@@ -34,8 +36,6 @@ export const requestToTask = async (request: VideoSequenceRequest): Promise<Vide
|
|
| 34 |
|
| 35 |
seed: getValidNumber(request.sequence.seed, 0, 4294967295, generateSeed()),
|
| 36 |
|
| 37 |
-
upscale: request.sequence.upscale === true,
|
| 38 |
-
|
| 39 |
noise: request.sequence.noise === true,
|
| 40 |
|
| 41 |
steps: getValidNumber(request.sequence.steps, 1, 60, 35),
|
|
@@ -48,6 +48,7 @@ export const requestToTask = async (request: VideoSequenceRequest): Promise<Vide
|
|
| 48 |
outroDurationMs: 3000,
|
| 49 |
|
| 50 |
// ---------- VideoSequenceData ---------
|
|
|
|
| 51 |
nbCompletedShots: 0,
|
| 52 |
nbTotalShots: 0,
|
| 53 |
progressPercent: 0,
|
|
@@ -62,54 +63,17 @@ export const requestToTask = async (request: VideoSequenceRequest): Promise<Vide
|
|
| 62 |
shots: [],
|
| 63 |
}
|
| 64 |
|
|
|
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
const defaultSeed = generateSeed()
|
| 74 |
-
const seedStr = getValidNumber(Number(`${query.seed || defaultSeed}`)
|
| 75 |
-
const maybeSeed = Number(seedStr)
|
| 76 |
-
const seed = isNaN(maybeSeed) || ! isFinite(maybeSeed) ? defaultSeed : maybeSeed
|
| 77 |
-
|
| 78 |
-
// in production we want those ON by default
|
| 79 |
-
const upscale = `${query.upscale || "true"}` === "true"
|
| 80 |
-
const interpolate = `${query.upscale || "true"}` === "true"
|
| 81 |
-
const noise = `${query.noise || "true"}` === "true"
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
const defaultDuration = 3
|
| 85 |
-
const maxDuration = 5
|
| 86 |
-
const durationStr = Number(`${query.duration || defaultDuration}`)
|
| 87 |
-
const maybeDuration = Number(durationStr)
|
| 88 |
-
const duration = Math.min(maxDuration, Math.max(1, isNaN(maybeDuration) || !isFinite(maybeDuration) ? defaultDuration : maybeDuration))
|
| 89 |
-
|
| 90 |
-
const defaultSteps = 35
|
| 91 |
-
const stepsStr = Number(`${query.steps || defaultSteps}`)
|
| 92 |
-
const maybeSteps = Number(stepsStr)
|
| 93 |
-
const nbSteps = Math.min(60, Math.max(1, isNaN(maybeSteps) || !isFinite(maybeSteps) ? defaultSteps : maybeSteps))
|
| 94 |
-
|
| 95 |
-
// const frames per second
|
| 96 |
-
const defaultFps = 24
|
| 97 |
-
const fpsStr = Number(`${query.fps || defaultFps}`)
|
| 98 |
-
const maybeFps = Number(fpsStr)
|
| 99 |
-
const nbFrames = Math.min(60, Math.max(8, isNaN(maybeFps) || !isFinite(maybeFps) ? defaultFps : maybeFps))
|
| 100 |
-
|
| 101 |
-
const defaultResolution = 576
|
| 102 |
-
const resolutionStr = Number(`${query.resolution || defaultResolution}`)
|
| 103 |
-
const maybeResolution = Number(resolutionStr)
|
| 104 |
-
const resolution = Math.min(1080, Math.max(256, isNaN(maybeResolution) || !isFinite(maybeResolution) ? defaultResolution : maybeResolution))
|
| 105 |
-
|
| 106 |
-
const actorPrompt = `${query.actorPrompt || ""}`
|
| 107 |
-
|
| 108 |
-
const actorVoicePrompt = `${query.actorVoicePrompt || ""}`
|
| 109 |
-
|
| 110 |
-
const actorDialoguePrompt = `${query.actorDialoguePrompt || ""}`
|
| 111 |
-
|
| 112 |
|
|
|
|
| 113 |
|
| 114 |
return task
|
| 115 |
}
|
|
|
|
| 3 |
// convert a request (which might be invalid)
|
| 4 |
|
| 5 |
import { VideoSequenceRequest, VideoTask } from "../types.mts"
|
| 6 |
+
import { generateSeed } from "../services/generateSeed.mts"
|
| 7 |
+
import { getValidNumber } from "./getValidNumber.mts"
|
| 8 |
+
import { getValidResolution } from "./getValidResolution.mts"
|
| 9 |
+
import { parseShotRequest } from "./parseShotRequest.mts"
|
| 10 |
+
import { sequenceFormatVersion } from "../database/constants.mts"
|
| 11 |
|
| 12 |
+
|
| 13 |
+
export const parseVideoRequest = async (request: VideoSequenceRequest): Promise<VideoTask> => {
|
| 14 |
|
| 15 |
const task: VideoTask = {
|
| 16 |
// ------------ VideoSequenceMeta -------------
|
|
|
|
| 36 |
|
| 37 |
seed: getValidNumber(request.sequence.seed, 0, 4294967295, generateSeed()),
|
| 38 |
|
|
|
|
|
|
|
| 39 |
noise: request.sequence.noise === true,
|
| 40 |
|
| 41 |
steps: getValidNumber(request.sequence.steps, 1, 60, 35),
|
|
|
|
| 48 |
outroDurationMs: 3000,
|
| 49 |
|
| 50 |
// ---------- VideoSequenceData ---------
|
| 51 |
+
version: sequenceFormatVersion,
|
| 52 |
nbCompletedShots: 0,
|
| 53 |
nbTotalShots: 0,
|
| 54 |
progressPercent: 0,
|
|
|
|
| 63 |
shots: [],
|
| 64 |
}
|
| 65 |
|
| 66 |
+
const maybeShots = Array.isArray(request.shots) ? request.shots : []
|
| 67 |
|
| 68 |
+
for (const maybeShot of maybeShots) {
|
| 69 |
+
try {
|
| 70 |
+
const shot = await parseShotRequest(task, maybeShot)
|
| 71 |
+
task.shots.push(shot)
|
| 72 |
+
} catch (err) {
|
| 73 |
+
console.log(`error parsing shot: `, maybeShot)
|
| 74 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
+
}
|
| 77 |
|
| 78 |
return task
|
| 79 |
}
|