216 lines
6.4 KiB
TypeScript
216 lines
6.4 KiB
TypeScript
import 'dotenv/config'
|
|
import express from 'express';
|
|
import { Express, Request, Response, NextFunction } from 'express'
|
|
import fs from 'fs/promises';
|
|
import { tmpdir } from 'os';
|
|
import { join } from 'path';
|
|
import { Database } from 'sqlite3';
|
|
import bodyParser from 'body-parser';
|
|
import multer from 'multer';
|
|
import { v4 as uuid } from 'uuid';
|
|
import getType from 'mime';
|
|
import type { Logger } from 'winston';
|
|
import * as Handlebars from 'handlebars';
|
|
import { Server } from 'ws';
|
|
import type { WebSocket } from 'ws';
|
|
|
|
import { createLog } from './log'
|
|
import { sendMail } from './mail';
|
|
import { Files } from './files';
|
|
import type { SequenceObject, VideoObject } from './files';
|
|
import { Shell } from './shell';
|
|
import { delay } from './delay';
|
|
import { FD } from './fd';
|
|
import { FFMPEG } from './ffmpeg';
|
|
import { Camera } from './camera';
|
|
|
|
const log : Logger = createLog('fm');
|
|
const app : Express = express();
|
|
let wss : Server;
|
|
let fd : FD;
|
|
let ffmpeg : FFMPEG;
|
|
let camera : Camera;
|
|
let index : HandlebarsTemplateDelegate<any>;
|
|
|
|
let port : number;
|
|
let wsPort : number;
|
|
let sequences : string;
|
|
let videos : string;
|
|
let width : number;
|
|
let height : number;
|
|
|
|
log.info('Starting filmout_manager...');
|
|
|
|
app.use(bodyParser.json());
|
|
app.use(bodyParser.urlencoded({ extended: true }));
|
|
|
|
app.use('/static', express.static('./static'));
|
|
|
|
interface WebSocketExtended extends WebSocket {
|
|
ip? : string,
|
|
session? : string
|
|
}
|
|
|
|
interface Message {
|
|
cmd? : string;
|
|
}
|
|
|
|
async function createTemplate (filePath : string) : Promise<HandlebarsTemplateDelegate<any>> {
|
|
let tmpl : string;
|
|
try {
|
|
tmpl = await fs.readFile(filePath, 'utf8');
|
|
} catch (err) {
|
|
log.error(err);
|
|
return null
|
|
}
|
|
return Handlebars.compile(tmpl);
|
|
}
|
|
|
|
async function settings () {
|
|
let sequencesExists : boolean = false;
|
|
let videosExists : boolean = false;
|
|
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']}`);
|
|
}
|
|
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']}`);
|
|
}
|
|
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 {
|
|
width = parseInt(process.env['WIDTH']);
|
|
log.info(`WIDTH=${width}`);
|
|
}
|
|
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 {
|
|
height = parseInt(process.env['HEIGHT'])
|
|
log.info(`HEIGHT=${height}`);
|
|
}
|
|
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') {
|
|
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 (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}`);
|
|
}
|
|
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']);
|
|
log.info(`WS_PORT=${port}`);
|
|
}
|
|
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.exists(sequences);
|
|
if (!sequencesExists) {
|
|
log.error(`The SEQUENCES directory in .env, ${sequences}, does not exist`);
|
|
process.exit(8);
|
|
}
|
|
log.info(`SEQUENCES=${sequences}`);
|
|
}
|
|
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.exists(videos);
|
|
if (!sequencesExists) {
|
|
log.error(`The VIDEOS directory in .env, ${videos}, does not exist`);
|
|
process.exit(8);
|
|
}
|
|
log.info(`VIDEOS=${videos}`);
|
|
}
|
|
}
|
|
|
|
function onWssConnection (ws : WebSocketExtended, req : Request) {
|
|
let ip : string = req.headers['x-forwarded-for'] as string || 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 = uuid();
|
|
ws.on('message', function (data) { onClientMessage(data, ws) });
|
|
|
|
}
|
|
|
|
async function onClientMessage (data : any, ws : WebSocket) {
|
|
let msg : Message = null;
|
|
let res : Message = {};
|
|
try {
|
|
msg = JSON.parse(data);
|
|
} catch (err) {
|
|
log.error('Error parsing message', err);
|
|
}
|
|
if (msg !== null && typeof msg.cmd !== 'undefined') {
|
|
res = await cmd(msg.cmd);
|
|
}
|
|
ws.send(JSON.stringify(res));
|
|
}
|
|
|
|
async function cmd (id : string) : Promise<Message> {
|
|
switch(id) {
|
|
case 'open' :
|
|
await cameraOpen();
|
|
return { cmd : 'open' }
|
|
default :
|
|
log.warn(`No matching command: ${id}`);
|
|
}
|
|
}
|
|
|
|
async function cameraOpen () {
|
|
await camera.open();
|
|
}
|
|
|
|
app.get('/', async (req : Request, res : Response, next : NextFunction) => {
|
|
const sequencesArr : SequenceObject[] = await Files.enumerateSequences(sequences);
|
|
const videosArr : VideoObject[] = await Files.enumerateVideos(videos);
|
|
const html : string = index({ sequences : sequencesArr, videos : videosArr, width, height });
|
|
res.send(html);
|
|
});
|
|
|
|
async function main () {
|
|
await settings();
|
|
index = await createTemplate('./views/index.hbs');
|
|
ffmpeg = new FFMPEG(process.env['FFMPEG']);
|
|
camera = new Camera();
|
|
|
|
//fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']));
|
|
app.listen(port, async () => {
|
|
log.info(`filmout_manager HTTP server running on port ${port}`);
|
|
});
|
|
|
|
wss = new Server({ port : wsPort, clientTracking : true });
|
|
wss.on('connection', onWssConnection);
|
|
log.info(`filmout_manager WebSocket server running on port ${wsPort}`);
|
|
|
|
ffmpeg.listFormats();
|
|
|
|
|
|
}
|
|
|
|
|
|
main(); |