Create initial project. Can launch and kill filmout_display.

This commit is contained in:
mmcwilliams 2024-04-26 11:39:08 -06:00
commit 08f825e809
20 changed files with 5246 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

9
dist/delay/index.js vendored Normal file
View File

@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.delay = void 0;
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
exports.delay = delay;
module.exports = { delay };
//# sourceMappingURL=index.js.map

1
dist/delay/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/delay/index.ts"],"names":[],"mappings":";;;AAAA,SAAgB,KAAK,CAAE,EAAU;IAC7B,OAAO,IAAI,OAAO,CAAE,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAE,CAAC;AAC7D,CAAC;AAFD,sBAEC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC"}

24
dist/index.js vendored Normal file
View File

@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("dotenv/config");
const log_1 = require("./log");
const shell_1 = require("./shell");
const delay_1 = require("./delay");
let log = (0, log_1.createLog)('fm');
let logfd = (0, log_1.createLog)('fd');
log.info('Starting filmout_manager');
function logfdstd(data) {
logfd.info(data);
}
function logfdstderr(data) {
logfd.error(data);
}
async function main() {
let shell = new shell_1.Shell(['../filmout_display/build/bin/fd', '2560', '1600'], logfdstd, logfdstderr, null, true);
shell.execute();
await (0, delay_1.delay)(5000);
shell.kill();
process.exit();
}
main();
//# sourceMappingURL=index.js.map

1
dist/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yBAAsB;AAgBtB,+BAAiC;AAEjC,mCAAgC;AAChC,mCAAgC;AAEhC,IAAI,GAAG,GAAY,IAAA,eAAS,EAAC,IAAI,CAAC,CAAC;AACnC,IAAI,KAAK,GAAY,IAAA,eAAS,EAAC,IAAI,CAAC,CAAC;AAErC,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAErC,SAAS,QAAQ,CAAE,IAAU;IAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClB,CAAC;AACD,SAAS,WAAW,CAAE,IAAU;IAC/B,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,IAAI;IAClB,IAAI,KAAK,GAAW,IAAI,aAAK,CAAC,CAAE,iCAAiC,EAAE,MAAM,EAAE,MAAM,CAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACxH,KAAK,CAAC,OAAO,EAAE,CAAC;IAChB,MAAM,IAAA,aAAK,EAAC,IAAI,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,EAAE,CAAC;IACb,OAAO,CAAC,IAAI,EAAE,CAAC;AAChB,CAAC;AAED,IAAI,EAAE,CAAA"}

50
dist/log/index.js vendored Normal file
View File

@ -0,0 +1,50 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLog = void 0;
/** @module log */
/** Wrapper for winston that tags streams and optionally writes files with a simple interface. */
/** Module now also supports optional papertrail integration, other services to follow */
const winston_1 = require("winston");
const { SPLAT } = require('triple-beam');
const { isObject } = require('lodash');
const APP_NAME = process.env.APP_NAME || 'default';
let winstonPapertrail;
function formatObject(param) {
if (isObject(param)) {
return JSON.stringify(param);
}
return param;
}
const all = (0, winston_1.format)((info) => {
const splat = info[SPLAT] || [];
const message = formatObject(info.message);
const rest = splat.map(formatObject).join(' ');
info.message = `${message} ${rest}`;
return info;
});
const myFormat = winston_1.format.printf(({ level, message, label, timestamp }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
/**
* Returns a winston logger configured to service
*
* @param {string} label Label appearing on logger
* @param {string} filename Optional file to write log to
*
* @returns {object} Winston logger
*/
function createLog(label, filename = null) {
const tports = [new (winston_1.transports.Console)()];
const fmat = winston_1.format.combine(all(), winston_1.format.label({ label }), winston_1.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }), winston_1.format.colorize(), myFormat);
let papertrailOpts;
if (filename !== null) {
tports.push(new (winston_1.transports.File)({ filename }));
}
return (0, winston_1.createLogger)({
format: fmat,
transports: tports
});
}
exports.createLog = createLog;
module.exports = { createLog };
//# sourceMappingURL=index.js.map

