diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000000000000000000000000000000000000..56611752ac822de651a144907c93a58542e3e29c
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,6 @@
+
+
+to allow multiple videos to be processed a the same time:
+
+[ ] yield from the loop at each step
+[ ] random processing of videos
diff --git a/package-lock.json b/package-lock.json
index 371a39370edea3f54ad06230fedc079c11fab7a2..6fb19e7250b36ac6c008a036dfa76987162e0902 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,15 +14,19 @@
"@types/express": "^4.17.17",
"@types/ffmpeg-concat": "^1.1.2",
"@types/uuid": "^9.0.2",
+ "eventsource-parser": "^1.0.0",
"express": "^4.18.2",
"ffmpeg-concat": "^1.3.0",
"fluent-ffmpeg": "^2.1.2",
"fs-extra": "^11.1.1",
+ "gpt-tokens": "^1.1.1",
"node-fetch": "^3.3.1",
+ "openai": "^3.3.0",
"puppeteer": "^20.8.0",
"temp-dir": "^3.0.0",
"ts-node": "^10.9.1",
- "uuid": "^9.0.0"
+ "uuid": "^9.0.0",
+ "yaml": "^2.3.1"
}
},
"node_modules/@babel/code-frame": {
@@ -686,6 +690,14 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
"integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg=="
},
+ "node_modules/axios": {
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
+ "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+ "dependencies": {
+ "follow-redirects": "^1.14.8"
+ }
+ },
"node_modules/b4a": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
@@ -1222,6 +1234,11 @@
"ms": "2.0.0"
}
},
+ "node_modules/decimal.js": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
+ },
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -1479,6 +1496,14 @@
"node": ">= 0.6"
}
},
+ "node_modules/eventsource-parser": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.0.0.tgz",
+ "integrity": "sha512-9jgfSCa3dmEme2ES3mPByGXfgZ87VbP97tng1G2nWwWx6bV2nYxm2AWCrbQjXToSe+yYlqaZNtxffR9IeQr95g==",
+ "engines": {
+ "node": ">=14.18"
+ }
+ },
"node_modules/execa": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
@@ -1772,6 +1797,25 @@
"node": ">=0.8.0"
}
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/foreground-child": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -2218,6 +2262,16 @@
"through2": "^0.6.3"
}
},
+ "node_modules/gpt-tokens": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/gpt-tokens/-/gpt-tokens-1.1.1.tgz",
+ "integrity": "sha512-fB1u0ZH7PywF9FByfWCqn6Hpp3so/pFUmk3AiV4QlOskr57LK8Ds3YJOjdemWKRGJQ+2pT9ikt++Eb+/et9gTQ==",
+ "dependencies": {
+ "decimal.js": "^10.4.3",
+ "js-tiktoken": "^1.0.7",
+ "openai": "^3.3.0"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2609,6 +2663,14 @@
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="
},
+ "node_modules/js-tiktoken": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.7.tgz",
+ "integrity": "sha512-biba8u/clw7iesNEWLOLwrNGoBP2lA+hTaBLs/D45pJdUPFXyxD6nhcDVtADChghv4GgyAiMKYMiRx7x6h7Biw==",
+ "dependencies": {
+ "base64-js": "^1.5.1"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3349,6 +3411,28 @@
"wrappy": "1"
}
},
+ "node_modules/openai": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-3.3.0.tgz",
+ "integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==",
+ "dependencies": {
+ "axios": "^0.26.0",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/openai/node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -5059,6 +5143,14 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
+ "node_modules/yaml": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
+ "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/yargs": {
"version": "17.7.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
diff --git a/package.json b/package.json
index 1b1b3540288999a7558c687b6f65575e499d9c89..fbc9036c1e77383a478aa50d9da65de44058fcb0 100644
--- a/package.json
+++ b/package.json
@@ -21,14 +21,18 @@
"@types/express": "^4.17.17",
"@types/ffmpeg-concat": "^1.1.2",
"@types/uuid": "^9.0.2",
+ "eventsource-parser": "^1.0.0",
"express": "^4.18.2",
"ffmpeg-concat": "^1.3.0",
"fluent-ffmpeg": "^2.1.2",
"fs-extra": "^11.1.1",
+ "gpt-tokens": "^1.1.1",
"node-fetch": "^3.3.1",
+ "openai": "^3.3.0",
"puppeteer": "^20.8.0",
"temp-dir": "^3.0.0",
"ts-node": "^10.9.1",
- "uuid": "^9.0.0"
+ "uuid": "^9.0.0",
+ "yaml": "^2.3.1"
}
}
diff --git a/src/config.mts b/src/config.mts
index 7d5dd34f0bcb119b200b631ed1dd156465a57982..f21d34b4c1b644004a0c4459f683f7cd3115be8d 100644
--- a/src/config.mts
+++ b/src/config.mts
@@ -2,9 +2,9 @@ import path from "node:path"
export const storagePath = `${process.env.VC_STORAGE_PATH || './sandbox'}`
-export const tasksDirPath = path.join(storagePath, "tasks")
-export const pendingTasksDirFilePath = path.join(tasksDirPath, "pending")
-export const completedTasksDirFilePath = path.join(tasksDirPath, "completed")
+export const metadataDirPath = path.join(storagePath, "metadata")
+export const pendingMetadataDirFilePath = path.join(metadataDirPath, "pending")
+export const completedMetadataDirFilePath = path.join(metadataDirPath, "completed")
export const filesDirPath = path.join(storagePath, "files")
export const pendingFilesDirFilePath = path.join(filesDirPath, "pending")
diff --git a/src/data/all_words.json b/src/data/all_words.json
index 8f40dbabf69752152a716a6f081e00efff8b7a95..b69d06ee89b2b62c9c501efaa5229cf5d6e70509 100644
--- a/src/data/all_words.json
+++ b/src/data/all_words.json
@@ -4269,7 +4269,7 @@
"tank",
"tap",
"target",
- "task",
+ "video",
"taste",
"tax",
"tea",
diff --git a/src/data/good_words.json b/src/data/good_words.json
index 0ab3beb34f8305deff7b4e93b9656a22f327c216..7c36f2b3dc848f1c7f7216e7f4db26403b13fea9 100644
--- a/src/data/good_words.json
+++ b/src/data/good_words.json
@@ -4259,7 +4259,7 @@
"tank",
"tap",
"target",
- "task",
+ "video",
"taste",
"tax",
"tea",
diff --git a/src/index.mts b/src/index.mts
index 8e475cb4ef0aa10888bb4d1da6a633876d7993bc..551a47a765b69c74e32ccd963d0a2a8e310b5d10 100644
--- a/src/index.mts
+++ b/src/index.mts
@@ -4,17 +4,21 @@ import path from "node:path"
import { validate as uuidValidate } from "uuid"
import express from "express"
-import { VideoTask, VideoTaskRequest } from "./types.mts"
+import { Video, VideoStatus, VideoAPIRequest } from "./types.mts"
import { parseVideoRequest } from "./utils/parseVideoRequest.mts"
-import { savePendingTask } from "./scheduler/savePendingTask.mts"
-import { getTask } from "./scheduler/getTask.mts"
+import { savePendingVideo } from "./scheduler/savePendingVideo.mts"
+import { getVideo } from "./scheduler/getVideo.mts"
import { main } from "./main.mts"
import { completedFilesDirFilePath } from "./config.mts"
-import { deleteTask } from "./scheduler/deleteTask.mts"
-import { getPendingTasks } from "./scheduler/getPendingTasks.mts"
+import { markVideoAsToDelete } from "./scheduler/markVideoAsToDelete.mts"
+import { markVideoAsToAbort } from "./scheduler/markVideoAsToAbort.mts"
+import { markVideoAsToPause } from "./scheduler/markVideoAsToPause.mts"
+import { markVideoAsPending } from "./scheduler/markVideoAsPending.mts"
+import { getPendingVideos } from "./scheduler/getPendingVideos.mts"
import { hasValidAuthorization } from "./utils/hasValidAuthorization.mts"
-import { getAllTasksForOwner } from "./scheduler/getAllTasksForOwner.mts"
+import { getAllVideosForOwner } from "./scheduler/getAllVideosForOwner.mts"
import { initFolders } from "./initFolders.mts"
+import { sortVideosByYoungestFirst } from "./utils/sortVideosByYoungestFirst.mts"
initFolders()
// to disable all processing (eg. to debug)
@@ -26,8 +30,8 @@ const port = 7860
app.use(express.json())
-app.post("/", async (req, res) => {
- const request = req.body as VideoTaskRequest
+app.post("/:ownerId", async (req, res) => {
+ const request = req.body as VideoAPIRequest
if (!hasValidAuthorization(req.headers)) {
console.log("Invalid authorization")
@@ -37,68 +41,45 @@ app.post("/", async (req, res) => {
return
}
- let task: VideoTask = null
+ const ownerId = req.params.ownerId
- console.log(`creating task from request..`)
- console.log(`request: `, JSON.stringify(request))
- try {
- task = await parseVideoRequest(request)
- } catch (err) {
- console.error(`failed to create task: ${task} (${err})`)
+ if (!uuidValidate(ownerId)) {
+ console.error("invalid owner id")
res.status(400)
- res.write(JSON.stringify({ error: "query seems to be malformed" }))
+ res.write(JSON.stringify({ error: `invalid owner id` }))
res.end()
return
}
- console.log(`saving task ${task.id}`)
+ let video: Video = null
+
+ console.log(`creating video from request..`)
+ console.log(`request: `, JSON.stringify(request))
try {
- await savePendingTask(task)
- res.status(200)
- res.write(JSON.stringify(task))
- res.end()
+ video = await parseVideoRequest(ownerId, request)
} catch (err) {
- console.error(err)
- res.status(500)
- res.write(JSON.stringify({ error: "couldn't save the task" }))
- res.end()
- }
-})
-
-// only get the tasks for a specific owner
-app.get("/owner/:ownerId", async (req, res) => {
- if (!hasValidAuthorization(req.headers)) {
- console.log("Invalid authorization")
- res.status(401)
- res.write(JSON.stringify({ error: "invalid token" }))
- res.end()
- return
- }
-
- const ownerId = req.params.ownerId
-
- if (!uuidValidate(ownerId)) {
- console.error("invalid owner id")
+ console.error(`failed to create video: ${video} (${err})`)
res.status(400)
- res.write(JSON.stringify({ error: `invalid owner id` }))
+ res.write(JSON.stringify({ error: "query seems to be malformed" }))
res.end()
return
}
+ console.log(`saving video ${video.id}`)
try {
- const tasks = await getAllTasksForOwner(ownerId)
+ await savePendingVideo(video)
res.status(200)
- res.write(JSON.stringify(tasks, null, 2))
+ res.write(JSON.stringify(video))
res.end()
} catch (err) {
console.error(err)
res.status(500)
- res.write(JSON.stringify({ error: `couldn't get the tasks for owner ${ownerId}` }))
+ res.write(JSON.stringify({ error: "couldn't save the video" }))
res.end()
}
})
-app.get("/download/:id\.mp4", async (req, res) => {
+app.get("/:ownerId/:videoId\.mp4", async (req, res) => {
/*
for simplicity, let's skip auth when fetching videos
@@ -113,7 +94,7 @@ app.get("/download/:id\.mp4", async (req, res) => {
}
*/
- const [ownerId, videoId] = `${req.params.id}`.split("_")
+ const ownerId = req.params.ownerId
if (!uuidValidate(ownerId)) {
console.error("invalid owner id")
@@ -123,6 +104,7 @@ app.get("/download/:id\.mp4", async (req, res) => {
return
}
+ const videoId = req.params.videoId
if (!uuidValidate(videoId)) {
console.error("invalid video id")
@@ -132,9 +114,9 @@ app.get("/download/:id\.mp4", async (req, res) => {
return
}
- let task: VideoTask = null
+ let video: Video = null
try {
- task = await getTask(ownerId, videoId)
+ video = await getVideo(ownerId, videoId)
console.log(`returning video ${videoId} to owner ${ownerId}`)
} catch (err) {
res.status(404)
@@ -143,7 +125,7 @@ app.get("/download/:id\.mp4", async (req, res) => {
return
}
- const completedFilePath = path.join(completedFilesDirFilePath, task.fileName)
+ const completedFilePath = path.join(completedFilesDirFilePath, video.fileName)
// note: we DON'T want to use the pending file path, as there may be operations on it
// (ie. a process might be busy writing stuff to it)
@@ -177,8 +159,9 @@ app.get("/download/:id\.mp4", async (req, res) => {
}
})
-// get all pending tasks
-app.get("/", async (req, res) => {
+// get metadata (json)
+app.get("/:ownerId/:videoId", async (req, res) => {
+
if (!hasValidAuthorization(req.headers)) {
console.log("Invalid authorization")
res.status(401)
@@ -187,20 +170,106 @@ app.get("/", async (req, res) => {
return
}
+ const ownerId = req.params.ownerId
+
+ if (!uuidValidate(ownerId)) {
+ console.error("invalid owner id")
+ res.status(400)
+ res.write(JSON.stringify({ error: `invalid owner id` }))
+ res.end()
+ return
+ }
+
+ const videoId = req.params.videoId
+
+ if (!uuidValidate(videoId)) {
+ console.error("invalid video id")
+ res.status(400)
+ res.write(JSON.stringify({ error: `invalid video id` }))
+ res.end()
+ return
+ }
+
try {
- const tasks = await getPendingTasks()
+ const video = await getVideo(ownerId, videoId)
res.status(200)
- res.write(JSON.stringify(tasks, null, 2))
+ res.write(JSON.stringify(video))
+ res.end()
+ } catch (err) {
+ console.error(err)
+ res.status(404)
+ res.write(JSON.stringify({ error: "couldn't find this video" }))
+ res.end()
+ }
+})
+
+// only get the videos for a specific owner
+app.get("/:ownerId", async (req, res) => {
+ if (!hasValidAuthorization(req.headers)) {
+ console.log("Invalid authorization")
+ res.status(401)
+ res.write(JSON.stringify({ error: "invalid token" }))
+ res.end()
+ return
+ }
+
+ const ownerId = req.params.ownerId
+
+ if (!uuidValidate(ownerId)) {
+ console.error(`invalid owner d ${ownerId}`)
+ res.status(400)
+ res.write(JSON.stringify({ error: `invalid owner id ${ownerId}` }))
+ res.end()
+ return
+ }
+
+ try {
+ const videos = await getAllVideosForOwner(ownerId)
+ sortVideosByYoungestFirst(videos)
+ res.status(200)
+ res.write(JSON.stringify(videos, null, 2))
+ res.end()
+ } catch (err) {
+ console.error(err)
+ res.status(500)
+ res.write(JSON.stringify({ error: `couldn't get the videos for owner ${ownerId}` }))
+ res.end()
+ }
+})
+
+// get all pending videos - this is for admin usage only
+app.get("/", async (req, res) => {
+ if (!hasValidAuthorization(req.headers)) {
+ // this is what users will see in the space - but no need to show something scary
+ console.log("Invalid authorization")
+ res.status(200)
+ res.write(`
+This space is the REST API used by VideoChain UI:
+https://jbilcke-hf-videochain-ui.hf.space
+ `)
+ res.end()
+ // res.status(401)
+ // res.write(JSON.stringify({ error: "invalid token" }))
+ // res.end()
+ return
+ }
+
+ try {
+ const videos = await getPendingVideos()
+ res.status(200)
+ res.write(JSON.stringify(videos, null, 2))
res.end()
} catch (err) {
console.error(err)
res.status(500)
- res.write(JSON.stringify({ error: "couldn't get the tasks" }))
+ res.write(JSON.stringify({ error: "couldn't get the videos" }))
res.end()
}
})
-app.get("/:id", async (req, res) => {
+
+// edit a video
+app.patch("/:ownerId/:videoId", async (req, res) => {
if (!hasValidAuthorization(req.headers)) {
console.log("Invalid authorization")
@@ -210,38 +279,113 @@ app.get("/:id", async (req, res) => {
return
}
- const [ownerId, videoId] = `${req.params.id}`.split("_")
+ const ownerId = req.params.ownerId
if (!uuidValidate(ownerId)) {
- console.error("invalid owner id")
+ console.error(`invalid owner id ${ownerId}`)
res.status(400)
- res.write(JSON.stringify({ error: `invalid owner id` }))
+ res.write(JSON.stringify({ error: `invalid owner id ${ownerId}` }))
res.end()
return
}
+ const videoId = req.params.videoId
if (!uuidValidate(videoId)) {
- console.error("invalid video id")
+ console.error(`invalid video id ${videoId}`)
res.status(400)
- res.write(JSON.stringify({ error: `invalid video id` }))
+ res.write(JSON.stringify({ error: `invalid video id ${videoId}` }))
res.end()
return
}
+ let status: VideoStatus = "unknown"
try {
- const task = await getTask(ownerId, videoId)
- res.status(200)
- res.write(JSON.stringify(task))
- res.end()
+ const request = req.body as { status: VideoStatus }
+ if (['pending', 'abort', 'delete', 'pause'].includes(request.status)) {
+ status = request.status
+ } else {
+ throw new Error(`invalid video status "${request.status}"`)
+ }
} catch (err) {
- console.error(err)
- res.status(404)
- res.write(JSON.stringify({ error: "couldn't find this task" }))
+ console.error(`invalid parameter (${err})`)
+ res.status(401)
+ res.write(JSON.stringify({ error: `invalid parameter (${err})` }))
res.end()
+ return
+ }
+
+ switch (status) {
+ case 'delete':
+ try {
+ await markVideoAsToDelete(ownerId, videoId)
+ console.log(`deleting video ${videoId}`)
+ res.status(200)
+ res.write(JSON.stringify({ success: true }))
+ res.end()
+ } catch (err) {
+ console.error(`failed to delete video ${videoId} (${err})`)
+ res.status(500)
+ res.write(JSON.stringify({ error: `failed to delete video ${videoId}` }))
+ res.end()
+ }
+ break
+
+ case 'abort':
+ try {
+ await markVideoAsToAbort(ownerId, videoId)
+ console.log(`aborted video ${videoId}`)
+ res.status(200)
+ res.write(JSON.stringify({ success: true }))
+ res.end()
+ } catch (err) {
+ console.error(`failed to abort video ${videoId} (${err})`)
+ res.status(500)
+ res.write(JSON.stringify({ error: `failed to abort video ${videoId}` }))
+ res.end()
+ }
+ break
+
+ case 'pause':
+ try {
+ await markVideoAsToPause(ownerId, videoId)
+ console.log(`paused video ${videoId}`)
+ res.status(200)
+ res.write(JSON.stringify({ success: true }))
+ res.end()
+ } catch (err) {
+ console.error(`failed to pause video ${videoId} (${err})`)
+ res.status(500)
+ res.write(JSON.stringify({ error: `failed to pause video ${videoId}` }))
+ res.end()
+ }
+ break
+
+ case 'pending':
+ try {
+ await markVideoAsPending(ownerId, videoId)
+ console.log(`unpausing video ${videoId}`)
+ res.status(200)
+ res.write(JSON.stringify({ success: true }))
+ res.end()
+ } catch (err) {
+ console.error(`failed to unpause video ${videoId} (${err})`)
+ res.status(500)
+ res.write(JSON.stringify({ error: `failed to unpause video ${videoId}` }))
+ res.end()
+ }
+ break
+
+ default:
+ console.log(`unsupported status ${status}`)
+ res.status(401)
+ res.write(JSON.stringify({ error: `unsupported status ${status}` }))
+ res.end()
}
})
+// delete a video - this is legacy, we should use other functions instead
+/*
app.delete("/:id", async (req, res) => {
if (!hasValidAuthorization(req.headers)) {
@@ -270,27 +414,32 @@ app.delete("/:id", async (req, res) => {
return
}
- let task: VideoTask = null
+ // ecurity note: we always check the existence if the video first
+ // that's because we are going to delete all the associated files with a glob,
+ // so we must be sure the id is not a system path or something ^^
+ let video: Video = null
try {
- task = await getTask(ownerId, videoId)
+ video = await getVideo(ownerId, videoId)
} catch (err) {
console.error(err)
res.status(404)
- res.write(JSON.stringify({ error: "couldn't find this task" }))
+ res.write(JSON.stringify({ error: "couldn't find this video" }))
res.end()
+ return
}
try {
- await deleteTask(task)
+ await markVideoAsToDelete(ownerId, videoId)
res.status(200)
res.write(JSON.stringify({ success: true }))
res.end()
} catch (err) {
console.error(err)
res.status(500)
- res.write(JSON.stringify({ success: false, error: "failed to delete the task" }))
+ res.write(JSON.stringify({ success: false, error: "failed to delete the video" }))
res.end()
}
})
+*/
app.listen(port, () => { console.log(`Open http://localhost:${port}`) })
\ No newline at end of file
diff --git a/src/initFolders.mts b/src/initFolders.mts
index 6642a088ff4fa7b9448b363cc4389c4442e22374..525220a9f413a43b19db42341a5c1d795e0766fc 100644
--- a/src/initFolders.mts
+++ b/src/initFolders.mts
@@ -1,11 +1,18 @@
-import { tasksDirPath, pendingTasksDirFilePath, completedTasksDirFilePath, filesDirPath, pendingFilesDirFilePath, completedFilesDirFilePath } from "./config.mts"
+import {
+ metadataDirPath,
+ pendingMetadataDirFilePath,
+ completedMetadataDirFilePath,
+ filesDirPath,
+ pendingFilesDirFilePath,
+ completedFilesDirFilePath
+} from "./config.mts"
import { createDirIfNeeded } from "./utils/createDirIfNeeded.mts"
export const initFolders = () => {
console.log(`initializing folders..`)
- createDirIfNeeded(tasksDirPath)
- createDirIfNeeded(pendingTasksDirFilePath)
- createDirIfNeeded(completedTasksDirFilePath)
+ createDirIfNeeded(metadataDirPath)
+ createDirIfNeeded(pendingMetadataDirFilePath)
+ createDirIfNeeded(completedMetadataDirFilePath)
createDirIfNeeded(filesDirPath)
createDirIfNeeded(pendingFilesDirFilePath)
createDirIfNeeded(completedFilesDirFilePath)
diff --git a/src/llm/enrichVideoSpecsUsingLLM.mts b/src/llm/enrichVideoSpecsUsingLLM.mts
new file mode 100644
index 0000000000000000000000000000000000000000..c4df0ac751c006c49abaa6a8482fbd6f579d9876
--- /dev/null
+++ b/src/llm/enrichVideoSpecsUsingLLM.mts
@@ -0,0 +1,79 @@
+import { ChatCompletionRequestMessage } from "openai"
+
+import { Video, VideoAPIRequest } from "../types.mts"
+import { generateYAML } from "./openai/generateYAML.mts"
+import { HallucinatedVideoRequest, OpenAIErrorResponse } from "./types.mts"
+import { getQueryChatMessages } from "../preproduction/prompts.mts"
+import { getValidNumber } from "../utils/getValidNumber.mts"
+
+
+export const enrichVideoSpecsUsingLLM = async (video: Video): Promise