server/router/video.js

210 lines
5.6 KiB
JavaScript
Raw Permalink Normal View History

2023-02-08 12:30:56 +01:00
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;