1
dist/log/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/log/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;AAEZ,kBAAkB;AAClB,iGAAiG;AACjG,yFAAyF;AAEzF,qCAA2D;AAC3D,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvC,MAAM,QAAQ,GAAY,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,SAAS,CAAC;AAE5D,IAAI,iBAAiB,CAAC;AAEtB,SAAS,YAAY,CAAE,KAAW;IAChC,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,GAAG,GAAG,IAAA,gBAAM,EAAC,CAAC,IAAU,EAAE,EAAE;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,OAAO,GAAG,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;IACpC,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,gBAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAQ,EAAE,EAAE;IAC5E,OAAO,GAAG,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,OAAO,EAAE,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH;;;;;;;EAOE;AACF,SAAgB,SAAS,CAAE,KAAc,EAAE,WAAoB,IAAI;IAC/D,MAAM,MAAM,GAAW,CAAE,IAAI,CAAC,oBAAU,CAAC,OAAO,CAAC,EAAE,CAAE,CAAC;IACtD,MAAM,IAAI,GAAS,gBAAM,CAAC,OAAO,CAC7B,GAAG,EAAE,EACL,gBAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,EACvB,gBAAM,CAAC,SAAS,CAAC,EAAC,MAAM,EAAE,yBAAyB,EAAC,CAAC,EACrD,gBAAM,CAAC,QAAQ,EAAE,EACjB,QAAQ,CACX,CAAC;IACF,IAAI,cAAoB,CAAC;IAEzB,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAE,IAAI,CAAC,oBAAU,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAE,CAAC;IACvD,CAAC;IAED,OAAO,IAAA,sBAAY,EAAC;QAChB,MAAM,EAAG,IAAI;QACb,UAAU,EAAG,MAAM;KACtB,CAAC,CAAC;AACP,CAAC;AAnBD,8BAmBC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,SAAS,EAAE,CAAC"}

48
dist/mail/index.js vendored Normal file
View File

@ -0,0 +1,48 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendMail = void 0;
const nodemailer_1 = __importDefault(require("nodemailer"));
const log_1 = require("../log");
const from = process.env['MAIL_FROM'];
const smtp = process.env['MAIL_SMTP'];
const port = typeof process.env['MAIL_SMTP_PORT'] !== 'undefined' ? 25 : parseInt(process.env['MAIL_SMTP_PORT'], 10);
let transporter = null;
const log = (0, log_1.createLog)('mail');
if (typeof from !== 'undefined' && from !== null && from !== '' &&
typeof smtp !== 'undefined' && smtp !== null && smtp !== '') {
transporter = nodemailer_1.default.createTransport({
host: smtp,
port,
secure: false,
tls: { rejectUnauthorized: false }
});
}
async function sendMail(to, subject, body) {
if (transporter !== null) {
try {
await transporter.sendMail({
from,
to,
subject,
html: body
});
log.info(`Sent email "${subject}"`);
}
catch (err) {
log.error(`Error sending email "${subject}"`);
log.error(err);
return false;
}
}
else {
log.warn(`Email not configured, not sending "${subject}"`);
return false;
}
return true;
}
exports.sendMail = sendMail;
module.exports = { sendMail };
//# sourceMappingURL=index.js.map

1
dist/mail/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mail/index.ts"],"names":[],"mappings":";;;;;;AAAA,4DAAoC;AACpC,gCAAmC;AAGnC,MAAM,IAAI,GAAY,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAC/C,MAAM,IAAI,GAAY,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAC/C,MAAM,IAAI,GAAY,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC;AAE9H,IAAI,WAAW,GAA4B,IAAI,CAAC;AAEhD,MAAM,GAAG,GAAY,IAAA,eAAS,EAAC,MAAM,CAAC,CAAC;AAEvC,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE;IAC3D,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;IAC9D,WAAW,GAAG,oBAAU,CAAC,eAAe,CAAC;QACrC,IAAI,EAAE,IAAI;QACV,IAAI;QACJ,MAAM,EAAG,KAAK;QACd,GAAG,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE;KACrC,CAAC,CAAC;AACP,CAAC;AAEM,KAAK,UAAU,QAAQ,CAAE,EAAW,EAAE,OAAgB,EAAE,IAAa;IACxE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACD,MAAM,WAAW,CAAC,QAAQ,CAAC;gBACvB,IAAI;gBACJ,EAAE;gBACF,OAAO;gBACP,IAAI,EAAE,IAAI;aACb,CAAC,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,eAAe,OAAO,GAAG,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,GAAG,CAAC,KAAK,CAAC,wBAAwB,OAAO,GAAG,CAAC,CAAC;YAC9C,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACf,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,IAAI,CAAC,sCAAsC,OAAO,GAAG,CAAC,CAAC;QAC3D,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AApBD,4BAoBC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,QAAQ,EAAE,CAAC"}

