"use strict"; 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 }; }; Object.defineProperty(exports, "__esModule", { value: true }); require("dotenv/config"); const express_1 = __importDefault(require("express")); const promises_1 = __importDefault(require("fs/promises")); const body_parser_1 = __importDefault(require("body-parser")); const uuid_1 = require("uuid"); const Handlebars = __importStar(require("handlebars")); const ws_1 = require("ws"); const log_1 = require("./log"); const env_1 = require("./env"); const files_1 = require("./files"); const testimage_1 = require("./testimage"); const fd_1 = require("./fd"); const display_1 = require("./display"); const ffmpeg_1 = require("./ffmpeg"); const ffprobe_1 = require("./ffprobe"); const camera_1 = require("./camera"); const sequence_1 = require("./sequence"); const image_1 = require("./image"); let mock = false; const log = (0, log_1.createLog)('fm'); const app = (0, express_1.default)(); let wss; let fd; let display; let ffmpeg; let ffprobe; let image; let camera; let sequence; let index; let focusImage = null; let framingImage = null; let port; let wsPort; let sequences; let videos; 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 })); app.use('/static', express_1.default.static('./static')); 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; let videosExists = false; if ((0, env_1.envString)('FD', null) === null) { 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']}`); } if ((0, env_1.envString)('FFMPEG', null) === null) { 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']}`); } if ((0, env_1.envInt)('WIDTH', null) === null) { log.error('Please include a WIDTH value containing the width of the screen you are using in .env'); process.exit(2); } else { width = (0, env_1.envInt)('WIDTH', 0); log.info(`WIDTH=${width}`); } if ((0, env_1.envInt)('HEIGHT', null) === null) { log.error('Please include a HEIGHT value containing the height of the screen you are using in .env'); process.exit(3); } else { height = (0, env_1.envInt)('HEIGHT', 0); log.info(`HEIGHT=${height}`); } if ((0, env_1.envString)('FD_HOST', null) === null) { 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 ((0, env_1.envInt)('FD_PORT', null) === null) { log.error('Please include a FD_PORT value with the port that the fd socket server is hosted on in .env'); process.exit(5); } else { log.info(`FD_PORT=${process.env['FD_PORT']}`); } if ((0, env_1.envInt)('PORT', null) === null) { 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 = (0, env_1.envInt)('PORT', 8080); log.info(`PORT=${port}`); } if ((0, env_1.envInt)('WS_PORT', null) === null) { 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 = (0, env_1.envInt)('WS_PORT', 8081); 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); } if ((0, env_1.envString)('SEQUENCES', null) === null) { log.error('Please include a SEQUENCES directory where the image sequences will be located in .env'); process.exit(7); } else { sequences = (0, env_1.envString)('SEQUENCES', null); sequencesExists = await files_1.Files.init(sequences); if (!sequencesExists) { log.error(`The SEQUENCES directory in .env, ${sequences}, does not exist`); process.exit(8); } log.info(`SEQUENCES=${sequences}`); } if ((0, env_1.envString)('VIDEOS', null) === null) { log.error('Please include a VIDEOS directory where the videos will be located in .env'); process.exit(7); } else { videos = (0, env_1.envString)('VIDEOS', null); 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}`); } if ((0, env_1.envString)('MOCK', null) !== null) { if ((0, env_1.envString)('MOCK', '').trim().toLowerCase() === "true" || (0, env_1.envString)('MOCK', '').trim() === '1') { mock = true; log.info(`MOCK=true`); } else { mock = false; } } } 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)(); ws.on('message', function (data) { onClientMessage(data, ws); }); sequence.updateClientsOnLoad(); } 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') { await cmd(msg); } } async function cmd(msg) { let success = false; switch (msg.cmd) { case 'pong': //received keepalive break; case 'open': await cameraOpen(); break; case 'close': await cameraClose(); break; case 'select': await select(msg.state.sequence.hash); break; case 'start': await start(); break; case 'stop': stop(); break; case 'advance': frameAdvance(); break; case 'rewind': frameRewind(); break; case 'set': frameSet(msg.state.sequence.current); break; case 'exposure': exposureSet(msg.state.exposure); break; case 'focus': await focus(); break; case 'framing': await framing(); break; case 'offset': offset(msg); break; case 'size': size(msg); break; case 'scale': scale(msg); break; default: log.warn(`No matching command: ${msg.cmd}`); } } async function cameraOpen() { await camera.open(); send({ cmd: 'open' }); } async function cameraClose() { await camera.close(); send({ cmd: 'close' }); } function frameAdvance() { focusImage = null; sequence.frameAdvance(); } function frameRewind() { focusImage = null; sequence.frameRewind(); } function frameSet(frame) { focusImage = null; sequence.frameSet(frame); } function exposureSet(exposure) { if (exposure < 1) { exposure = 1; } sequence.setExposure(exposure); } async function select(id) { const sequencesArr = await files_1.Files.enumerateSequences(); 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}`)); return false; } await sequence.load(seq); return true; } async function start() { if (focusImage !== null) { await stopFocus(); } if (framingImage !== null) { await stopFraming(); } sequence.start(); } function stop() { sequence.stop(); } async function send(msg) { const msgStr = JSON.stringify(msg); wss.clients.forEach((client) => { client.send(msgStr); }); } async function keepAlive() { await send({ cmd: 'ping' }); } async function focus() { let pos; let dims; let state; let filePath; if (focusImage !== null) { await stopFocus(); return; } if (sequence.isLoaded()) { state = sequence.getState(); pos = { w: state.display.width, h: state.display.height, x: state.offset.x, y: state.offset.y }; } else { dims = display.getScreen(); pos = { w: dims.width, h: dims.height, x: 0, y: 0 }; } focusImage = await testimage_1.TestImage.Focus(pos.w, pos.h); await fd.load(focusImage, pos.x, pos.y, pos.w, pos.h); await fd.display(focusImage); send({ cmd: 'focus' }); } async function stopFocus() { focusImage = null; await fd.stop(focusImage); send({ cmd: 'unfocus' }); } async function framing() { let pos; let dims; let state; let filePath; if (framingImage !== null) { await stopFraming(); return; } if (sequence.isLoaded()) { state = sequence.getState(); pos = { w: state.display.width, h: state.display.height, x: state.offset.x, y: state.offset.y }; } else { dims = display.getScreen(); pos = { w: dims.width, h: dims.height, x: 0, y: 0 }; } framingImage = await testimage_1.TestImage.Frame(pos.w, pos.h); await fd.load(framingImage, pos.x, pos.y, pos.w, pos.h); await fd.display(framingImage); send({ cmd: 'framing' }); } async function stopFraming() { framingImage = null; await fd.stop(framingImage); send({ cmd: 'unframing' }); } function offset(msg) { let current = sequence.getCurrent(); if (current !== null) { sequence.updateOffset(msg.x, msg.y); } } function size(msg) { let current = sequence.getCurrent(); if (current !== null) { sequence.updateSize(msg.width, msg.height); } } function scale(msg) { let current = sequence.getCurrent(); if (current !== null) { sequence.updateScale(msg.scale); } } app.get('/', async (req, res, next) => { const sequencesArr = await files_1.Files.enumerateSequences(); //const videosArr : VideoObject[] = await Files.enumerateVideos(videos); const html = index({ sequences: sequencesArr, width, height, wsPort }); res.send(html); }); 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 (focusImage !== null) { try { data = await image.thumbnail(focusImage, 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 if (framingImage !== null) { try { data = await image.thumbnail(framingImage, 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 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); }); async function main() { await settings(); index = await createTemplate('./views/index.hbs'); ffmpeg = new ffmpeg_1.FFMPEG((0, env_1.envString)('FFMPEG', 'ffmpeg')); ffprobe = new ffprobe_1.FFPROBE(); image = new image_1.Image(); camera = new camera_1.Camera(mock); display = new display_1.Display(width, height); fd = new fd_1.FD((0, env_1.envString)('FD', 'fd'), width, height, (0, env_1.envString)('FD_HOST', 'localhost'), (0, env_1.envInt)('FD_PORT', 8082), (0, env_1.envString)('FD_DISPLAY', null), mock); app.listen(port, async () => { log.info(`filmout_manager HTTP server running on port ${port}`); }); wss = new ws_1.Server({ port: wsPort, clientTracking: true }); wss.on('connection', onWssConnection); log.info(`filmout_manager WebSocket server running on port ${wsPort}`); //ffmpeg.listFormats(); //log.info(await TestImage.Focus(640, 480)); sequence = new sequence_1.Sequence(camera, fd, display, ffprobe, send); setInterval(keepAlive, 30000); } main(); process.stdin.resume(); // so the program will not close instantly async function exitHandler(options, exitCode) { if (options.cleanup) { log.info(`Cleaning up...`); try { await fd.exit(); } catch (err) { log.error('Error cleanly exiting filmout_display (fd) executable', err); } } exitCode == 'SIGINT' ? log.info(`exit: ${exitCode}`) : log.error(`exit: ${exitCode}`, new Error(`Exited with non-zero code: "${exitCode}"`)); if (options.exit) process.exit(); } // do something when app is closing process.on('exit', exitHandler.bind(null, { cleanup: true })); // catches ctrl+c event process.on('SIGINT', exitHandler.bind(null, { exit: true })); // catches "kill pid" (for example: nodemon restart) process.on('SIGUSR1', exitHandler.bind(null, { exit: true })); process.on('SIGUSR2', exitHandler.bind(null, { exit: true })); // catches uncaught exceptions process.on('uncaughtException', exitHandler.bind(null, { exit: true })); //# sourceMappingURL=index.js.map