Check all local mounted harddrives for image sequence directories

This commit is contained in:
Matt McWilliams 2024-10-26 19:43:01 -04:00
parent 273252ceae
commit 5b03182b3d
11 changed files with 197 additions and 55 deletions

3
dist/env/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
export declare function envString(variable: string, defaultString: string): string;
export declare function envFloat(variable: string, defaultFloat: number): number;
export declare function envInt(variable: string, defaultInt: number): number;

17
dist/env/index.js vendored Normal file
View File

@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envInt = exports.envFloat = exports.envString = void 0;
function envString(variable, defaultString) {
return typeof process.env[variable] !== 'undefined' ? process.env[variable] : defaultString;
}
exports.envString = envString;
function envFloat(variable, defaultFloat) {
return typeof process.env[variable] !== 'undefined' ? parseFloat(process.env[variable]) : defaultFloat;
}
exports.envFloat = envFloat;
function envInt(variable, defaultInt) {
return typeof process.env[variable] !== 'undefined' ? parseInt(process.env[variable]) : defaultInt;
}
exports.envInt = envInt;
module.exports = { envString, envFloat, envInt };
//# sourceMappingURL=index.js.map

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

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":";;;AAAA,SAAgB,SAAS,CAAE,QAAiB,EAAE,aAAsB;IACnE,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;AAC7F,CAAC;AAFD,8BAEC;AAED,SAAgB,QAAQ,CAAE,QAAiB,EAAE,YAAqB;IACjE,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AACxG,CAAC;AAFD,4BAEC;AAED,SAAgB,MAAM,CAAE,QAAiB,EAAE,UAAmB;IAC7D,OAAO,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AACpG,CAAC;AAFD,wBAEC;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC"}

View File