66
dist/shell/index.js vendored Normal file
View File

@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Shell = void 0;
const child_process_1 = require("child_process");
const log_1 = require("../log");
const os_1 = require("os");
class Shell {
constructor(args, stdio = null, stderr = null, after = null, silent = false) {
this.lines = [];
this.stdio = null;
this.stderr = null;
this.after = null;
this.silent = false;
const bin = args.shift();
this.bin = bin;
this.args = args;
this.stdio = stdio;
this.stderr = stderr;
this.silent = silent;
this.after = after;
this.log = (0, log_1.createLog)(bin);
}
async execute() {
return new Promise((resolve, reject) => {
this.child = (0, child_process_1.spawn)(this.bin, this.args);
this.log.info(`Shell: ${this.bin} ${this.args.join(' ')}`);
this.child.stdout.on('data', (data) => {
if (!this.silent)
this.log.info(data.toString());
if (this.after !== null)
this.lines.push(data);
if (this.stdio !== null) {
this.stdio(data.toString());
}
});
this.child.stderr.on('data', (data) => {
if (!this.silent)
this.log.warn(data.toString());
if (this.stderr !== null) {
this.stderr(data.toString());
}
});
this.child.on('close', (code) => {
if (this.after !== null) {
this.after(this.lines.join(os_1.EOL));
}
if (code === 0) {
this.log.info(`Complete: ${this.bin} ${this.args.join(' ')}`);
return resolve(code);
}
else {
this.log.error(`Error executing: ${this.bin} ${this.args.join(' ')}`);
return reject(code);
}
});
});
}
kill() {
this.log.warn(`Killing: ${this.bin} ${this.args.join(' ')}`);
//this.child.stdin.pause();
this.child.kill();
}
}
exports.Shell = Shell;
module.exports = { Shell };
//# sourceMappingURL=index.js.map

1
dist/shell/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shell/index.ts"],"names":[],"mappings":"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"}

4762
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

36
package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "filmout_manager",
"version": "0.0.1",
"description": "Filmout manager is the control software for creating filmouts with the Galil and filmout display.",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"compile": "./node_modules/.bin/tsc -p tsconfig.json"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@types/express": "^4.17.21",
"@types/multer": "^1.4.11",
"@types/node": "^20.12.7",
"@types/nodemailer": "^6.4.14",
"@types/uuid": "^9.0.8",
"@types/ws": "^8.5.10",
"typescript": "^5.4.5"
},
"dependencies": {
"body-parser": "^1.20.2",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"galil": "github:artaic/galil",
"handlebars": "^4.7.8",
"lodash": "^4.17.21",
"multer": "^1.4.5-lts.1",
"nodemailer": "^6.9.13",
"sqlite3": "^5.1.7",
"triple-beam": "^1.4.1",
"uuid": "^9.0.1",
"winston": "^3.13.0",
"ws": "^8.16.0"
}
}

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

5
src/delay/index.ts Normal file
View File

@ -0,0 +1,5 @@
export function delay (ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}
module.exports = { delay };

42
src/index.ts Normal file
View File

@ -0,0 +1,42 @@
import 'dotenv/config'
import express from 'express';
import { Express, Request, Response, NextFunction } from 'express'
import fs from 'fs/promises';
import { createReadStream } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { createHash, Hash } from 'crypto';
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 { createLog } from './log'
import { sendMail } from './mail';
import { Shell } from './shell';
import { delay } from './delay';
let log : Logger = createLog('fm');
let logfd : Logger = createLog('fd');
log.info('Starting filmout_manager');
function logfdstd (data : any) {
logfd.info(data);
}
function logfdstderr (data : any) {
logfd.error(data);
}
async function main () {
let shell : Shell = new Shell([ '../filmout_display/build/bin/fd', '2560', '1600' ], logfdstd, logfdstderr, null, true);
shell.execute();
await delay(5000);
shell.kill();
process.exit();
}
main()

63
src/log/index.ts Normal file
View File

