nonstopmmd/index.js
2023-10-13 16:29:20 -07:00

101 lines
2.8 KiB
JavaScript

var express = require("express");
require("express-async-errors");
var morgan = require("morgan");
var fs = require("fs/promises");
var getVideoLength = v => require('video-length')(v, {bin: "mediainfo"});
var app = module.exports.app = express();
app.set("env", process.env.NODE_ENV || "production");
app.set("trust proxy", ["loopback","linklocal","uniquelocal","173.245.48.0/20","103.21.244.0/22","103.22.200.0/22","103.31.4.0/22","141.101.64.0/18","108.162.192.0/18","190.93.240.0/20","188.114.96.0/20","197.234.240.0/22","198.41.128.0/17","162.158.0.0/15","104.16.0.0/13","104.24.0.0/14","172.64.0.0/13","131.0.72.0/22","2400:cb00::/32","2606:4700::/32","2803:f800::/32","2405:b500::/32","2405:8100::/32","2a06:98c0::/29","2c0f:f248::/32"]);
module.exports.server = app.listen(process.env.PORT || 8080, process.env.ADDR);
app.use(morgan(`:date[iso] :remote-addr :method :url ":req[user-agent]" :referrer`));
app.use("/videos/", express.static("videos"));
app.get("/playlist", async (req, res) => {
res.send(await getPlaylist());
});
app.use(express.static("public"));
require("./mousie-server");
var currentPlaylist;
async function getPlaylist() {
if (!currentPlaylist || Date.now() > currentPlaylist.timestamp + currentPlaylist.totalDuration*1000) {
currentPlaylist = await generatePlaylist();
}
return currentPlaylist;
}
async function generatePlaylist() {
var videofiles = await fs.readdir("videos");
var videos = [];
for (let name of videofiles) {
let metadata = await getMetadata(name);
if (!metadata.duration) {
console.warn("missing duration:", name);
continue;
};
videos.push({
name,
duration: metadata.duration,
source: metadata.source || inferSource(name)
});
}
shuffle(videos);
return {
videos,
timestamp: Date.now(),
totalDuration: videos.reduce((td, v) => td + v.duration, 0)
}
}
async function getMetadata(video) {
var videopath = `videos/${video}`;
var metadatapath = `metadata/${video}.json`;
try {
var metadata = JSON.parse(await fs.readFile(metadatapath, "utf8"));
} catch (error) {
var metadata = {
duration: null
};
try {
metadata.duration = await getVideoLength(videopath);
} catch(error) {
console.error("get video length:", error.message);
};
await fs.writeFile(metadatapath, JSON.stringify(metadata));
}
return metadata;
}
function inferSource(name) {
var youtube_id = name.match(/\[([A-Za-z0-9_-]{11})\]/);
if (youtube_id)
return `https://www.youtube.com/watch?v=${youtube_id[1]}`;
var niconico_id = name.match(/\[(sm\d+)\]/);
if (niconico_id)
return `https://www.nicovideo.jp/watch/${niconico_id[1]}`;
}
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}