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)) {
|
2024-08-18 22:15:47 +02:00
|
|
|
res.end();
|
2023-02-08 12:30:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|