@ -1,9 +1,11 @@
interface SequenceObject {
created: number;
path: string;
hash: string;
name: string;
}
interface VideoObject {
created: number;
path: string;
hash: string;
name: string;
@ -17,6 +19,7 @@ export declare class Files {
private static sequencesDir;
static init(sequencesDir: string): Promise<boolean>;
static exists(path: string): Promise<boolean>;
static enumerateSequenceDirs(): Promise<string[]>;
static enumerateSequences(): Promise<SequenceObject[]>;
static isSequence(dirPath: string): Promise<boolean>;
static enumerateVideos(path: string): Promise<VideoObject[]>;

63
dist/files/index.js vendored
View File

@ -4,6 +4,8 @@ exports.Files = void 0;
const promises_1 = require("fs/promises");
const hash_1 = require("../hash");
const path_1 = require("path");
const env_1 = require("../env");
const os_1 = require("os");
const videoExtensions = [
'.mp4',
'.mkv',
@ -31,20 +33,61 @@ class Files {
return false;
}
}
static async enumerateSequenceDirs() {
const sequencesDirs = [
await (0, promises_1.realpath)(this.sequencesDir)
];
let drivesDir = null;
let drives = [];
let drive;
let user;
let stats;
if ((0, os_1.platform)() === 'darwin') {
drivesDir = `/Volumes/`;
}
else if ((0, os_1.platform)() === 'linux') {
user = (0, env_1.envString)('USER', null);
if (user !== null) {
drivesDir = (0, path_1.join)('/media', user);
}
}
if (drivesDir !== null) {
try {
drives = await (0, promises_1.readdir)(drivesDir);
}
catch (err) {
//
}
}
for (let drive of drives) {
if (drive.substring(0, 1) === '.') {
continue;
}
try {
drive = await (0, promises_1.realpath)((0, path_1.join)(drivesDir, drive));
stats = await (0, promises_1.lstat)(drive);
}
catch (err) {
continue;
}
if (stats.isDirectory()) {
sequencesDirs.push(drive);
}
}
return sequencesDirs;
}
static async enumerateSequences() {
const dirs = [];
let all;
let stats;
let dirPath;
const paths = [
await (0, promises_1.realpath)(this.sequencesDir)
];
const paths = await this.enumerateSequenceDirs();
for (let path of paths) {
try {
all = await (0, promises_1.readdir)(path);
}
catch (err) {
throw err;
//
}
for (let elem of all) {
if (elem.substring(0, 1) === '.') {
@ -60,16 +103,21 @@ class Files {
if (!stats.isDirectory()) {
continue;
}
if (!this.isSequence(dirPath)) {
if (!await this.isSequence(dirPath)) {
continue;
}
console.log(`Adding ${dirPath}`);
dirs.push({
created: +stats.birthtime,
path: dirPath,
hash: hash_1.Hashes.stringHash(dirPath),
name: (0, path_1.basename)(dirPath)
});
}
}
dirs.sort((a, b) => {
return b.created - a.created;
});
return dirs;
}
static async isSequence(dirPath) {
@ -83,6 +131,7 @@ class Files {
catch (err) {
//
}
console.dir(`${dirPath} ${all.length}`);
for (let elem of all) {
filePath = (0, path_1.join)(dirPath, elem);
try {
@ -91,7 +140,8 @@ class Files {
catch (err) {
//
}
if (stats.isFile() && imageExtensions.indexOf((0, path_1.extname)((0, path_1.basename)(elem).toLowerCase())) !== -1) {
if (stats.isFile() && elem.substring(0, 1) !== '.' && imageExtensions.indexOf((0, path_1.extname)((0, path_1.basename)(elem).toLowerCase())) !== -1) {
console.log(`Has file ${filePath}`);
sequence = true;
break;
}
@ -116,6 +166,7 @@ class Files {
stats = await (0, promises_1.lstat)(filePath);
if (stats.isFile() && videoExtensions.indexOf((0, path_1.extname)((0, path_1.basename)(elem).toLowerCase())) !== -1) {
videos.push({
created: +stats.birthtime,
path: filePath,
hash: hash_1.Hashes.stringHash(filePath),
name: (0, path_1.basename)(filePath)

File diff suppressed because one or more lines are too long

41
dist/index.js vendored
View File

@ -34,6 +34,7 @@ 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");
@ -81,76 +82,76 @@ async function createTemplate(filePath) {
async function settings() {
let sequencesExists = false;
let videosExists = false;
if (typeof process.env['FD'] === 'undefined') {
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 (typeof process.env['FFMPEG'] === 'undefined') {
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 (typeof process.env['WIDTH'] === 'undefined') {
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 = parseInt(process.env['WIDTH']);
width = (0, env_1.envInt)('WIDTH', 0);
log.info(`WIDTH=${width}`);
}
if (typeof process.env['HEIGHT'] === 'undefined') {
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 = parseInt(process.env['HEIGHT']);
height = (0, env_1.envInt)('HEIGHT', 0);
log.info(`HEIGHT=${height}`);
}
if (typeof process.env['FD_HOST'] === 'undefined') {
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 (typeof process.env['FD_PORT'] === 'undefined') {
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 (typeof process.env['PORT'] === 'undefined') {
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 = parseInt(process.env['PORT']);
port = (0, env_1.envInt)('PORT', 8080);
log.info(`PORT=${port}`);
}
if (typeof process.env['WS_PORT'] === 'undefined') {
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 = parseInt(process.env['WS_PORT']);
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 (typeof process.env['SEQUENCES'] === 'undefined') {
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 = process.env['SEQUENCES'];
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`);
@ -158,12 +159,12 @@ async function settings() {
}
log.info(`SEQUENCES=${sequences}`);
}
if (typeof process.env['VIDEOS'] === 'undefined') {
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 = process.env['VIDEOS'];
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`);
@ -171,8 +172,8 @@ async function settings() {
}
log.info(`VIDEOS=${videos}`);
}
if (typeof process.env['MOCK'] !== 'undefined') {
if (process.env['MOCK'].trim().toLowerCase() === "true" || process.env['MOCK'].trim() === '1') {
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`);
}
@ -461,12 +462,12 @@ app.get('/:width/:height/image.jpg', async (req, res, next) => {
async function main() {
await settings();
index = await createTemplate('./views/index.hbs');
ffmpeg = new ffmpeg_1.FFMPEG(process.env['FFMPEG']);
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(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), mock);
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), mock);
app.listen(port, async () => {
log.info(`filmout_manager HTTP server running on port ${port}`);
});

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

13
src/env/index.ts vendored Normal file
View File

@ -0,0 +1,13 @@
export function envString (variable : string, defaultString : string) : string {
return typeof process.env[variable] !== 'undefined' ? process.env[variable] : defaultString;
}
export function envFloat (variable : string, defaultFloat : number ) : number {
return typeof process.env[variable] !== 'undefined' ? parseFloat(process.env[variable]) : defaultFloat;
}
export function envInt (variable : string, defaultInt : number ) : number {
return typeof process.env[variable] !== 'undefined' ? parseInt(process.env[variable]) : defaultInt;
}
module.exports = { envString, envFloat, envInt };

View File

@ -3,14 +3,17 @@ import type { Stats } from 'fs';
import { Hashes } from '../hash';
import { basename, extname, join } from 'path';
import { envString } from '../env';
import { platform } from 'os';
interface SequenceObject {
created : number,
path : string,
hash : string,
name : string
}
interface VideoObject {
created : number,
path : string,
hash : string,
name : string
@ -54,21 +57,62 @@ export class Files {
}
}
public static async enumerateSequenceDirs () : Promise<string[]> {
const sequencesDirs : string[] = [
await realpath(this.sequencesDir)
];
let drivesDir : string = null;
let drives : string[] = [];
let drive : string;
let user : string;
let stats : Stats;
if (platform() === 'darwin') {
drivesDir = `/Volumes/`;
} else if (platform() === 'linux') {
user = envString('USER', null);
if (user !== null) {
drivesDir = join('/media', user);
}
}
if (drivesDir !== null) {
try {
drives = await readdir(drivesDir);
} catch (err) {
//
}
}
for (let drive of drives) {
if (drive.substring(0, 1) === '.') {
continue;
}
try {
drive = await realpath(join(drivesDir, drive));
stats = await lstat(drive);
} catch (err) {
continue;
}
if (stats.isDirectory()) {
sequencesDirs.push(drive);
}
}
return sequencesDirs;
}
public static async enumerateSequences () : Promise<SequenceObject[]> {
const dirs : SequenceObject[] = [];
let all : string[];
let stats : Stats;
let dirPath : string;
const paths : string[] = [
await realpath(this.sequencesDir)
];
const paths : string[] = await this.enumerateSequenceDirs();
for (let path of paths) {
try {
all = await readdir(path)
} catch (err) {
throw err;
//
}
for (let elem of all) {
@ -84,10 +128,11 @@ export class Files {
if (!stats.isDirectory()) {
continue;
}
if (!this.isSequence(dirPath)) {
if (!await this.isSequence(dirPath)) {
continue;
}
dirs.push({
created : + stats.birthtime,
path : dirPath,
hash : Hashes.stringHash(dirPath),
name : basename(dirPath)
@ -95,6 +140,10 @@ export class Files {
}
}
dirs.sort((a, b) => {
return b.created - a.created;
})
return dirs;
}
@ -103,11 +152,13 @@ export class Files {
let all : string[];
let stats : Stats;
let filePath : string;
try {
all = await readdir(dirPath);
} catch (err) {
//
}
for (let elem of all) {
filePath = join(dirPath, elem);
try {
@ -115,7 +166,7 @@ export class Files {
} catch (err) {
//
}
if (stats.isFile() && imageExtensions.indexOf(extname(basename(elem).toLowerCase())) !== -1) {
if (stats.isFile() && elem.substring(0, 1) !== '.' && imageExtensions.indexOf(extname(basename(elem).toLowerCase())) !== -1) {
sequence = true;
break;
}
@ -143,6 +194,7 @@ export class Files {
stats = await lstat(filePath);
if (stats.isFile() && videoExtensions.indexOf(extname(basename(elem).toLowerCase())) !== -1) {
videos.push({
created : + stats.birthtime,
path : filePath,
hash : Hashes.stringHash(filePath),
name : basename(filePath)

View File

@ -14,7 +14,8 @@ import * as Handlebars from 'handlebars';
import { Server } from 'ws';
import type { WebSocket } from 'ws';
import { createLog } from './log'
import { createLog } from './log';
import { envString, envInt } from './env';
import { sendMail } from './mail';
import { Files } from './files';
import type { SequenceObject, VideoObject, ImageObject } from './files';
@ -78,56 +79,56 @@ async function createTemplate (filePath : string) : Promise<HandlebarsTemplateDe
async function settings () {
let sequencesExists : boolean = false;
let videosExists : boolean = false;
if (typeof process.env['FD'] === 'undefined') {
if (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 (typeof process.env['FFMPEG'] === 'undefined') {
if (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 (typeof process.env['WIDTH'] === 'undefined') {
if (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 = parseInt(process.env['WIDTH']);
width = envInt('WIDTH', 0);
log.info(`WIDTH=${width}`);
}
if (typeof process.env['HEIGHT'] === 'undefined') {
if (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 = parseInt(process.env['HEIGHT'])
height = envInt('HEIGHT', 0);
log.info(`HEIGHT=${height}`);
}
if (typeof process.env['FD_HOST'] === 'undefined') {
if (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 (typeof process.env['FD_PORT'] === 'undefined') {
if (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 (typeof process.env['PORT'] === 'undefined') {
if (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 = parseInt(process.env['PORT']);
port = envInt('PORT', 8080);
log.info(`PORT=${port}`);
}
if (typeof process.env['WS_PORT'] === 'undefined') {
if (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 = parseInt(process.env['WS_PORT']);
wsPort = envInt('WS_PORT', 8081);
log.info(`WS_PORT=${wsPort}`);
}
if (wsPort === port) {
@ -135,11 +136,11 @@ async function settings () {
process.exit(7);
}
if (typeof process.env['SEQUENCES'] === 'undefined') {
if (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 = process.env['SEQUENCES'];
sequences = envString('SEQUENCES', null);
sequencesExists = await Files.init(sequences);
if (!sequencesExists) {
log.error(`The SEQUENCES directory in .env, ${sequences}, does not exist`);
@ -147,11 +148,11 @@ async function settings () {
}
log.info(`SEQUENCES=${sequences}`);
}
if (typeof process.env['VIDEOS'] === 'undefined') {
if (envString('VIDEOS', null) === null) {
log.error('Please include a VIDEOS directory where the videos will be located in .env');
process.exit(7);
} else {
videos = process.env['VIDEOS'];
videos = envString('VIDEOS', null);
videosExists = await Files.exists(videos);
if (!sequencesExists) {
log.error(`The VIDEOS directory in .env, ${videos}, does not exist`);
@ -159,8 +160,8 @@ async function settings () {
}
log.info(`VIDEOS=${videos}`);
}
if (typeof process.env['MOCK'] !== 'undefined') {
if (process.env['MOCK'].trim().toLowerCase() === "true" || process.env['MOCK'].trim() === '1') {
if (envString('MOCK', null) !== null) {
if (envString('MOCK', '').trim().toLowerCase() === "true" || envString('MOCK', '').trim() === '1') {
mock = true;
log.info(`MOCK=true`);
} else {
@ -461,12 +462,12 @@ app.get('/:width/:height/image.jpg', async (req : Request, res : Response, next
async function main () {
await settings();
index = await createTemplate('./views/index.hbs');
ffmpeg = new FFMPEG(process.env['FFMPEG']);
ffmpeg = new FFMPEG(envString('FFMPEG', 'ffmpeg'));
ffprobe = new FFPROBE();
image = new Image();
camera = new Camera(mock);
display = new Display(width, height);
fd = new FD(process.env['FD'], width, height, process.env['FD_HOST'], parseInt(process.env['FD_PORT']), mock);
fd = new FD(envString('FD', 'fd'), width, height, envString('FD_HOST', 'localhost'), envInt('FD_PORT', 8082), mock);
app.listen(port, async () => {
log.info(`filmout_manager HTTP server running on port ${port}`);