diff --git a/index.js b/index.js index 0109216..eb20f47 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ import { fileURLToPath } from "url"; import sqlite3 from "sqlite3"; let sessions = []; +let videoQueue = []; const db = new sqlite3.Database("database.sqlite3"); @@ -25,6 +26,22 @@ function dbRun(query, ...parameters) { return new Promise((resolve, reject) => db.run(query, parameters, (err, data) => err ? reject(err) : resolve(data))); } +function runCommand(cmd, args) { + return new Promise(resolve => { + const process = childProcess.spawn(cmd, args); + + let data = ""; + let error = false; + process.stdout.on("data", chunk => data += chunk); + process.stderr.on("data", chunk => { + error = true; + data += chunk; + }); + + process.on("close", () => resolve({ error, data })); + }); +} + function videoPath(id) { return `videos/${id}.webm`; } @@ -167,7 +184,7 @@ app.post("/api/upload-video", authorized(), fileUpload({ limits: { fileSize: 2 * } if (req.files.video.mimetype !== "video/mp4") { - return res.status(400).json({ ok: false, error: "bad mimetype" }); + return res.status(415).json({ ok: false, error: "bad mimetype" }); } const userId = req.user.id; @@ -176,33 +193,51 @@ app.post("/api/upload-video", authorized(), fileUpload({ limits: { fileSize: 2 * const newPath = path.join(dirname(), videoPath(id)); const thumbnailPath = path.join(dirname(), "videos", `${id}.png`); - console.log(newPath); - - let exitCode = await new Promise(resolve => { - const process = childProcess.spawn("ffmpeg", ["-i", tempPath, "-b:v", "1M", "-b:a", "192k", newPath]); - process.stderr.on("data", (data) => console.error(data.toString())); - process.on("close", resolve); - }); - - if (exitCode !== 0) { - throw new Error("ffmpeg failed"); + const durationResult = await runCommand("ffprobe", ["-i", tempPath, "-show_format", "-loglevel", "error"]); + if (durationResult.error) { + return res.status(400).json({ ok: false, error: "invalid video file" }); } - exitCode = await new Promise(resolve => { - const process = childProcess.spawn("ffmpeg", ["-i", tempPath, "-ss", "00:00:01.000", "-vframes", "1", thumbnailPath]); - process.stderr.on("data", (data) => console.error(data.toString())); - process.on("close", resolve); + const duration = parseFloat(durationResult.data.match(/duration=([.\d]+)/)[1]); + + const queueItem = { + videoId: id, + userId, + errors: [], + progress: 0, + duration, + }; + + videoQueue.push(queueItem); + + const uploadProcess = childProcess.spawn("ffmpeg", ["-i", tempPath, "-b:v", "1M", "-b:a", "192k", "-loglevel", "error", newPath]); + const thumbnailProcess = childProcess.spawn("ffmpeg", ["-i", tempPath, "-ss", "00:00:01.000", "-vframes", "1", "-loglevel", "error", thumbnailPath]); + + uploadProcess.stderr.on("data", data => queueItem.errors.push(data.toString())); + thumbnailProcess.stderr.on("data", data => queueItem.errors.push(data.toString())); + + uploadProcess.on("close", () => { + if (queueItem.errors.length) { + return; + } + + dbRun("INSERT INTO videos (id, user_id, title) VALUES (?, ?, ?)", id, userId, title); + + const index = videoQueue.indexOf(item => item.videoId === queueItem.videoId) + videoQueue.splice(index, 1); }); - if (exitCode !== 0) { - throw new Error("thumbnail generation failed"); - } - - await dbRun("INSERT INTO videos (id, user_id, title) VALUES (?, ?, ?)", id, userId, title); - return res.status(200).json({ ok: true }); }); +app.get("/api/video-queue", authorized(), (req, res) => { + const userId = req.user.id; + + const videos = videoQueue.filter(item => item.userId === userId); + + return res.status(200).json({ ok: true, videos }); +}); + app.get("/api/video-info", async (req, res) => { const id = req.query["id"];