@ -0,0 +1,63 @@
'use strict'
/** @module log */
/** Wrapper for winston that tags streams and optionally writes files with a simple interface. */
/** Module now also supports optional papertrail integration, other services to follow */
import { format, transports, createLogger } from 'winston';
const { SPLAT } = require('triple-beam');
const { isObject } = require('lodash');
const APP_NAME : string = process.env.APP_NAME || 'default';
let winstonPapertrail;
function formatObject (param : any) {
if (isObject(param)) {
return JSON.stringify(param);
}
return param;
}
const all = format((info : any) => {
const splat = info[SPLAT] || [];
const message = formatObject(info.message);
const rest = splat.map(formatObject).join(' ');
info.message = `${message} ${rest}`;
return info;
});
const myFormat = format.printf(({ level, message, label, timestamp } : any) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
/**
* Returns a winston logger configured to service
*
* @param {string} label Label appearing on logger
* @param {string} filename Optional file to write log to
*
* @returns {object} Winston logger
*/
export function createLog (label : string, filename : string = null) {
const tports : any[] = [ new (transports.Console)() ];
const fmat : any = format.combine(
all(),
format.label({ label }),
format.timestamp({format: 'YYYY-MM-DD HH:mm:ss.SSS'}),
format.colorize(),
myFormat,
);
let papertrailOpts : any;
if (filename !== null) {
tports.push( new (transports.File)({ filename }) );
}
return createLogger({
format : fmat,
transports : tports
});
}
module.exports = { createLog };

45
src/mail/index.ts Normal file
View File

@ -0,0 +1,45 @@
import nodemailer from 'nodemailer';
import { createLog } from '../log';
import type { Logger } from 'winston';
const from : string = process.env['MAIL_FROM'];
const smtp : string = process.env['MAIL_SMTP'];
const port : number = typeof process.env['MAIL_SMTP_PORT'] !== 'undefined' ? 25 : parseInt(process.env['MAIL_SMTP_PORT'], 10);
let transporter : nodemailer.Transporter = null;
const log : Logger = createLog('mail');
if (typeof from !== 'undefined' && from !== null && from !== '' &&
typeof smtp !== 'undefined' && smtp !== null && smtp !== '') {
transporter = nodemailer.createTransport({
host: smtp,
port,
secure : false,
tls: { rejectUnauthorized: false }
});
}
export async function sendMail (to : string, subject : string, body : string) : Promise<boolean>{
if (transporter !== null) {
try {
await transporter.sendMail({
from,
to,
subject,
html: body
});
log.info(`Sent email "${subject}"`);
} catch (err) {
log.error(`Error sending email "${subject}"`);
log.error(err);
return false;
}
} else {
log.warn(`Email not configured, not sending "${subject}"`);
return false;
}
return true;
}
module.exports = { sendMail };

71
src/shell/index.ts Normal file
View File

@ -0,0 +1,71 @@
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
import { createLog } from '../log';
import type { Logger } from 'winston';
import { EOL } from 'os';
export class Shell {
private child : ChildProcessWithoutNullStreams;
private log : Logger;
private bin : string;
private args : any[];
private lines : string[] = [];
private stdio : Function = null;
private stderr : Function = null;
private after : Function = null;
private silent : boolean = false;
constructor (args : any[], stdio : Function = null, stderr : Function = null, after : Function = null, silent : boolean = false) {
const bin : string = args.shift();
this.bin = bin;
this.args = args;
this.stdio = stdio;
this.stderr = stderr;
this.silent = silent;
this.after = after;
this.log = createLog(bin);
}
public async execute () : Promise<number> {
return new Promise((resolve : Function, reject : Function) => {
this.child = spawn(this.bin, this.args);
this.log.info(`Shell: ${this.bin} ${this.args.join(' ')}`);
this.child.stdout.on('data', (data : string) => {
if (!this.silent) this.log.info(data.toString());
if (this.after !== null) this.lines.push(data);
if (this.stdio !== null) {
this.stdio(data.toString());
}
});
this.child.stderr.on('data', (data : string) => {
if (!this.silent) this.log.warn(data.toString());
if (this.stderr !== null) {
this.stderr(data.toString());
}
});
this.child.on('close', (code : number) => {
if (this.after !== null) {
this.after(this.lines.join(EOL));
}
if (code === 0) {
this.log.info(`Complete: ${this.bin} ${this.args.join(' ')}`);
return resolve(code);
} else {
this.log.error(`Error executing: ${this.bin} ${this.args.join(' ')}`);
return reject(code);
}
});
});
}
public kill () {
this.log.warn(`Killing: ${this.bin} ${this.args.join(' ')}`);
//this.child.stdin.pause();
this.child.kill();
}
}
module.exports = { Shell };

19
tsconfig.json Executable file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "ES2020",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"removeComments" : false,
"baseUrl" : "dist",
"outDir": "./dist/",
"rootDir" : "./src/",
"paths" : {
}
},
"exclude" : [
"./dist"
]
}