2024-04-26 17:39:08 +00:00
|
|
|
"use strict";
|
2024-05-08 21:08:47 +00:00
|
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
|
|
if (k2 === undefined) k2 = k;
|
|
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
|
|
}
|
|
|
|
Object.defineProperty(o, k2, desc);
|
|
|
|
}) : (function(o, m, k, k2) {
|
|
|
|
if (k2 === undefined) k2 = k;
|
|
|
|
o[k2] = m[k];
|
|
|
|
}));
|
|
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
|
|
}) : function(o, v) {
|
|
|
|
o["default"] = v;
|
|
|
|
});
|
|
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
|
if (mod && mod.__esModule) return mod;
|
|
|
|
var result = {};
|
|
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
|
|
__setModuleDefault(result, mod);
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
|
|
};
|
2024-04-26 17:39:08 +00:00
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
require("dotenv/config");
|
2024-05-08 21:08:47 +00:00
|
|
|
const express_1 = __importDefault(require("express"));
|
|
|
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
|
|
const body_parser_1 = __importDefault(require("body-parser"));
|
2024-05-15 21:29:59 +00:00
|
|
|
const uuid_1 = require("uuid");
|
2024-05-08 21:08:47 +00:00
|
|
|
const Handlebars = __importStar(require("handlebars"));
|
2024-05-15 21:29:59 +00:00
|
|
|
const ws_1 = require("ws");
|
2024-04-26 17:39:08 +00:00
|
|
|
const log_1 = require("./log");
|
2024-05-08 21:08:47 +00:00
|
|
|
const files_1 = require("./files");
|
2024-08-24 14:23:15 +00:00
|
|
|
const fd_1 = require("./fd");
|
2024-08-05 02:34:03 +00:00
|
|
|
const display_1 = require("./display");
|
2024-05-15 18:34:24 +00:00
|
|
|
const ffmpeg_1 = require("./ffmpeg");
|
2024-08-09 20:20:07 +00:00
|
|
|
const ffprobe_1 = require("./ffprobe");
|
2024-07-11 21:26:14 +00:00
|
|
|
const camera_1 = require("./camera");
|
2024-08-01 15:57:49 +00:00
|
|
|
const sequence_1 = require("./sequence");
|
2024-08-18 01:22:17 +00:00
|
|
|
const image_1 = require("./image");
|
2024-08-24 20:26:49 +00:00
|
|
|
let mock = false;
|
2024-05-08 21:08:47 +00:00
|
|
|
const log = (0, log_1.createLog)('fm');
|
|
|
|
const app = (0, express_1.default)();
|
2024-05-15 21:29:59 +00:00
|
|
|
let wss;
|
2024-04-27 17:30:02 +00:00
|
|
|
let fd;
|
2024-08-05 02:34:03 +00:00
|
|
|
let display;
|
2024-05-15 18:34:24 +00:00
|
|
|
let ffmpeg;
|
2024-08-09 20:20:07 +00:00
|
|
|
let ffprobe;
|
2024-08-18 01:22:17 +00:00
|
|
|
let image;
|
2024-07-11 21:26:14 +00:00
|
|
|
let camera;
|
2024-08-01 15:57:49 +00:00
|
|
|
let sequence;
|
2024-05-08 21:08:47 +00:00
|
|
|
let index;
|
|
|
|
let port;
|
2024-05-15 21:29:59 +00:00
|
|
|
let wsPort;
|
2024-05-08 21:08:47 +00:00
|
|
|
let sequences;
|
2024-05-15 15:21:39 +00:00
|
|
|
let videos;
|
2024-05-08 21:08:47 +00:00
|
|
|
let width;
|
|
|
|
let height;
|
|
|
|
log.info('Starting filmout_manager...');
|
|
|
|
app.use(body_parser_1.default.json());
|
|
|
|
app.use(body_parser_1.default.urlencoded({ extended: true }));
|
2024-05-15 21:29:59 +00:00
|
|
|
app.use('/static', express_1.default.static('./static'));
|
2024-05-08 21:08:47 +00:00
|
|
|
async function createTemplate(filePath) {
|
|
|
|
let tmpl;
|
|
|
|
try {
|
|
|
|
tmpl = await promises_1.default.readFile(filePath, 'utf8');
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
log.error(err);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return Handlebars.compile(tmpl);
|
|
|
|
}
|
|
|
|
async function settings() {
|
|
|
|
let sequencesExists = false;
|
2024-05-15 15:21:39 +00:00
|
|
|
let videosExists = false;
|
2024-04-27 17:30:02 +00:00
|
|
|
if (typeof process.env['FD'] === 'undefined') {
|
|
|
|
log.error('Please include an FD value containing the path to your fd binary in .env');
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log.info(`FD=${process.env['FD']}`);
|
|
|
|
}
|
2024-05-15 21:29:59 +00:00
|
|
|
if (typeof process.env['FFMPEG'] === 'undefined') {
|
|
|
|
log.error('Please include an FFMPEG value containing the path to your ffmpeg binary in .env');
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log.info(`FFMPEG=${process.env['FFMPEG']}`);
|
|
|
|
}
|
2024-04-27 17:30:02 +00:00
|
|
|
if (typeof process.env['WIDTH'] === 'undefined') {
|
|
|
|
log.error('Please include a WIDTH value containing the width of the screen you are using in .env');
|
|
|
|
process.exit(2);
|
|
|
|
}
|
|
|
|
else {
|
2024-05-08 21:08:47 +00:00
|
|
|
width = parseInt(process.env['WIDTH']);
|
|
|
|
log.info(`WIDTH=${width}`);
|
2024-04-27 17:30:02 +00:00
|
|
|
}
|
|
|
|
if (typeof process.env['HEIGHT'] === 'undefined') {
|
|
|
|
log.error('Please include a HEIGHT value containing the height of the screen you are using in .env');
|
|
|
|
process.exit(3);
|
|
|
|
}
|
|
|
|
else {
|
2024-05-08 21:08:47 +00:00
|
|
|
height = parseInt(process.env['HEIGHT']);
|
|
|
|
log.info(`HEIGHT=${height}`);
|
2024-04-27 17:30:02 +00:00
|
|
|
}
|
|
|
|
if (typeof process.env['FD_HOST'] === 'undefined') {
|
|
|
|
log.error('Please include a FD_HOST value with the host that the fd socket server is hosted on in .env');
|
|
|
|
process.exit(4);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log.info(`FD_HOST=${process.env['FD_HOST']}`);
|
|
|
|
}
|
|
|
|
if (typeof process.env['FD_PORT'] === 'undefined') {
|
2024-05-08 21:08:47 +00:00
|
|
|
log.error('Please include a FD_PORT value with the port that the fd socket server is hosted on in .env');
|
2024-04-27 17:30:02 +00:00
|
|
|
process.exit(5);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log.info(`FD_PORT=${process.env['FD_PORT']}`);
|
|
|
|
}
|
2024-05-08 21:08:47 +00:00
|
|
|
if (typeof process.env['PORT'] === 'undefined') {
|
|
|
|
log.error('Please include a PORT value with the port that the HTTP web process is hosted on in .env');
|
|
|
|
process.exit(6);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
port = parseInt(process.env['PORT']);
|
|
|
|
log.info(`PORT=${port}`);
|
|
|
|
}
|
2024-05-15 21:29:59 +00:00
|
|
|
if (typeof process.env['WS_PORT'] === 'undefined') {
|
|
|
|
log.error('Please include a WSPORT value with the port that the WebSocket web process is hosted on in .env');
|
|
|
|
process.exit(6);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
wsPort = parseInt(process.env['WS_PORT']);
|
2024-08-15 02:43:02 +00:00
|
|
|
log.info(`WS_PORT=${wsPort}`);
|
|
|
|
}
|
|
|
|
if (wsPort === port) {
|
|
|
|
log.error(`Websocket port (${wsPort}) should not be the same as HTTP port (${port})`);
|
|
|
|
process.exit(7);
|
2024-05-15 21:29:59 +00:00
|
|
|
}
|
2024-05-08 21:08:47 +00:00
|
|
|
if (typeof process.env['SEQUENCES'] === 'undefined') {
|
|
|
|
log.error('Please include a SEQUENCES directory where the image sequences will be located in .env');
|
|
|
|
process.exit(7);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sequences = process.env['SEQUENCES'];
|
|
|
|
sequencesExists = await files_1.Files.exists(sequences);
|
|
|
|
if (!sequencesExists) {
|
|
|
|
log.error(`The SEQUENCES directory in .env, ${sequences}, does not exist`);
|
|
|
|
process.exit(8);
|
|
|
|
}
|
|
|
|
log.info(`SEQUENCES=${sequences}`);
|
|
|
|
}
|
2024-05-15 15:21:39 +00:00
|
|
|
if (typeof process.env['VIDEOS'] === 'undefined') {
|
|
|
|
log.error('Please include a VIDEOS directory where the videos will be located in .env');
|
|
|
|
process.exit(7);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
videos = process.env['VIDEOS'];
|
|
|
|
videosExists = await files_1.Files.exists(videos);
|
|
|
|
if (!sequencesExists) {
|
|
|
|
log.error(`The VIDEOS directory in .env, ${videos}, does not exist`);
|
|
|
|
process.exit(8);
|
|
|
|
}
|
|
|
|
log.info(`VIDEOS=${videos}`);
|
|
|
|
}
|
2024-08-24 20:26:49 +00:00
|
|
|
if (typeof process.env['MOCK'] !== 'undefined') {
|
|
|
|
if (process.env['MOCK'].trim().toLowerCase() === "true" || process.env['MOCK'].trim() === '1') {
|
|
|
|
mock = true;
|
|
|
|
log.info(`MOCK=true`);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mock = false;
|
|
|
|
}
|
|
|
|
}
|
2024-04-26 17:39:08 +00:00
|
|
|
}
|
2024-05-15 21:29:59 +00:00
|
|
|
function onWssConnection(ws, req) {
|
|
|
|
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
|
|
|
if (ip.substr(0, 7) === "::ffff:")
|
|
|
|
ip = ip.substr(7);
|
|
|
|
log.info(`Client ${ip} connected to WebSocket server`);
|
|
|
|
ws.ip = ip;
|
|
|
|
ws.session = (0, uuid_1.v4)();
|
2024-07-13 02:00:24 +00:00
|
|
|
ws.on('message', function (data) { onClientMessage(data, ws); });
|
2024-08-24 14:23:15 +00:00
|
|
|
sequence.updateClientsOnLoad();
|
2024-07-13 02:00:24 +00:00
|
|
|
}
|
|
|
|
async function onClientMessage(data, ws) {
|
|
|
|
let msg = null;
|
|
|
|
try {
|
|
|
|
msg = JSON.parse(data);
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
log.error('Error parsing message', err);
|
|
|
|
}
|
|
|
|
if (msg !== null && typeof msg.cmd !== 'undefined') {
|
2024-08-15 02:43:02 +00:00
|
|
|
await cmd(msg);
|
2024-07-13 02:00:24 +00:00
|
|
|
}
|
|
|
|
}
|
2024-08-01 15:57:49 +00:00
|
|
|
async function cmd(msg) {
|
2024-08-15 02:43:02 +00:00
|
|
|
let success = false;
|
2024-08-01 15:57:49 +00:00
|
|
|
switch (msg.cmd) {
|
2024-08-24 20:26:49 +00:00
|
|
|
case 'pong':
|
|
|
|
//received keepalive
|
|
|
|
break;
|
2024-07-13 02:00:24 +00:00
|
|
|
case 'open':
|
|
|
|
await cameraOpen();
|
2024-08-15 02:43:02 +00:00
|
|
|
break;
|
2024-08-01 15:57:49 +00:00
|
|
|
case 'close':
|
|
|
|
await cameraClose();
|
2024-08-15 02:43:02 +00:00
|
|
|
break;
|
2024-08-01 15:57:49 +00:00
|
|
|
case 'select':
|
2024-08-16 04:52:46 +00:00
|
|
|
await select(msg.state.sequence.hash);
|
2024-08-15 02:43:02 +00:00
|
|
|
break;
|
2024-08-24 14:23:15 +00:00
|
|
|
case 'start':
|
|
|
|
start();
|
|
|
|
break;
|
|
|
|
case 'stop':
|
|
|
|
stop();
|
|
|
|
break;
|
|
|
|
case 'advance':
|
|
|
|
frameAdvance();
|
|
|
|
break;
|
|
|
|
case 'rewind':
|
|
|
|
frameRewind();
|
|
|
|
break;
|
2024-07-13 02:00:24 +00:00
|
|
|
default:
|
2024-08-01 15:57:49 +00:00
|
|
|
log.warn(`No matching command: ${msg.cmd}`);
|
2024-07-13 02:00:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
async function cameraOpen() {
|
|
|
|
await camera.open();
|
2024-08-15 02:43:02 +00:00
|
|
|
send({ cmd: 'open' });
|
2024-05-15 21:29:59 +00:00
|
|
|
}
|
2024-08-01 15:57:49 +00:00
|
|
|
async function cameraClose() {
|
|
|
|
await camera.close();
|
2024-08-15 02:43:02 +00:00
|
|
|
send({ cmd: 'close' });
|
2024-08-01 15:57:49 +00:00
|
|
|
}
|
2024-08-24 14:23:15 +00:00
|
|
|
function frameAdvance() {
|
|
|
|
sequence.frameAdvance();
|
|
|
|
}
|
|
|
|
function frameRewind() {
|
|
|
|
sequence.frameRewind();
|
|
|
|
}
|
2024-08-01 15:57:49 +00:00
|
|
|
async function select(id) {
|
|
|
|
const sequencesArr = await files_1.Files.enumerateSequences(sequences);
|
|
|
|
const seq = sequencesArr.find(el => el.hash === id);
|
|
|
|
if (typeof seq == 'undefined' || seq == null) {
|
|
|
|
log.error('Sequence not found, maybe deleted?', new Error(`Cannot find sequence ${id}`));
|
2024-08-15 02:43:02 +00:00
|
|
|
return false;
|
2024-08-01 15:57:49 +00:00
|
|
|
}
|
|
|
|
await sequence.load(seq);
|
2024-08-15 02:43:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
2024-08-24 14:23:15 +00:00
|
|
|
function start() {
|
|
|
|
sequence.start();
|
|
|
|
}
|
|
|
|
function stop() {
|
|
|
|
sequence.stop();
|
|
|
|
}
|
2024-08-15 02:43:02 +00:00
|
|
|
async function send(msg) {
|
|
|
|
const msgStr = JSON.stringify(msg);
|
|
|
|
wss.clients.forEach((client) => {
|
|
|
|
client.send(msgStr);
|
|
|
|
});
|
2024-08-01 15:57:49 +00:00
|
|
|
}
|
2024-08-24 20:26:49 +00:00
|
|
|
async function keepAlive() {
|
|
|
|
await send({ cmd: 'ping' });
|
|
|
|
}
|
2024-05-08 21:08:47 +00:00
|
|
|
app.get('/', async (req, res, next) => {
|
2024-05-15 21:29:59 +00:00
|
|
|
const sequencesArr = await files_1.Files.enumerateSequences(sequences);
|
2024-07-31 18:39:21 +00:00
|
|
|
//const videosArr : VideoObject[] = await Files.enumerateVideos(videos);
|
2024-08-24 20:26:49 +00:00
|
|
|
const html = index({ sequences: sequencesArr, width, height, wsPort });
|
2024-05-08 21:08:47 +00:00
|
|
|
res.send(html);
|
|
|
|
});
|
2024-08-18 01:22:17 +00:00
|
|
|
app.get('/:width/:height/image.jpg', async (req, res, next) => {
|
|
|
|
let data;
|
|
|
|
let current = sequence.getCurrent();
|
|
|
|
let width = parseInt(req.params.width);
|
|
|
|
let height = parseInt(req.params.height);
|
|
|
|
if (current !== null) {
|
|
|
|
try {
|
|
|
|
data = await image.thumbnail(current.path, width, height);
|
|
|
|
log.info(`Image: ${current.path} - ${width},${height}`);
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
log.error('Error getting thumbnail of ${current}', err);
|
|
|
|
return next(new Error('Error getting thumbnail'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
try {
|
|
|
|
data = await image.blank(width, height);
|
|
|
|
log.info(`Blank - ${width},${height}`);
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
log.error('Error generating blank image', err);
|
|
|
|
return next(new Error('Error generating blank image'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res.contentType('image/jpeg');
|
|
|
|
res.send(data);
|
2024-08-16 04:52:46 +00:00
|
|
|
});
|
2024-04-26 17:39:08 +00:00
|
|
|
async function main() {
|
2024-05-08 21:08:47 +00:00
|
|
|
await settings();
|
|
|
|
index = await createTemplate('./views/index.hbs');
|
2024-05-15 21:29:59 +00:00
|
|
|
ffmpeg = new ffmpeg_1.FFMPEG(process.env['FFMPEG']);
|
2024-08-09 20:20:07 +00:00
|
|
|
ffprobe = new ffprobe_1.FFPROBE();
|
2024-08-18 01:22:17 +00:00
|
|
|
image = new image_1.Image();
|
2024-08-24 20:26:49 +00:00
|
|
|
camera = new camera_1.Camera(mock);
|
2024-08-05 02:34:03 +00:00
|
|
|
display = new display_1.Display(width, height);
|
2024-08-24 20:26:49 +00:00
|
|
|
fd = new fd_1.FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), mock);
|
2024-05-08 21:08:47 +00:00
|
|
|
app.listen(port, async () => {
|
|
|
|
log.info(`filmout_manager HTTP server running on port ${port}`);
|
|
|
|
});
|
2024-05-15 21:29:59 +00:00
|
|
|
wss = new ws_1.Server({ port: wsPort, clientTracking: true });
|
|
|
|
wss.on('connection', onWssConnection);
|
|
|
|
log.info(`filmout_manager WebSocket server running on port ${wsPort}`);
|
2024-08-05 02:34:03 +00:00
|
|
|
//ffmpeg.listFormats();
|
|
|
|
//log.info(await TestImage.Focus(640, 480));
|
2024-08-15 02:43:02 +00:00
|
|
|
sequence = new sequence_1.Sequence(camera, fd, display, ffprobe, send);
|
2024-08-24 20:26:49 +00:00
|
|
|
setInterval(keepAlive, 30000);
|
2024-04-26 17:39:08 +00:00
|
|
|
}
|
|
|
|
main();
|
|
|
|
//# sourceMappingURL=index.js.map
|