import express from "express" import fileUpload from "express-fileupload" import path from "path" import os from "os" import fs from "fs" import archiver from "archiver" import ffmpeg from "fluent-ffmpeg" // import { initFolders } from "./initFolders.mts" import { runColmap, ColmapOptions } from "./colmap.mts" // initFolders() declare module 'express-serve-static-core' { interface Request { files: any; } } const app = express() const port = 7860 const maxActiveRequests = 4 let activeRequests = 0 app.use(express.json({limit: '50mb'})) app.use(express.urlencoded({limit: '50mb', extended: true})) app.use(fileUpload()) app.post("/", async (req, res) => { if (activeRequests >= maxActiveRequests) { res.status(503).json({message: "Service Unavailable: Max concurrent requests reached. Please try again later"}).end(); return } activeRequests++ if (!req.files || !req.files.data || req.files.data.mimetype !== 'video/mp4') { res.status(400).json({error: "Missing or invalid data file in request"}).end() return } let options: ColmapOptions = req.body let dataFile: fileUpload.UploadedFile = req.files.data let projectTempDir = path.join(os.tmpdir(), Math.random().toString().slice(2)) let outputTempDir = path.join(os.tmpdir(), Math.random().toString().slice(2)) try { fs.mkdirSync(projectTempDir) fs.mkdirSync(outputTempDir) await dataFile.mv(path.join(projectTempDir, dataFile.name)) let imageFolder = path.join(projectTempDir, 'images'); fs.mkdirSync(imageFolder) await new Promise((resolve, reject) => { ffmpeg(path.join(projectTempDir, dataFile.name)) .outputOptions('-vf', 'fps=1') // Change this value depending on the number of frames you want from video. .output(path.join(imageFolder, 'image-%03d.png')) .on('end', resolve) .on('error', reject) .run() }) options.projectPath = projectTempDir options.workspacePath = projectTempDir options.imagePath = imageFolder const result = await runColmap(options) let outputFilePath = path.join(outputTempDir, 'output.zip') let output = fs.createWriteStream(outputFilePath) let archive = archiver('zip') archive.directory(outputTempDir, false) archive.pipe(output) await archive.finalize() res.status(200) res.download(outputFilePath, 'output.zip', (error) => { if (!error) fs.rmSync(projectTempDir, {recursive: true, force: true}) fs.rmSync(outputTempDir, {recursive: true, force: true}) }) } catch (error) { res.status(500).json({ error: "Couldn't generate pose data", message: error }).end() } finally { activeRequests-- } }); app.get("/", async (req, res) => { res.status(200) res.write(` Campose API is a micro-service used to generate came pose data from a set of images. `) res.end() }) app.listen(port, () => { console.log(`Open http://localhost:${port}`) })