Filmout supports still images. Uses FFMPEG to convert to png, still. Should use sharp and re-sample the image to the maximum allowed for the screen in the smallest dimension.

This commit is contained in:
mmcwilliams 2019-07-07 00:02:01 -04:00
parent 0f52812767
commit b5da7ba233
10 changed files with 501 additions and 101 deletions

View File

@ -1,5 +1,19 @@
'use strict'; 'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const sharp_1 = __importDefault(require("sharp"));
const animated = __importStar(require("animated-gif-detector"));
const path_1 = require("path");
const fs_extra_1 = require("fs-extra");
const delay_1 = require("delay"); const delay_1 = require("delay");
class FilmOut { class FilmOut {
/** /**
@ -7,9 +21,13 @@ class FilmOut {
**/ **/
constructor(display, ffmpeg, ffprobe, ui, light) { constructor(display, ffmpeg, ffprobe, ui, light) {
this.id = 'filmout'; this.id = 'filmout';
this.videoExtensions = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4'];
this.stillExtensions = ['.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
this.gifExtension = '.gif';
this.state = { this.state = {
frame: 0, frame: 0,
frames: 0, frames: 0,
still: false,
path: null, path: null,
fileName: null, fileName: null,
info: {}, info: {},
@ -56,6 +74,9 @@ class FilmOut {
**/ **/
async move() { async move() {
let start = +new Date(); let start = +new Date();
if (this.state.still) {
return false;
}
if (this.state.dir) { if (this.state.dir) {
this.state.frame++; this.state.frame++;
} }
@ -89,8 +110,8 @@ class FilmOut {
await delay_1.delay(20); await delay_1.delay(20);
} }
/** /**
* *
**/ **/
async end() { async end() {
await delay_1.delay(20); await delay_1.delay(20);
this.display.hide(); this.display.hide();
@ -100,26 +121,67 @@ class FilmOut {
* *
**/ **/
async onConnect(evt, arg) { async onConnect(evt, arg) {
let info;
let frames = 0; let frames = 0;
try { let isAnimated = false;
info = await this.ffprobe.info(arg.path); let info;
let ext;
ext = path_1.extname(arg.fileName.toLowerCase());
console.dir(arg);
console.log(ext);
if (ext === this.gifExtension) {
try {
isAnimated = await this.isGifAnimated(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
await this.ui.send(this.id, { valid: false });
return false;
}
this.state.still = !isAnimated;
} }
catch (err) { else if (this.stillExtensions.indexOf(ext) !== -1) {
//this.log.error(err, 'FILMOUT', true, true); this.state.still = true;
this.state.enabled = false; }
await this.ui.send(this.id, { valid: false }); else if (this.videoExtensions.indexOf(ext) !== -1) {
this.state.still = false;
}
else {
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
return false; return false;
} }
try { if (this.state.still) {
frames = await this.ffprobe.frames(arg.path); try {
info = await this.stillInfo(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
frames = 1;
} }
catch (err) { else {
this.log.error(err, 'FILMOUT', true, true); try {
this.state.enabled = false; info = await this.ffprobe.info(arg.path);
await this.ui.send(this.id, { valid: false }); }
return false; catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
try {
frames = await this.ffprobe.frames(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
} }
console.dir(info);
this.state.frame = 0; this.state.frame = 0;
this.state.path = arg.path; this.state.path = arg.path;
this.state.fileName = arg.fileName; this.state.fileName = arg.fileName;
@ -130,6 +192,29 @@ class FilmOut {
this.state.enabled = true; this.state.enabled = true;
return await this.ui.send(this.id, { valid: true, state: JSON.stringify(this.state) }); return await this.ui.send(this.id, { valid: true, state: JSON.stringify(this.state) });
} }
/**
* Return true if gif is animated, false if it is a still
**/
async isGifAnimated(pathStr) {
let gifBuffer;
try {
gifBuffer = await fs_extra_1.readFile(pathStr);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
return false;
}
return animated(gifBuffer);
}
/**
* Return information on a still image using the sharp module
**/
async stillInfo(pathStr) {
return sharp_1.default(pathStr).metadata();
}
/**
*
**/
async previewFrame(evt, arg) { async previewFrame(evt, arg) {
const state = JSON.parse(JSON.stringify(this.state)); const state = JSON.parse(JSON.stringify(this.state));
let path; let path;
@ -144,6 +229,9 @@ class FilmOut {
} }
this.ui.send('preview_frame', { path, frame: arg.frame }); this.ui.send('preview_frame', { path, frame: arg.frame });
} }
/**
*
**/
async preview(evt, arg) { async preview(evt, arg) {
const state = JSON.parse(JSON.stringify(this.state)); const state = JSON.parse(JSON.stringify(this.state));
let path; let path;
@ -164,6 +252,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async focus(evt, arg) { async focus(evt, arg) {
this.log.info(`Showing focus screen`); this.log.info(`Showing focus screen`);
try { try {
@ -174,6 +265,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async field(evt, arg) { async field(evt, arg) {
this.log.info(`Showing field guide screen`); this.log.info(`Showing field guide screen`);
try { try {
@ -184,6 +278,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async meter(evt, arg) { async meter(evt, arg) {
this.log.info(`Showing meter screen`); this.log.info(`Showing meter screen`);
try { try {
@ -194,6 +291,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async close(evt, arg) { async close(evt, arg) {
try { try {
await this.display.hide(); await this.display.hide();
@ -203,6 +303,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
onDisplay(evt, arg) { onDisplay(evt, arg) {
this.display.change(arg.display); this.display.change(arg.display);
this.log.info(`Changing the display to ${arg.display}`); this.log.info(`Changing the display to ${arg.display}`);

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,8 @@ let filmout;
class FilmOut { class FilmOut {
constructor() { constructor() {
this.id = 'filmout'; this.id = 'filmout';
this.extensions = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4', '.gif',
'.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
this.displays = []; this.displays = [];
this.state = { this.state = {
frame: 0, frame: 0,
@ -99,13 +101,12 @@ class FilmOut {
$('#filmout_stats_monitor_size').text(`${display.width} x ${display.height}`); $('#filmout_stats_monitor_size').text(`${display.width} x ${display.height}`);
$('#filmout_stats_monitor_aspect').text(`${aspect}`); $('#filmout_stats_monitor_aspect').text(`${aspect}`);
$('#filmout_stats_monitor_scale').text(`${parseFloat(display.scale).toFixed(1)} scale factor`); $('#filmout_stats_monitor_scale').text(`${parseFloat(display.scale).toFixed(1)} scale factor`);
console.dir(display); //console.dir(display);
this.state.display = id; this.state.display = id;
ipcRenderer.send('display', { display: id }); ipcRenderer.send('display', { display: id });
} }
selectFile() { selectFile() {
const elem = $('#digital'); const elem = $('#digital');
const extensions = ['mpg', 'mpeg', 'mov', 'mkv', 'avi', 'mp4'];
dialog.showOpenDialog({ dialog.showOpenDialog({
title: `Select video or image sequence`, title: `Select video or image sequence`,
properties: [`openFile`], properties: [`openFile`],
@ -120,19 +121,18 @@ class FilmOut {
if (!files) if (!files)
return false; return false;
let valid = false; let valid = false;
let path = files[0]; let pathStr = files[0];
let displayName; let displayName;
if (path && path !== '') { let ext;
for (let ext of extensions) { if (pathStr && pathStr !== '') {
if (path.toLowerCase().indexOf(`.${ext}`) !== -1) { ext = path.extname(pathStr.toLowerCase());
valid = true; valid = this.extensions.indexOf(ext) === -1 ? false : true;
} if (!valid) {
}
if (!valid)
return false; return false;
log.info(`Selected video ${path.split('/').pop()}`, 'DIGITAL', true); }
elem.attr('data-file', path); log.info(`Selected video ${pathStr.split('/').pop()}`, 'DIGITAL', true);
displayName = path.split('/').pop(); elem.attr('data-file', pathStr);
displayName = pathStr.split('/').pop();
elem.val(displayName); elem.val(displayName);
$('#filmout_file').val(displayName); $('#filmout_file').val(displayName);
} }

View File

@ -6,6 +6,7 @@ const notifier = require('node-notifier');
const fs = require('fs'); const fs = require('fs');
const uuid = require('uuid').v4; const uuid = require('uuid').v4;
const moment = require('moment'); const moment = require('moment');
const path = require('path');
const humanizeDuration = require('humanize-duration'); const humanizeDuration = require('humanize-duration');
const PACKAGE = require('./package.json'); const PACKAGE = require('./package.json');
const cfg = require('./data/cfg.json'); const cfg = require('./data/cfg.json');

View File

@ -13,8 +13,12 @@ declare var proj : any;
declare var grid : any; declare var grid : any;
declare var dialog : any; declare var dialog : any;
declare var ipcRenderer : any; declare var ipcRenderer : any;
declare var path : any;
function gcd (a : number, b : number) { /**
* Determine the greatest common denominator
*/
function gcd (a : number, b : number) : any {
if (b === 0) return a; if (b === 0) return a;
return gcd(b, a % b); return gcd(b, a % b);
} }
@ -49,6 +53,9 @@ let filmout : FilmOut;
class FilmOut { class FilmOut {
private id : string = 'filmout'; private id : string = 'filmout';
private extensions : string[] = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4',
'.gif',
'.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
private displays : any[] = []; private displays : any[] = [];
private state : any = { private state : any = {
frame : 0, frame : 0,
@ -100,6 +107,7 @@ class FilmOut {
const w : number = display.width / scale; const w : number = display.width / scale;
const elem : any = $('#filmout_monitor'); const elem : any = $('#filmout_monitor');
const aspect : any = reduceRatio(display.width, display.height); const aspect : any = reduceRatio(display.width, display.height);
let h : number; let h : number;
let top : number; let top : number;
@ -120,13 +128,12 @@ class FilmOut {
$('#filmout_stats_monitor_size').text(`${display.width} x ${display.height}`); $('#filmout_stats_monitor_size').text(`${display.width} x ${display.height}`);
$('#filmout_stats_monitor_aspect').text(`${aspect}`); $('#filmout_stats_monitor_aspect').text(`${aspect}`);
$('#filmout_stats_monitor_scale').text(`${parseFloat(display.scale).toFixed(1)} scale factor`); $('#filmout_stats_monitor_scale').text(`${parseFloat(display.scale).toFixed(1)} scale factor`);
console.dir(display); //console.dir(display);
this.state.display = id; this.state.display = id;
ipcRenderer.send('display', { display : id }); ipcRenderer.send('display', { display : id });
} }
selectFile () { selectFile () {
const elem : any = $('#digital'); const elem : any = $('#digital');
const extensions : string[] = ['mpg', 'mpeg', 'mov', 'mkv', 'avi', 'mp4'];
dialog.showOpenDialog({ dialog.showOpenDialog({
title : `Select video or image sequence`, title : `Select video or image sequence`,
properties : [`openFile`], // openDirectory, multiSelection, openFile properties : [`openFile`], // openDirectory, multiSelection, openFile
@ -140,18 +147,18 @@ class FilmOut {
}, (files : string[]) => { }, (files : string[]) => {
if (!files) return false; if (!files) return false;
let valid : boolean = false; let valid : boolean = false;
let path : string = files[0]; let pathStr : string = files[0];
let displayName : string; let displayName : string;
if (path && path !== '') { let ext : string;
for (let ext of extensions) { if (pathStr && pathStr !== '') {
if (path.toLowerCase().indexOf(`.${ext}`) !== -1) { ext = path.extname(pathStr.toLowerCase());
valid = true; valid = this.extensions.indexOf(ext) === -1 ? false : true;
} if (!valid) {
return false;
} }
if (!valid) return false; log.info(`Selected video ${pathStr.split('/').pop()}`, 'DIGITAL', true);
log.info(`Selected video ${path.split('/').pop()}`, 'DIGITAL', true); elem.attr('data-file', pathStr);
elem.attr('data-file', path); displayName = pathStr.split('/').pop();
displayName = path.split('/').pop();
elem.val(displayName); elem.val(displayName);
$('#filmout_file').val(displayName); $('#filmout_file').val(displayName);
} }

View File

@ -1,5 +1,19 @@
'use strict'; 'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const sharp_1 = __importDefault(require("sharp"));
const animated = __importStar(require("animated-gif-detector"));
const path_1 = require("path");
const fs_extra_1 = require("fs-extra");
const delay_1 = require("delay"); const delay_1 = require("delay");
class FilmOut { class FilmOut {
/** /**
@ -7,9 +21,13 @@ class FilmOut {
**/ **/
constructor(display, ffmpeg, ffprobe, ui, light) { constructor(display, ffmpeg, ffprobe, ui, light) {
this.id = 'filmout'; this.id = 'filmout';
this.videoExtensions = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4'];
this.stillExtensions = ['.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
this.gifExtension = '.gif';
this.state = { this.state = {
frame: 0, frame: 0,
frames: 0, frames: 0,
still: false,
path: null, path: null,
fileName: null, fileName: null,
info: {}, info: {},
@ -56,6 +74,9 @@ class FilmOut {
**/ **/
async move() { async move() {
let start = +new Date(); let start = +new Date();
if (this.state.still) {
return false;
}
if (this.state.dir) { if (this.state.dir) {
this.state.frame++; this.state.frame++;
} }
@ -89,8 +110,8 @@ class FilmOut {
await delay_1.delay(20); await delay_1.delay(20);
} }
/** /**
* *
**/ **/
async end() { async end() {
await delay_1.delay(20); await delay_1.delay(20);
this.display.hide(); this.display.hide();
@ -100,26 +121,67 @@ class FilmOut {
* *
**/ **/
async onConnect(evt, arg) { async onConnect(evt, arg) {
let info;
let frames = 0; let frames = 0;
try { let isAnimated = false;
info = await this.ffprobe.info(arg.path); let info;
let ext;
ext = path_1.extname(arg.fileName.toLowerCase());
console.dir(arg);
console.log(ext);
if (ext === this.gifExtension) {
try {
isAnimated = await this.isGifAnimated(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
await this.ui.send(this.id, { valid: false });
return false;
}
this.state.still = !isAnimated;
} }
catch (err) { else if (this.stillExtensions.indexOf(ext) !== -1) {
//this.log.error(err, 'FILMOUT', true, true); this.state.still = true;
this.state.enabled = false; }
await this.ui.send(this.id, { valid: false }); else if (this.videoExtensions.indexOf(ext) !== -1) {
this.state.still = false;
}
else {
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
return false; return false;
} }
try { if (this.state.still) {
frames = await this.ffprobe.frames(arg.path); try {
info = await this.stillInfo(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
frames = 1;
} }
catch (err) { else {
this.log.error(err, 'FILMOUT', true, true); try {
this.state.enabled = false; info = await this.ffprobe.info(arg.path);
await this.ui.send(this.id, { valid: false }); }
return false; catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
try {
frames = await this.ffprobe.frames(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
} }
console.dir(info);
this.state.frame = 0; this.state.frame = 0;
this.state.path = arg.path; this.state.path = arg.path;
this.state.fileName = arg.fileName; this.state.fileName = arg.fileName;
@ -130,6 +192,29 @@ class FilmOut {
this.state.enabled = true; this.state.enabled = true;
return await this.ui.send(this.id, { valid: true, state: JSON.stringify(this.state) }); return await this.ui.send(this.id, { valid: true, state: JSON.stringify(this.state) });
} }
/**
* Return true if gif is animated, false if it is a still
**/
async isGifAnimated(pathStr) {
let gifBuffer;
try {
gifBuffer = await fs_extra_1.readFile(pathStr);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
return false;
}
return animated(gifBuffer);
}
/**
* Return information on a still image using the sharp module
**/
async stillInfo(pathStr) {
return sharp_1.default(pathStr).metadata();
}
/**
*
**/
async previewFrame(evt, arg) { async previewFrame(evt, arg) {
const state = JSON.parse(JSON.stringify(this.state)); const state = JSON.parse(JSON.stringify(this.state));
let path; let path;
@ -144,6 +229,9 @@ class FilmOut {
} }
this.ui.send('preview_frame', { path, frame: arg.frame }); this.ui.send('preview_frame', { path, frame: arg.frame });
} }
/**
*
**/
async preview(evt, arg) { async preview(evt, arg) {
const state = JSON.parse(JSON.stringify(this.state)); const state = JSON.parse(JSON.stringify(this.state));
let path; let path;
@ -164,6 +252,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async focus(evt, arg) { async focus(evt, arg) {
this.log.info(`Showing focus screen`); this.log.info(`Showing focus screen`);
try { try {
@ -174,6 +265,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async field(evt, arg) { async field(evt, arg) {
this.log.info(`Showing field guide screen`); this.log.info(`Showing field guide screen`);
try { try {
@ -184,6 +278,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async meter(evt, arg) { async meter(evt, arg) {
this.log.info(`Showing meter screen`); this.log.info(`Showing meter screen`);
try { try {
@ -194,6 +291,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async close(evt, arg) { async close(evt, arg) {
try { try {
await this.display.hide(); await this.display.hide();
@ -203,6 +303,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
onDisplay(evt, arg) { onDisplay(evt, arg) {
this.display.change(arg.display); this.display.change(arg.display);
this.log.info(`Changing the display to ${arg.display}`); this.log.info(`Changing the display to ${arg.display}`);

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,19 @@
'use strict'; 'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const sharp_1 = __importDefault(require("sharp"));
const animated = __importStar(require("animated-gif-detector"));
const path_1 = require("path");
const fs_extra_1 = require("fs-extra");
const delay_1 = require("delay"); const delay_1 = require("delay");
class FilmOut { class FilmOut {
/** /**
@ -7,9 +21,13 @@ class FilmOut {
**/ **/
constructor(display, ffmpeg, ffprobe, ui, light) { constructor(display, ffmpeg, ffprobe, ui, light) {
this.id = 'filmout'; this.id = 'filmout';
this.videoExtensions = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4'];
this.stillExtensions = ['.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
this.gifExtension = '.gif';
this.state = { this.state = {
frame: 0, frame: 0,
frames: 0, frames: 0,
still: false,
path: null, path: null,
fileName: null, fileName: null,
info: {}, info: {},
@ -56,6 +74,9 @@ class FilmOut {
**/ **/
async move() { async move() {
let start = +new Date(); let start = +new Date();
if (this.state.still) {
return false;
}
if (this.state.dir) { if (this.state.dir) {
this.state.frame++; this.state.frame++;
} }
@ -89,8 +110,8 @@ class FilmOut {
await delay_1.delay(20); await delay_1.delay(20);
} }
/** /**
* *
**/ **/
async end() { async end() {
await delay_1.delay(20); await delay_1.delay(20);
this.display.hide(); this.display.hide();
@ -100,26 +121,67 @@ class FilmOut {
* *
**/ **/
async onConnect(evt, arg) { async onConnect(evt, arg) {
let info;
let frames = 0; let frames = 0;
try { let isAnimated = false;
info = await this.ffprobe.info(arg.path); let info;
let ext;
ext = path_1.extname(arg.fileName.toLowerCase());
console.dir(arg);
console.log(ext);
if (ext === this.gifExtension) {
try {
isAnimated = await this.isGifAnimated(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
await this.ui.send(this.id, { valid: false });
return false;
}
this.state.still = !isAnimated;
} }
catch (err) { else if (this.stillExtensions.indexOf(ext) !== -1) {
//this.log.error(err, 'FILMOUT', true, true); this.state.still = true;
this.state.enabled = false; }
await this.ui.send(this.id, { valid: false }); else if (this.videoExtensions.indexOf(ext) !== -1) {
this.state.still = false;
}
else {
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
return false; return false;
} }
try { if (this.state.still) {
frames = await this.ffprobe.frames(arg.path); try {
info = await this.stillInfo(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
frames = 1;
} }
catch (err) { else {
this.log.error(err, 'FILMOUT', true, true); try {
this.state.enabled = false; info = await this.ffprobe.info(arg.path);
await this.ui.send(this.id, { valid: false }); }
return false; catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
try {
frames = await this.ffprobe.frames(arg.path);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid: false });
return false;
}
} }
console.dir(info);
this.state.frame = 0; this.state.frame = 0;
this.state.path = arg.path; this.state.path = arg.path;
this.state.fileName = arg.fileName; this.state.fileName = arg.fileName;
@ -130,6 +192,29 @@ class FilmOut {
this.state.enabled = true; this.state.enabled = true;
return await this.ui.send(this.id, { valid: true, state: JSON.stringify(this.state) }); return await this.ui.send(this.id, { valid: true, state: JSON.stringify(this.state) });
} }
/**
* Return true if gif is animated, false if it is a still
**/
async isGifAnimated(pathStr) {
let gifBuffer;
try {
gifBuffer = await fs_extra_1.readFile(pathStr);
}
catch (err) {
this.log.error(err, 'FILMOUT', true, true);
return false;
}
return animated(gifBuffer);
}
/**
* Return information on a still image using the sharp module
**/
async stillInfo(pathStr) {
return sharp_1.default(pathStr).metadata();
}
/**
*
**/
async previewFrame(evt, arg) { async previewFrame(evt, arg) {
const state = JSON.parse(JSON.stringify(this.state)); const state = JSON.parse(JSON.stringify(this.state));
let path; let path;
@ -144,6 +229,9 @@ class FilmOut {
} }
this.ui.send('preview_frame', { path, frame: arg.frame }); this.ui.send('preview_frame', { path, frame: arg.frame });
} }
/**
*
**/
async preview(evt, arg) { async preview(evt, arg) {
const state = JSON.parse(JSON.stringify(this.state)); const state = JSON.parse(JSON.stringify(this.state));
let path; let path;
@ -164,6 +252,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async focus(evt, arg) { async focus(evt, arg) {
this.log.info(`Showing focus screen`); this.log.info(`Showing focus screen`);
try { try {
@ -174,6 +265,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async field(evt, arg) { async field(evt, arg) {
this.log.info(`Showing field guide screen`); this.log.info(`Showing field guide screen`);
try { try {
@ -184,6 +278,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async meter(evt, arg) { async meter(evt, arg) {
this.log.info(`Showing meter screen`); this.log.info(`Showing meter screen`);
try { try {
@ -194,6 +291,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async close(evt, arg) { async close(evt, arg) {
try { try {
await this.display.hide(); await this.display.hide();
@ -203,6 +303,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
onDisplay(evt, arg) { onDisplay(evt, arg) {
this.display.change(arg.display); this.display.change(arg.display);
this.log.info(`Changing the display to ${arg.display}`); this.log.info(`Changing the display to ${arg.display}`);

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,20 @@
'use strict'; 'use strict';
import sharp from 'sharp';
import * as animated from 'animated-gif-detector';
import { extname } from 'path';
import { readFile } from 'fs-extra';
import { delay } from 'delay'; import { delay } from 'delay';
class FilmOut { class FilmOut {
private id : string = 'filmout'; private id : string = 'filmout';
private videoExtensions : string[] = ['.mpg', '.mpeg', '.mov', '.mkv', '.avi', '.mp4'];
private stillExtensions : string[] = ['.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp'];
private gifExtension : string = '.gif';
public state : any = { public state : any = {
frame : 0, frame : 0,
frames : 0, frames : 0,
still : false,
path : null, path : null,
fileName : null, fileName : null,
info : {}, info : {},
@ -45,6 +53,7 @@ class FilmOut {
**/ **/
private listen () { private listen () {
this.ipc.on(this.id, this.onConnect.bind(this)); this.ipc.on(this.id, this.onConnect.bind(this));
this.ipc.on('focus', this.focus.bind(this)); this.ipc.on('focus', this.focus.bind(this));
this.ipc.on('field', this.field.bind(this)); this.ipc.on('field', this.field.bind(this));
this.ipc.on('meter', this.meter.bind(this)); this.ipc.on('meter', this.meter.bind(this));
@ -64,6 +73,9 @@ class FilmOut {
**/ **/
public async move () { public async move () {
let start : number = +new Date(); let start : number = +new Date();
if (this.state.still) {
return false;
}
if (this.state.dir) { if (this.state.dir) {
this.state.frame++; this.state.frame++;
} else { } else {
@ -94,7 +106,7 @@ class FilmOut {
await this.display.show(this.state.frame); await this.display.show(this.state.frame);
await delay(20); await delay(20);
} }
/** /**
* *
**/ **/
private async end () { private async end () {
@ -106,26 +118,64 @@ class FilmOut {
* *
**/ **/
async onConnect (evt : any, arg : any) { async onConnect (evt : any, arg : any) {
let info; let frames : number = 0;
let frames = 0; let isAnimated : boolean = false;
let info : any;
let ext : string;
try { ext = extname(arg.fileName.toLowerCase());
info = await this.ffprobe.info(arg.path);
} catch (err) { console.dir(arg)
//this.log.error(err, 'FILMOUT', true, true); console.log(ext)
this.state.enabled = false;
await this.ui.send(this.id, { valid : false }); if (ext === this.gifExtension) {
return false; try {
} isAnimated = await this.isGifAnimated(arg.path);
try { } catch (err) {
frames = await this.ffprobe.frames(arg.path); this.log.error(err, 'FILMOUT', true, true);
} catch (err) { await this.ui.send(this.id, { valid : false });
this.log.error(err, 'FILMOUT', true, true); return false;
this.state.enabled = false; }
await this.ui.send(this.id, { valid : false }); this.state.still = !isAnimated;
} else if (this.stillExtensions.indexOf(ext) !== -1) {
this.state.still = true;
} else if (this.videoExtensions.indexOf(ext) !== -1) {
this.state.still = false;
} else {
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
return false; return false;
} }
if (this.state.still) {
try {
info = await this.stillInfo(arg.path);
} catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid : false });
return false;
}
frames = 1;
} else {
try {
info = await this.ffprobe.info(arg.path);
} catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid : false });
return false;
}
try {
frames = await this.ffprobe.frames(arg.path);
} catch (err) {
this.log.error(err, 'FILMOUT', true, true);
this.state.enabled = false;
await this.ui.send(this.id, { valid : false });
return false;
}
}
console.dir(info)
this.state.frame = 0; this.state.frame = 0;
this.state.path = arg.path; this.state.path = arg.path;
this.state.fileName = arg.fileName; this.state.fileName = arg.fileName;
@ -137,7 +187,28 @@ class FilmOut {
this.state.enabled = true; this.state.enabled = true;
return await this.ui.send(this.id, { valid : true, state : JSON.stringify(this.state) }); return await this.ui.send(this.id, { valid : true, state : JSON.stringify(this.state) });
} }
/**
* Return true if gif is animated, false if it is a still
**/
async isGifAnimated (pathStr : string) {
let gifBuffer : Buffer;
try {
gifBuffer = await readFile(pathStr);
} catch (err) {
this.log.error(err, 'FILMOUT', true, true);
return false;
}
return animated(gifBuffer);
}
/**
* Return information on a still image using the sharp module
**/
async stillInfo (pathStr : string) {
return sharp(pathStr).metadata();
}
/**
*
**/
async previewFrame (evt : any, arg : any) { async previewFrame (evt : any, arg : any) {
const state : any = JSON.parse(JSON.stringify(this.state)); const state : any = JSON.parse(JSON.stringify(this.state));
let path : string; let path : string;
@ -152,7 +223,9 @@ class FilmOut {
} }
this.ui.send('preview_frame', { path, frame : arg.frame }) this.ui.send('preview_frame', { path, frame : arg.frame })
} }
/**
*
**/
async preview (evt : any, arg : any) { async preview (evt : any, arg : any) {
const state : any = JSON.parse(JSON.stringify(this.state)); const state : any = JSON.parse(JSON.stringify(this.state));
let path : string; let path : string;
@ -174,7 +247,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async focus (evt : any, arg : any) { async focus (evt : any, arg : any) {
this.log.info(`Showing focus screen`); this.log.info(`Showing focus screen`);
try { try {
@ -184,7 +259,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async field (evt : any, arg : any) { async field (evt : any, arg : any) {
this.log.info(`Showing field guide screen`); this.log.info(`Showing field guide screen`);
try { try {
@ -194,7 +271,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async meter (evt : any, arg : any) { async meter (evt : any, arg : any) {
this.log.info(`Showing meter screen`); this.log.info(`Showing meter screen`);
try { try {
@ -204,7 +283,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
async close (evt : any, arg : any) { async close (evt : any, arg : any) {
try { try {
await this.display.hide(); await this.display.hide();
@ -213,7 +294,9 @@ class FilmOut {
this.log.error(err, 'FILMOUT', true, true); this.log.error(err, 'FILMOUT', true, true);
} }
} }
/**
*
**/
onDisplay (evt : any, arg : any) { onDisplay (evt : any, arg : any) {
this.display.change(arg.display); this.display.change(arg.display);
this.log.info(`Changing the display to ${arg.display}`); this.log.info(`Changing the display to ${arg.display}`);