console.log("router/video INIT"); const express = require("express"); const fs = require("fs"); const ffmpeg = require("fluent-ffmpeg"); const database = require("../services/database"); const notifier = require("../services/notifier"); const server = require("../server"); const passport = server.passport; const config = server.config; const router = new express.Router(); const video_cache_dir = config.cache_folder + "/video_cache/"; const checkGuest = server.checkGuest; const resize_image_for_box = require("../services/cover/resizer").resize_image_for_box if (!fs.existsSync(video_cache_dir)) { fs.mkdirSync(video_cache_dir); } router.route("") .post(passport.authenticate("jwt", { session: false }), (req, res) => { if (req.files) { let file = undefined; let cover = undefined; if (Array.isArray(req.files.file)) { file = req.files.file[0]; cover = req.files.file[1]; } else { file = req.files.file; } let box = JSON.parse(req.body.box); let url_user = config.upload_folder + "/" + req.user._id; let folder = Math.floor(Date.now() / 1000 / 60); let url_folder = url_user + "/" + folder let file_path = url_folder + "/" + file.name; let item = { path: file_path.replace(config.upload_folder, ""), box: box.title, box_path: url_folder, title: req.body.title, mime: file.mimetype, year: box.year, owner: req.user }; //SAVE VIDEO FILE function saveVideoFile() { file.mv(file_path).then(() => { notifier.emit("pathdata_excluded", item); res.status(200).end(); }); } if (cover) { resize_image_for_box(cover.data, (result) => { item.cover32 = result.cover32; item.cover64 = result.cover64; item.cover128 = result.cover128; item.cover256 = result.cover256; saveVideoFile(); }); } else { saveVideoFile(); } } }); router.route("/most_viewed") .get(checkGuest, (req, res) => { let filter = undefined; if (req.user._id == -1) { filter = ['global']; } database.videos.mostViewed(filter, (result) => { res.json(result); }); }); router.route("/:id/stream") .get((req, res) => { database.videos.byId(req.params.id, result => { process.stdout.write("video stream: " + result.title + "\n"); let full_path = config.video_folder + result.path; if (!fs.existsSync(full_path)) { full_path = config.upload_folder + result.path; if (!fs.existsSync(full_path)) { return res.end(); } } stream(req, res, full_path, result.mime); }); }); router.route("/:id/stream/:rate/:audioIndex") .get((req, res) => { if (!server.lists.video_quality.includes(req.params.rate)) { req.end(); } let video = getFileName(req); if (fs.existsSync(video)) { stream(req, res, video, "video/mpeg"); } else { convert(req, res, video, () => { stream(req, res, video, "video/mp4"); }); } }); router.route("/:id/convert/:rate/:audioIndex") .get((req, res) => { if (!server.lists.video_quality.includes(req.params.rate)) { req.end(); } let video = getFileName(req); if (!fs.existsSync(video)) { convert(req, res, video); } res.end(); }); function convert(req, res, video, callback = null) { database.videos.byId(req.params.id, result => { process.stdout.write("video stream: " + result.title + "\n"); let full_path = config.video_folder + result.path; if (!fs.existsSync(full_path)) { full_path = config.upload_folder + result.path; if (!fs.existsSync(full_path)) { return res.end(); } } ffmpeg(full_path) .audioBitrate(128) .audioChannels(2) .videoCodec("libvpx") .size("?x" + req.params.rate) .videoBitrate(req.params.rate * 2) .outputOptions([ "-cpu-used " + server.config.cpu_cores, "-map 0:v:0", "-map 0:a:" + req.params.audioIndex ]) .output(video) .on("start", commandLine => { process.stdout.write( "Spawned Ffmpeg with command: " + commandLine + "\n" ); if (callback) { let interval = setInterval(() => { if (fs.existsSync(video) && fs.statSync(video).size > 3000000) { clearInterval(interval); callback(); } }, 4000); } }) .on("error", e => { process.stdout.write(e); }) .run(); }); } function stream(req, res, video, mime) { process.stdout.write("video stream: " + video + "\n"); let stat = fs.statSync(video); if (req.headers.range) { var range = req.headers.range; var parts = range.replace(/bytes=/, "").split("-"); var partialstart = parts[0]; var partialend = parts[1]; var start = parseInt(partialstart, 10); var end = partialend ? parseInt(partialend, 10) : stat.size - 1; var chunksize = end - start + 1; var readStream = fs.createReadStream(video, { start: start, end: end }); res.writeHead(206, { "Content-Range": "bytes " + start + "-" + end + "/" + stat.size, "Accept-Ranges": "bytes", "Content-Length": chunksize, "Content-Type": mime }); readStream.pipe(res); } else { res.writeHead(200, { "Content-Length": stat.size, "Content-Type": mime }); fs.createReadStream(video).pipe(res); } } function getFileName(req) { return ( video_cache_dir + req.params.id + "-" + req.params.rate + "-" + req.params.audioIndex + ".webm" ); } module.exports = router;