Pre-export all frames in video (with confirmation dialog). Greatly improves sequence times and reliablity in conjunction with last commit. Resolves #36 and resolves #39.
This commit is contained in:
parent
aec3e29476
commit
d70de98256
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"ext_port": 1111,
|
"ext_port": 1111,
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"mcopy": {
|
"mcopy": {
|
||||||
|
|
|
@ -4,18 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
* @module display
|
* @module display
|
||||||
* Provides features for displaying a full screen display of images for the digital module.
|
* Provides features for displaying a full screen display of images for the digital module.
|
||||||
**/
|
**/
|
||||||
const spawn = require("spawn");
|
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const delay_1 = require("delay");
|
const delay_1 = require("delay");
|
||||||
const { BrowserWindow } = require('electron');
|
const { BrowserWindow } = require('electron');
|
||||||
function padded_frame(i) {
|
|
||||||
let len = (i + '').length;
|
|
||||||
let str = i + '';
|
|
||||||
for (let x = 0; x < 8 - len; x++) {
|
|
||||||
str = '0' + str;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
class WebView {
|
class WebView {
|
||||||
constructor(platform, display) {
|
constructor(platform, display) {
|
||||||
this.opened = false;
|
this.opened = false;
|
||||||
|
@ -78,9 +69,10 @@ class WebView {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
onLoad(evt, arg) {
|
onLoad(evt, arg) {
|
||||||
console.dir(arg);
|
if (this.loadWait[arg.src]) {
|
||||||
this.loadWait[arg.src]();
|
this.loadWait[arg.src]();
|
||||||
delete this.loadWait[arg.src];
|
delete this.loadWait[arg.src];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async focus() {
|
async focus() {
|
||||||
if (!this.digitalWindow) {
|
if (!this.digitalWindow) {
|
||||||
|
@ -139,28 +131,6 @@ class WebView {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class EOG {
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
open() {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
async show(src) {
|
|
||||||
//timeout 3 eog --fullscreen ${src}
|
|
||||||
this.cp = spawn('eog', ['--fullscreen', src]);
|
|
||||||
await delay_1.delay(200);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
hide() {
|
|
||||||
if (this.cp) {
|
|
||||||
this.cp.kill();
|
|
||||||
this.cp = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close() {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Display {
|
class Display {
|
||||||
constructor(sys) {
|
constructor(sys) {
|
||||||
this.platform = sys.platform;
|
this.platform = sys.platform;
|
||||||
|
@ -180,12 +150,8 @@ class Display {
|
||||||
await this.wv.open();
|
await this.wv.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async show(frame) {
|
async show(src) {
|
||||||
let padded = padded_frame(frame);
|
await this.wv.show(src);
|
||||||
let ext = 'png';
|
|
||||||
let tmppath;
|
|
||||||
tmppath = path_1.join(this.tmpdir, `export-${padded}.${ext}`);
|
|
||||||
await this.wv.show(tmppath);
|
|
||||||
}
|
}
|
||||||
async showPath(pathStr) {
|
async showPath(pathStr) {
|
||||||
return await this.wv.show(pathStr);
|
return await this.wv.show(pathStr);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
/** @module ffmpeg **/
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const fs_extra_1 = require("fs-extra");
|
const fs_extra_1 = require("fs-extra");
|
||||||
const exec_1 = require("exec");
|
const exec_1 = require("exec");
|
||||||
|
@ -55,28 +56,35 @@ class FFMPEG {
|
||||||
const w = state.info.width;
|
const w = state.info.width;
|
||||||
const h = state.info.height;
|
const h = state.info.height;
|
||||||
const padded = this.padded_frame(frameNum);
|
const padded = this.padded_frame(frameNum);
|
||||||
let ext = 'tif';
|
let ext = 'png';
|
||||||
let rgb = light.color;
|
//let rgb : any[] = light.color;
|
||||||
let tmpoutput;
|
let tmpoutput;
|
||||||
let cmd;
|
let cmd;
|
||||||
let output;
|
let output;
|
||||||
let cmd2;
|
//let cmd2 : string;
|
||||||
let output2;
|
//let output2 : any;
|
||||||
|
let fileExists = false;
|
||||||
let scale = '';
|
let scale = '';
|
||||||
if (w && h) {
|
if (w && h) {
|
||||||
scale = `,scale=${w}:${h}`;
|
scale = `,scale=${w}:${h}`;
|
||||||
}
|
}
|
||||||
//console.dir(state)
|
tmpoutput = path_1.join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
||||||
//if (system.platform !== 'nix') {
|
try {
|
||||||
ext = 'png';
|
fileExists = await fs_extra_1.exists(tmpoutput);
|
||||||
//}
|
}
|
||||||
tmpoutput = path_1.join(this.TMPDIR, `export-${padded}.${ext}`);
|
catch (err) {
|
||||||
rgb = rgb.map((e) => {
|
//
|
||||||
return parseInt(e);
|
}
|
||||||
});
|
if (fileExists) {
|
||||||
|
this.log.info(`File ${tmpoutput} exists`);
|
||||||
|
return tmpoutput;
|
||||||
|
}
|
||||||
|
//rgb = rgb.map((e : string) => {
|
||||||
|
// return parseInt(e);
|
||||||
|
//});
|
||||||
//
|
//
|
||||||
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`;
|
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
||||||
cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
//cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
||||||
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
||||||
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
||||||
//-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
|
//-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
|
||||||
|
@ -89,17 +97,16 @@ class FFMPEG {
|
||||||
}
|
}
|
||||||
if (output && output.stdout)
|
if (output && output.stdout)
|
||||||
this.log.info(`"${output.stdout}"`);
|
this.log.info(`"${output.stdout}"`);
|
||||||
if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
/*if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
||||||
try {
|
try {
|
||||||
this.log.info(cmd2);
|
this.log.info(cmd2);
|
||||||
output2 = await exec_1.exec(cmd2);
|
output2 = await exec(cmd2);
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (output2 && output2.stdout)
|
|
||||||
this.log.info(`"${output2.stdout}"`);
|
if (output2 && output2.stdout) this.log.info(`"${output2.stdout}"`);*/
|
||||||
return tmpoutput;
|
return tmpoutput;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -111,14 +118,20 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {?}
|
* @returns {?}
|
||||||
**/
|
**/
|
||||||
async frames(video, obj) {
|
async frames(state) {
|
||||||
|
const video = state.path;
|
||||||
|
const w = state.info.width;
|
||||||
|
const h = state.info.height;
|
||||||
const tmppath = this.TMPDIR;
|
const tmppath = this.TMPDIR;
|
||||||
let ext = 'tif';
|
let ext = 'png';
|
||||||
let tmpoutput;
|
let tmpoutput = path_1.join(tmppath, `${state.hash}-export-%08d.${ext}`);
|
||||||
//if (system.platform !== 'nix') {
|
let cmd;
|
||||||
ext = 'png';
|
let output;
|
||||||
//}
|
let scale = '';
|
||||||
tmpoutput = path_1.join(tmppath, `export-%08d.${ext}`);
|
if (w && h) {
|
||||||
|
scale = `scale=${w}:${h}`;
|
||||||
|
}
|
||||||
|
cmd = `${this.bin} -y -i "${video}" -vf "${scale}" -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
||||||
try {
|
try {
|
||||||
await fs_extra_1.mkdir(tmppath);
|
await fs_extra_1.mkdir(tmppath);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +139,15 @@ class FFMPEG {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
||||||
|
try {
|
||||||
|
this.log.info(cmd);
|
||||||
|
output = await exec_1.exec(cmd);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.log.error(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Clears a specific frame from the tmp directory
|
* Clears a specific frame from the tmp directory
|
||||||
|
@ -134,24 +156,19 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {boolean} True if successful, false if not
|
* @returns {boolean} True if successful, false if not
|
||||||
**/
|
**/
|
||||||
async clear(frame) {
|
async clear(state) {
|
||||||
const padded = this.padded_frame(frame);
|
const padded = this.padded_frame(state.frame);
|
||||||
let ext = 'tif';
|
let ext = 'png';
|
||||||
let tmppath;
|
let tmppath;
|
||||||
let tmpoutput;
|
|
||||||
let cmd;
|
|
||||||
let fileExists;
|
let fileExists;
|
||||||
//if (system.platform !== 'nix') {
|
tmppath = path_1.join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
||||||
ext = 'png';
|
|
||||||
//}
|
|
||||||
tmppath = path_1.join(this.TMPDIR, `export-${padded}.${ext}`);
|
|
||||||
try {
|
try {
|
||||||
fileExists = await fs_extra_1.exists(tmppath);
|
fileExists = await fs_extra_1.exists(tmppath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
if (!fs_extra_1.exists)
|
if (!fileExists)
|
||||||
return false;
|
return false;
|
||||||
try {
|
try {
|
||||||
await fs_extra_1.unlink(tmppath);
|
await fs_extra_1.unlink(tmppath);
|
||||||
|
@ -175,6 +192,12 @@ class FFMPEG {
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
|
files = files.filter((file) => {
|
||||||
|
if (file.indexOf('-export-') !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
if (files) {
|
if (files) {
|
||||||
files.forEach(async (file, index) => {
|
files.forEach(async (file, index) => {
|
||||||
try {
|
try {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ const animated_gif_detector_1 = __importDefault(require("animated-gif-detector")
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const fs_extra_1 = require("fs-extra");
|
const fs_extra_1 = require("fs-extra");
|
||||||
const delay_1 = require("delay");
|
const delay_1 = require("delay");
|
||||||
|
const crypto_1 = require("crypto");
|
||||||
/**
|
/**
|
||||||
* @module FilmOut
|
* @module FilmOut
|
||||||
**/
|
**/
|
||||||
|
@ -65,6 +66,15 @@ class FilmOut {
|
||||||
this.ipc.on('preview', this.preview.bind(this));
|
this.ipc.on('preview', this.preview.bind(this));
|
||||||
this.ipc.on('preview_frame', this.previewFrame.bind(this));
|
this.ipc.on('preview_frame', this.previewFrame.bind(this));
|
||||||
this.ipc.on('display', this.onDisplay.bind(this));
|
this.ipc.on('display', this.onDisplay.bind(this));
|
||||||
|
this.ipc.on('pre_export', this.onPreExport.bind(this));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a hash of a string.
|
||||||
|
*
|
||||||
|
* @param {string} data Data to produce hash of
|
||||||
|
*/
|
||||||
|
hash(data) {
|
||||||
|
return crypto_1.createHash('sha1').update(data).digest('hex');
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets filmout direction.
|
* Sets filmout direction.
|
||||||
|
@ -97,21 +107,15 @@ class FilmOut {
|
||||||
* Begin the process of exporting single frames from the video for display.
|
* Begin the process of exporting single frames from the video for display.
|
||||||
**/
|
**/
|
||||||
async start() {
|
async start() {
|
||||||
|
let path;
|
||||||
try {
|
try {
|
||||||
await this.ffmpeg.clearAll();
|
path = await this.ffmpeg.frame(this.state, this.light.state);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
try {
|
await this.display.show(path);
|
||||||
await this.ffmpeg.frame(this.state, this.light.state);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
await this.display.show(this.state.frame);
|
|
||||||
await delay_1.delay(20);
|
await delay_1.delay(20);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -156,6 +160,13 @@ class FilmOut {
|
||||||
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
|
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
await this.ffmpeg.clearAll();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
if (this.state.still) {
|
if (this.state.still) {
|
||||||
try {
|
try {
|
||||||
info = await this.stillInfo(arg.path);
|
info = await this.stillInfo(arg.path);
|
||||||
|
@ -194,11 +205,30 @@ class FilmOut {
|
||||||
this.state.fileName = arg.fileName;
|
this.state.fileName = arg.fileName;
|
||||||
this.state.frames = frames;
|
this.state.frames = frames;
|
||||||
this.state.info = info;
|
this.state.info = info;
|
||||||
|
this.state.hash = this.hash(arg.path);
|
||||||
this.log.info(`Opened ${this.state.fileName}`, 'FILMOUT', true, true);
|
this.log.info(`Opened ${this.state.fileName}`, 'FILMOUT', true, true);
|
||||||
this.log.info(`Frames : ${frames}`, 'FILMOUT', true, true);
|
this.log.info(`Frames : ${frames}`, 'FILMOUT', true, true);
|
||||||
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) });
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Pre-export all frames from video for display.
|
||||||
|
*
|
||||||
|
* @param {object} evt IPC event
|
||||||
|
* @param {object} arg IPC args
|
||||||
|
*/
|
||||||
|
async onPreExport(evt, arg) {
|
||||||
|
if (!this.state.path) {
|
||||||
|
return await this.ui.send('pre_export', { complete: false, err: 'No file to pre export.' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.ffmpeg.frames(this.state);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return await this.ui.send('pre_export', { complete: false, err });
|
||||||
|
}
|
||||||
|
return await this.ui.send('pre_export', { complete: true });
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Return true if gif is animated, false if it is a still
|
* Return true if gif is animated, false if it is a still
|
||||||
*
|
*
|
||||||
|
@ -248,7 +278,7 @@ class FilmOut {
|
||||||
this.ui.send('preview_frame', { path, frame: arg.frame });
|
this.ui.send('preview_frame', { path, frame: arg.frame });
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
* Open a single frame in a display window to preview filmout.
|
||||||
*
|
*
|
||||||
* @param {object} evt Original event
|
* @param {object} evt Original event
|
||||||
* @param {object} arg Arguments from message
|
* @param {object} arg Arguments from message
|
||||||
|
@ -267,7 +297,7 @@ class FilmOut {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.display.open();
|
await this.display.open();
|
||||||
await this.display.show(arg.frame);
|
await this.display.show(path);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -62,6 +62,7 @@ class FilmOut {
|
||||||
ipcRenderer.on(this.id, this.onFilmout.bind(this));
|
ipcRenderer.on(this.id, this.onFilmout.bind(this));
|
||||||
ipcRenderer.on('system', this.onSystem.bind(this));
|
ipcRenderer.on('system', this.onSystem.bind(this));
|
||||||
ipcRenderer.on('preview_frame', this.onFrame.bind(this));
|
ipcRenderer.on('preview_frame', this.onFrame.bind(this));
|
||||||
|
ipcRenderer.on('pre_export', this.onPreExport.bind(this));
|
||||||
}
|
}
|
||||||
onSystem(evt, args) {
|
onSystem(evt, args) {
|
||||||
let option;
|
let option;
|
||||||
|
@ -153,7 +154,7 @@ class FilmOut {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
log.info(`Selected video ${pathStr.split('/').pop()}`, 'DIGITAL', true);
|
log.info(`Selected video ${pathStr.split('/').pop()}`, 'FILMOUT', true);
|
||||||
elem.attr('data-file', pathStr);
|
elem.attr('data-file', pathStr);
|
||||||
displayName = pathStr.split('/').pop();
|
displayName = pathStr.split('/').pop();
|
||||||
elem.val(displayName);
|
elem.val(displayName);
|
||||||
|
@ -198,7 +199,7 @@ class FilmOut {
|
||||||
state = JSON.parse(args.state);
|
state = JSON.parse(args.state);
|
||||||
$('#digital').addClass('active');
|
$('#digital').addClass('active');
|
||||||
$('#projector_type_digital').prop('checked', 'checked');
|
$('#projector_type_digital').prop('checked', 'checked');
|
||||||
gui.notify('DEVICES', `Using video ${state.fileName}`);
|
gui.notify('FILMOUT', `Using video ${state.fileName}`);
|
||||||
seq.set(0, 'PF');
|
seq.set(0, 'PF');
|
||||||
grid.state(0);
|
grid.state(0);
|
||||||
seq.set(1, 'CF');
|
seq.set(1, 'CF');
|
||||||
|
@ -207,17 +208,20 @@ class FilmOut {
|
||||||
if (light.disabled) {
|
if (light.disabled) {
|
||||||
//light.enable();
|
//light.enable();
|
||||||
}
|
}
|
||||||
|
console.dir(state);
|
||||||
this.state.frame = 0;
|
this.state.frame = 0;
|
||||||
this.state.frames = state.frames;
|
this.state.frames = state.frames;
|
||||||
this.state.width = state.info.width;
|
this.state.width = state.info.width;
|
||||||
this.state.height = state.info.height;
|
this.state.height = state.info.height;
|
||||||
this.state.name = state.fileName;
|
this.state.name = state.fileName;
|
||||||
|
this.state.path = state.path;
|
||||||
$('#seq_loop').val(`${state.frames - 1}`).trigger('change');
|
$('#seq_loop').val(`${state.frames - 1}`).trigger('change');
|
||||||
$('#filmout_stats_video_name').text(state.fileName);
|
$('#filmout_stats_video_name').text(state.fileName);
|
||||||
$('#filmout_stats_video_size').text(`${state.info.width} x ${state.info.height}`);
|
$('#filmout_stats_video_size').text(`${state.info.width} x ${state.info.height}`);
|
||||||
$('#filmout_stats_video_frames').text(`${state.frames} frames`);
|
$('#filmout_stats_video_frames').text(`${state.frames} frames`);
|
||||||
gui.updateState();
|
gui.updateState();
|
||||||
this.previewFrame();
|
this.previewFrame();
|
||||||
|
this.preExport();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$('#projector_type_digital').prop('checked', 'checked');
|
$('#projector_type_digital').prop('checked', 'checked');
|
||||||
|
@ -235,6 +239,29 @@ class FilmOut {
|
||||||
elem[0].style.backgroundImage = `url('${args.path}')`;
|
elem[0].style.backgroundImage = `url('${args.path}')`;
|
||||||
elem.addClass('on');
|
elem.addClass('on');
|
||||||
}
|
}
|
||||||
|
preExport() {
|
||||||
|
let proceed = false;
|
||||||
|
if (this.state.path && this.state.path !== '') {
|
||||||
|
proceed = confirm(`Export all frames for ${this.state.name}? This may take a while, but will allow filmout sequences to run faster.`);
|
||||||
|
}
|
||||||
|
if (proceed) {
|
||||||
|
gui.overlay(true);
|
||||||
|
gui.spinner(true, `Exporting frames for ${this.state.name}`);
|
||||||
|
ipcRenderer.send('pre_export', {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPreExport(evt, args) {
|
||||||
|
log.info('onPreExport');
|
||||||
|
if (args.completed) {
|
||||||
|
gui.notify('FILMOUT', `Exported frames for ${this.state.name}`);
|
||||||
|
log.info(`Exported frames for ${this.state.name}`, 'FILMOUT', true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.error(args.err);
|
||||||
|
}
|
||||||
|
gui.overlay(false);
|
||||||
|
gui.spinner(false);
|
||||||
|
}
|
||||||
advance() {
|
advance() {
|
||||||
this.state.frame++;
|
this.state.frame++;
|
||||||
if (this.state.frame >= this.state.frames) {
|
if (this.state.frame >= this.state.frames) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mcopy-app",
|
"name": "mcopy-app",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mcopy-app",
|
"name": "mcopy-app",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"description": "GUI for the mcopy small gauge film optical printer platform",
|
"description": "GUI for the mcopy small gauge film optical printer platform",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -61,6 +61,7 @@ class FilmOut {
|
||||||
ipcRenderer.on(this.id, this.onFilmout.bind(this));
|
ipcRenderer.on(this.id, this.onFilmout.bind(this));
|
||||||
ipcRenderer.on('system', this.onSystem.bind(this));
|
ipcRenderer.on('system', this.onSystem.bind(this));
|
||||||
ipcRenderer.on('preview_frame', this.onFrame.bind(this));
|
ipcRenderer.on('preview_frame', this.onFrame.bind(this));
|
||||||
|
ipcRenderer.on('pre_export', this.onPreExport.bind(this));
|
||||||
}
|
}
|
||||||
onSystem (evt : Event, args : any) {
|
onSystem (evt : Event, args : any) {
|
||||||
let option : any;
|
let option : any;
|
||||||
|
@ -157,7 +158,7 @@ class FilmOut {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
log.info(`Selected video ${pathStr.split('/').pop()}`, 'DIGITAL', true);
|
log.info(`Selected video ${pathStr.split('/').pop()}`, 'FILMOUT', true);
|
||||||
elem.attr('data-file', pathStr);
|
elem.attr('data-file', pathStr);
|
||||||
displayName = pathStr.split('/').pop();
|
displayName = pathStr.split('/').pop();
|
||||||
elem.val(displayName);
|
elem.val(displayName);
|
||||||
|
@ -204,7 +205,7 @@ class FilmOut {
|
||||||
state = JSON.parse(args.state);
|
state = JSON.parse(args.state);
|
||||||
$('#digital').addClass('active');
|
$('#digital').addClass('active');
|
||||||
$('#projector_type_digital').prop('checked', 'checked');
|
$('#projector_type_digital').prop('checked', 'checked');
|
||||||
gui.notify('DEVICES', `Using video ${state.fileName}`);
|
gui.notify('FILMOUT', `Using video ${state.fileName}`);
|
||||||
|
|
||||||
seq.set(0, 'PF');
|
seq.set(0, 'PF');
|
||||||
grid.state(0);
|
grid.state(0);
|
||||||
|
@ -216,12 +217,13 @@ class FilmOut {
|
||||||
if (light.disabled) {
|
if (light.disabled) {
|
||||||
//light.enable();
|
//light.enable();
|
||||||
}
|
}
|
||||||
|
console.dir(state);
|
||||||
this.state.frame = 0;
|
this.state.frame = 0;
|
||||||
this.state.frames = state.frames;
|
this.state.frames = state.frames;
|
||||||
this.state.width = state.info.width;
|
this.state.width = state.info.width;
|
||||||
this.state.height = state.info.height;
|
this.state.height = state.info.height;
|
||||||
this.state.name = state.fileName;
|
this.state.name = state.fileName;
|
||||||
|
this.state.path = state.path;
|
||||||
|
|
||||||
$('#seq_loop').val(`${state.frames - 1}`).trigger('change');
|
$('#seq_loop').val(`${state.frames - 1}`).trigger('change');
|
||||||
$('#filmout_stats_video_name').text(state.fileName);
|
$('#filmout_stats_video_name').text(state.fileName);
|
||||||
|
@ -230,6 +232,7 @@ class FilmOut {
|
||||||
|
|
||||||
gui.updateState();
|
gui.updateState();
|
||||||
this.previewFrame();
|
this.previewFrame();
|
||||||
|
this.preExport();
|
||||||
} else {
|
} else {
|
||||||
$('#projector_type_digital').prop('checked', 'checked');
|
$('#projector_type_digital').prop('checked', 'checked');
|
||||||
$('#digital').removeClass('active');
|
$('#digital').removeClass('active');
|
||||||
|
@ -246,6 +249,30 @@ class FilmOut {
|
||||||
elem[0].style.backgroundImage = `url('${args.path}')`;
|
elem[0].style.backgroundImage = `url('${args.path}')`;
|
||||||
elem.addClass('on');
|
elem.addClass('on');
|
||||||
}
|
}
|
||||||
|
preExport () {
|
||||||
|
let proceed = false;
|
||||||
|
|
||||||
|
if (this.state.path && this.state.path !== '') {
|
||||||
|
proceed = confirm(`Export all frames for ${this.state.name}? This may take a while, but will allow filmout sequences to run faster.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proceed) {
|
||||||
|
gui.overlay(true);
|
||||||
|
gui.spinner(true, `Exporting frames for ${this.state.name}`);
|
||||||
|
ipcRenderer.send('pre_export', { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPreExport (evt : Event, args : any) {
|
||||||
|
log.info('onPreExport');
|
||||||
|
if (args.completed) {
|
||||||
|
gui.notify('FILMOUT', `Exported frames for ${this.state.name}`);
|
||||||
|
log.info(`Exported frames for ${this.state.name}`, 'FILMOUT', true);
|
||||||
|
} else {
|
||||||
|
log.error(args.err);
|
||||||
|
}
|
||||||
|
gui.overlay(false);
|
||||||
|
gui.spinner(false);
|
||||||
|
}
|
||||||
advance () {
|
advance () {
|
||||||
this.state.frame++;
|
this.state.frame++;
|
||||||
if (this.state.frame >= this.state.frames) {
|
if (this.state.frame >= this.state.frames) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"ext_port": 1111,
|
"ext_port": 1111,
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"mcopy": {
|
"mcopy": {
|
||||||
|
|
|
@ -4,18 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
* @module display
|
* @module display
|
||||||
* Provides features for displaying a full screen display of images for the digital module.
|
* Provides features for displaying a full screen display of images for the digital module.
|
||||||
**/
|
**/
|
||||||
const spawn = require("spawn");
|
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const delay_1 = require("delay");
|
const delay_1 = require("delay");
|
||||||
const { BrowserWindow } = require('electron');
|
const { BrowserWindow } = require('electron');
|
||||||
function padded_frame(i) {
|
|
||||||
let len = (i + '').length;
|
|
||||||
let str = i + '';
|
|
||||||
for (let x = 0; x < 8 - len; x++) {
|
|
||||||
str = '0' + str;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
class WebView {
|
class WebView {
|
||||||
constructor(platform, display) {
|
constructor(platform, display) {
|
||||||
this.opened = false;
|
this.opened = false;
|
||||||
|
@ -78,9 +69,10 @@ class WebView {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
onLoad(evt, arg) {
|
onLoad(evt, arg) {
|
||||||
console.dir(arg);
|
if (this.loadWait[arg.src]) {
|
||||||
this.loadWait[arg.src]();
|
this.loadWait[arg.src]();
|
||||||
delete this.loadWait[arg.src];
|
delete this.loadWait[arg.src];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async focus() {
|
async focus() {
|
||||||
if (!this.digitalWindow) {
|
if (!this.digitalWindow) {
|
||||||
|
@ -139,28 +131,6 @@ class WebView {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class EOG {
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
open() {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
async show(src) {
|
|
||||||
//timeout 3 eog --fullscreen ${src}
|
|
||||||
this.cp = spawn('eog', ['--fullscreen', src]);
|
|
||||||
await delay_1.delay(200);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
hide() {
|
|
||||||
if (this.cp) {
|
|
||||||
this.cp.kill();
|
|
||||||
this.cp = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close() {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Display {
|
class Display {
|
||||||
constructor(sys) {
|
constructor(sys) {
|
||||||
this.platform = sys.platform;
|
this.platform = sys.platform;
|
||||||
|
@ -180,12 +150,8 @@ class Display {
|
||||||
await this.wv.open();
|
await this.wv.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async show(frame) {
|
async show(src) {
|
||||||
let padded = padded_frame(frame);
|
await this.wv.show(src);
|
||||||
let ext = 'png';
|
|
||||||
let tmppath;
|
|
||||||
tmppath = path_1.join(this.tmpdir, `export-${padded}.${ext}`);
|
|
||||||
await this.wv.show(tmppath);
|
|
||||||
}
|
}
|
||||||
async showPath(pathStr) {
|
async showPath(pathStr) {
|
||||||
return await this.wv.show(pathStr);
|
return await this.wv.show(pathStr);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
/** @module ffmpeg **/
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const fs_extra_1 = require("fs-extra");
|
const fs_extra_1 = require("fs-extra");
|
||||||
const exec_1 = require("exec");
|
const exec_1 = require("exec");
|
||||||
|
@ -55,28 +56,35 @@ class FFMPEG {
|
||||||
const w = state.info.width;
|
const w = state.info.width;
|
||||||
const h = state.info.height;
|
const h = state.info.height;
|
||||||
const padded = this.padded_frame(frameNum);
|
const padded = this.padded_frame(frameNum);
|
||||||
let ext = 'tif';
|
let ext = 'png';
|
||||||
let rgb = light.color;
|
//let rgb : any[] = light.color;
|
||||||
let tmpoutput;
|
let tmpoutput;
|
||||||
let cmd;
|
let cmd;
|
||||||
let output;
|
let output;
|
||||||
let cmd2;
|
//let cmd2 : string;
|
||||||
let output2;
|
//let output2 : any;
|
||||||
|
let fileExists = false;
|
||||||
let scale = '';
|
let scale = '';
|
||||||
if (w && h) {
|
if (w && h) {
|
||||||
scale = `,scale=${w}:${h}`;
|
scale = `,scale=${w}:${h}`;
|
||||||
}
|
}
|
||||||
//console.dir(state)
|
tmpoutput = path_1.join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
||||||
//if (system.platform !== 'nix') {
|
try {
|
||||||
ext = 'png';
|
fileExists = await fs_extra_1.exists(tmpoutput);
|
||||||
//}
|
}
|
||||||
tmpoutput = path_1.join(this.TMPDIR, `export-${padded}.${ext}`);
|
catch (err) {
|
||||||
rgb = rgb.map((e) => {
|
//
|
||||||
return parseInt(e);
|
}
|
||||||
});
|
if (fileExists) {
|
||||||
|
this.log.info(`File ${tmpoutput} exists`);
|
||||||
|
return tmpoutput;
|
||||||
|
}
|
||||||
|
//rgb = rgb.map((e : string) => {
|
||||||
|
// return parseInt(e);
|
||||||
|
//});
|
||||||
//
|
//
|
||||||
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`;
|
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
||||||
cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
//cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
||||||
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
||||||
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
||||||
//-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
|
//-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
|
||||||
|
@ -89,17 +97,16 @@ class FFMPEG {
|
||||||
}
|
}
|
||||||
if (output && output.stdout)
|
if (output && output.stdout)
|
||||||
this.log.info(`"${output.stdout}"`);
|
this.log.info(`"${output.stdout}"`);
|
||||||
if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
/*if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
||||||
try {
|
try {
|
||||||
this.log.info(cmd2);
|
this.log.info(cmd2);
|
||||||
output2 = await exec_1.exec(cmd2);
|
output2 = await exec(cmd2);
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (output2 && output2.stdout)
|
|
||||||
this.log.info(`"${output2.stdout}"`);
|
if (output2 && output2.stdout) this.log.info(`"${output2.stdout}"`);*/
|
||||||
return tmpoutput;
|
return tmpoutput;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -111,14 +118,20 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {?}
|
* @returns {?}
|
||||||
**/
|
**/
|
||||||
async frames(video, obj) {
|
async frames(state) {
|
||||||
|
const video = state.path;
|
||||||
|
const w = state.info.width;
|
||||||
|
const h = state.info.height;
|
||||||
const tmppath = this.TMPDIR;
|
const tmppath = this.TMPDIR;
|
||||||
let ext = 'tif';
|
let ext = 'png';
|
||||||
let tmpoutput;
|
let tmpoutput = path_1.join(tmppath, `${state.hash}-export-%08d.${ext}`);
|
||||||
//if (system.platform !== 'nix') {
|
let cmd;
|
||||||
ext = 'png';
|
let output;
|
||||||
//}
|
let scale = '';
|
||||||
tmpoutput = path_1.join(tmppath, `export-%08d.${ext}`);
|
if (w && h) {
|
||||||
|
scale = `scale=${w}:${h}`;
|
||||||
|
}
|
||||||
|
cmd = `${this.bin} -y -i "${video}" -vf "${scale}" -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
||||||
try {
|
try {
|
||||||
await fs_extra_1.mkdir(tmppath);
|
await fs_extra_1.mkdir(tmppath);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +139,15 @@ class FFMPEG {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
||||||
|
try {
|
||||||
|
this.log.info(cmd);
|
||||||
|
output = await exec_1.exec(cmd);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.log.error(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Clears a specific frame from the tmp directory
|
* Clears a specific frame from the tmp directory
|
||||||
|
@ -134,24 +156,19 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {boolean} True if successful, false if not
|
* @returns {boolean} True if successful, false if not
|
||||||
**/
|
**/
|
||||||
async clear(frame) {
|
async clear(state) {
|
||||||
const padded = this.padded_frame(frame);
|
const padded = this.padded_frame(state.frame);
|
||||||
let ext = 'tif';
|
let ext = 'png';
|
||||||
let tmppath;
|
let tmppath;
|
||||||
let tmpoutput;
|
|
||||||
let cmd;
|
|
||||||
let fileExists;
|
let fileExists;
|
||||||
//if (system.platform !== 'nix') {
|
tmppath = path_1.join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
||||||
ext = 'png';
|
|
||||||
//}
|
|
||||||
tmppath = path_1.join(this.TMPDIR, `export-${padded}.${ext}`);
|
|
||||||
try {
|
try {
|
||||||
fileExists = await fs_extra_1.exists(tmppath);
|
fileExists = await fs_extra_1.exists(tmppath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
if (!fs_extra_1.exists)
|
if (!fileExists)
|
||||||
return false;
|
return false;
|
||||||
try {
|
try {
|
||||||
await fs_extra_1.unlink(tmppath);
|
await fs_extra_1.unlink(tmppath);
|
||||||
|
@ -175,6 +192,12 @@ class FFMPEG {
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
|
files = files.filter((file) => {
|
||||||
|
if (file.indexOf('-export-') !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
if (files) {
|
if (files) {
|
||||||
files.forEach(async (file, index) => {
|
files.forEach(async (file, index) => {
|
||||||
try {
|
try {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ const animated_gif_detector_1 = __importDefault(require("animated-gif-detector")
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const fs_extra_1 = require("fs-extra");
|
const fs_extra_1 = require("fs-extra");
|
||||||
const delay_1 = require("delay");
|
const delay_1 = require("delay");
|
||||||
|
const crypto_1 = require("crypto");
|
||||||
/**
|
/**
|
||||||
* @module FilmOut
|
* @module FilmOut
|
||||||
**/
|
**/
|
||||||
|
@ -65,6 +66,15 @@ class FilmOut {
|
||||||
this.ipc.on('preview', this.preview.bind(this));
|
this.ipc.on('preview', this.preview.bind(this));
|
||||||
this.ipc.on('preview_frame', this.previewFrame.bind(this));
|
this.ipc.on('preview_frame', this.previewFrame.bind(this));
|
||||||
this.ipc.on('display', this.onDisplay.bind(this));
|
this.ipc.on('display', this.onDisplay.bind(this));
|
||||||
|
this.ipc.on('pre_export', this.onPreExport.bind(this));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a hash of a string.
|
||||||
|
*
|
||||||
|
* @param {string} data Data to produce hash of
|
||||||
|
*/
|
||||||
|
hash(data) {
|
||||||
|
return crypto_1.createHash('sha1').update(data).digest('hex');
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets filmout direction.
|
* Sets filmout direction.
|
||||||
|
@ -97,21 +107,15 @@ class FilmOut {
|
||||||
* Begin the process of exporting single frames from the video for display.
|
* Begin the process of exporting single frames from the video for display.
|
||||||
**/
|
**/
|
||||||
async start() {
|
async start() {
|
||||||
|
let path;
|
||||||
try {
|
try {
|
||||||
await this.ffmpeg.clearAll();
|
path = await this.ffmpeg.frame(this.state, this.light.state);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
try {
|
await this.display.show(path);
|
||||||
await this.ffmpeg.frame(this.state, this.light.state);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
await this.display.show(this.state.frame);
|
|
||||||
await delay_1.delay(20);
|
await delay_1.delay(20);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -156,6 +160,13 @@ class FilmOut {
|
||||||
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
|
this.log.error(`File is not of a valid file type`, 'FILMOUT', true, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
await this.ffmpeg.clearAll();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
if (this.state.still) {
|
if (this.state.still) {
|
||||||
try {
|
try {
|
||||||
info = await this.stillInfo(arg.path);
|
info = await this.stillInfo(arg.path);
|
||||||
|
@ -194,11 +205,30 @@ class FilmOut {
|
||||||
this.state.fileName = arg.fileName;
|
this.state.fileName = arg.fileName;
|
||||||
this.state.frames = frames;
|
this.state.frames = frames;
|
||||||
this.state.info = info;
|
this.state.info = info;
|
||||||
|
this.state.hash = this.hash(arg.path);
|
||||||
this.log.info(`Opened ${this.state.fileName}`, 'FILMOUT', true, true);
|
this.log.info(`Opened ${this.state.fileName}`, 'FILMOUT', true, true);
|
||||||
this.log.info(`Frames : ${frames}`, 'FILMOUT', true, true);
|
this.log.info(`Frames : ${frames}`, 'FILMOUT', true, true);
|
||||||
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) });
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Pre-export all frames from video for display.
|
||||||
|
*
|
||||||
|
* @param {object} evt IPC event
|
||||||
|
* @param {object} arg IPC args
|
||||||
|
*/
|
||||||
|
async onPreExport(evt, arg) {
|
||||||
|
if (!this.state.path) {
|
||||||
|
return await this.ui.send('pre_export', { complete: false, err: 'No file to pre export.' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.ffmpeg.frames(this.state);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return await this.ui.send('pre_export', { complete: false, err });
|
||||||
|
}
|
||||||
|
return await this.ui.send('pre_export', { complete: true });
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Return true if gif is animated, false if it is a still
|
* Return true if gif is animated, false if it is a still
|
||||||
*
|
*
|
||||||
|
@ -248,7 +278,7 @@ class FilmOut {
|
||||||
this.ui.send('preview_frame', { path, frame: arg.frame });
|
this.ui.send('preview_frame', { path, frame: arg.frame });
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
* Open a single frame in a display window to preview filmout.
|
||||||
*
|
*
|
||||||
* @param {object} evt Original event
|
* @param {object} evt Original event
|
||||||
* @param {object} arg Arguments from message
|
* @param {object} arg Arguments from message
|
||||||
|
@ -267,7 +297,7 @@ class FilmOut {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.display.open();
|
await this.display.open();
|
||||||
await this.display.show(arg.frame);
|
await this.display.show(path);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mcopy-cli",
|
"name": "mcopy-cli",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"description": "CLI for controlling the mcopy optical printer platform",
|
"description": "CLI for controlling the mcopy optical printer platform",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"ext_port": 1111,
|
"ext_port": 1111,
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"mcopy": {
|
"mcopy": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mcopy",
|
"name": "mcopy",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mcopy",
|
"name": "mcopy",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"description": "Small gauge film optical printer platform",
|
"description": "Small gauge film optical printer platform",
|
||||||
"main": "build.js",
|
"main": "build.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"ext_port": 1111,
|
"ext_port": 1111,
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"mcopy": {
|
"mcopy": {
|
||||||
|
|
|
@ -5,22 +5,11 @@
|
||||||
* Provides features for displaying a full screen display of images for the digital module.
|
* Provides features for displaying a full screen display of images for the digital module.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
import spawn = require('spawn');
|
|
||||||
import { join as pathJoin } from 'path';
|
import { join as pathJoin } from 'path';
|
||||||
import { delay } from 'delay';
|
import { delay } from 'delay';
|
||||||
import { IpcMain } from 'electron';
|
|
||||||
|
|
||||||
const { BrowserWindow } = require('electron');
|
const { BrowserWindow } = require('electron');
|
||||||
|
|
||||||
function padded_frame (i : number) {
|
|
||||||
let len = (i + '').length;
|
|
||||||
let str = i + '';
|
|
||||||
for (let x = 0; x < 8 - len; x++) {
|
|
||||||
str = '0' + str;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
class WebView {
|
class WebView {
|
||||||
private digitalWindow : any;
|
private digitalWindow : any;
|
||||||
public opened : boolean = false;
|
public opened : boolean = false;
|
||||||
|
@ -91,8 +80,10 @@ class WebView {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad (evt : Event, arg : any) {
|
onLoad (evt : Event, arg : any) {
|
||||||
this.loadWait[arg.src]();
|
if (this.loadWait[arg.src]) {
|
||||||
delete this.loadWait[arg.src];
|
this.loadWait[arg.src]();
|
||||||
|
delete this.loadWait[arg.src];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async focus () {
|
async focus () {
|
||||||
if (!this.digitalWindow) {
|
if (!this.digitalWindow) {
|
||||||
|
@ -149,41 +140,12 @@ class WebView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EOG {
|
|
||||||
private cp : any;
|
|
||||||
constructor () {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public open () {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async show (src : string) {
|
|
||||||
//timeout 3 eog --fullscreen ${src}
|
|
||||||
this.cp = spawn('eog', ['--fullscreen', src]);
|
|
||||||
await delay(200)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
public hide () {
|
|
||||||
if (this.cp) {
|
|
||||||
this.cp.kill();
|
|
||||||
this.cp = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public close () {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Display {
|
class Display {
|
||||||
private platform : string;
|
private platform : string;
|
||||||
private displays : any[];
|
private displays : any[];
|
||||||
private display : any;
|
private display : any;
|
||||||
private tmpdir : string;
|
private tmpdir : string;
|
||||||
private wv : WebView;
|
private wv : WebView;
|
||||||
private eog : EOG;
|
|
||||||
|
|
||||||
constructor (sys : any) {
|
constructor (sys : any) {
|
||||||
this.platform = sys.platform;
|
this.platform = sys.platform;
|
||||||
|
@ -202,14 +164,8 @@ class Display {
|
||||||
await this.wv.open();
|
await this.wv.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async show (frame : number) {
|
public async show (src : string) {
|
||||||
let padded : string = padded_frame(frame);
|
await this.wv.show(src);
|
||||||
let ext : string = 'png';
|
|
||||||
let tmppath : string;
|
|
||||||
|
|
||||||
tmppath = pathJoin(this.tmpdir, `export-${padded}.${ext}`);
|
|
||||||
|
|
||||||
await this.wv.show(tmppath);
|
|
||||||
}
|
}
|
||||||
public async showPath (pathStr : string) {
|
public async showPath (pathStr : string) {
|
||||||
return await this.wv.show(pathStr);
|
return await this.wv.show(pathStr);
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
/** @module ffmpeg **/
|
/** @module ffmpeg **/
|
||||||
|
|
||||||
import uuid from 'uuid/v4';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { exists, mkdir, readdir, unlink } from 'fs-extra';
|
import { exists, mkdir, readdir, unlink } from 'fs-extra';
|
||||||
import { exec } from 'exec';
|
import { exec } from 'exec';
|
||||||
//const spawn = require('spawn');
|
|
||||||
import { exit } from 'exit';
|
interface FilmoutState {
|
||||||
|
frame : number;
|
||||||
|
path : string;
|
||||||
|
hash : string;
|
||||||
|
info : any;
|
||||||
|
}
|
||||||
|
|
||||||
/** @class FFMPEG **/
|
/** @class FFMPEG **/
|
||||||
|
|
||||||
|
@ -63,39 +67,46 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {string} Path of frame
|
* @returns {string} Path of frame
|
||||||
**/
|
**/
|
||||||
public async frame (state : any, light : any) {
|
public async frame (state : FilmoutState, light : any) {
|
||||||
const frameNum : number = state.frame;
|
const frameNum : number = state.frame;
|
||||||
const video : string = state.path;
|
const video : string = state.path;
|
||||||
const w : number = state.info.width;
|
const w : number = state.info.width;
|
||||||
const h : number = state.info.height;
|
const h : number = state.info.height;
|
||||||
const padded : string = this.padded_frame(frameNum);
|
const padded : string = this.padded_frame(frameNum);
|
||||||
let ext : string = 'tif';
|
let ext : string = 'png';
|
||||||
let rgb : any[] = light.color;
|
//let rgb : any[] = light.color;
|
||||||
let tmpoutput : string;
|
let tmpoutput : string;
|
||||||
let cmd : string;
|
let cmd : string;
|
||||||
let output : any;
|
let output : any;
|
||||||
let cmd2 : string;
|
//let cmd2 : string;
|
||||||
let output2 : any;
|
//let output2 : any;
|
||||||
|
let fileExists = false;
|
||||||
|
|
||||||
let scale : string = '';
|
let scale : string = '';
|
||||||
if (w && h) {
|
if (w && h) {
|
||||||
scale = `,scale=${w}:${h}`;
|
scale = `,scale=${w}:${h}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.dir(state)
|
tmpoutput = join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
||||||
|
|
||||||
//if (system.platform !== 'nix') {
|
try {
|
||||||
ext = 'png';
|
fileExists = await exists(tmpoutput);
|
||||||
//}
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
tmpoutput = join(this.TMPDIR, `export-${padded}.${ext}`);
|
if (fileExists) {
|
||||||
|
this.log.info(`File ${tmpoutput} exists`);
|
||||||
|
return tmpoutput;
|
||||||
|
}
|
||||||
|
|
||||||
rgb = rgb.map((e : string) => {
|
//rgb = rgb.map((e : string) => {
|
||||||
return parseInt(e);
|
// return parseInt(e);
|
||||||
});
|
//});
|
||||||
//
|
|
||||||
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`;
|
//
|
||||||
cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
cmd = `${this.bin} -y -i "${video}" -vf "select='gte(n\\,${frameNum})'${scale}" -vframes 1 -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
||||||
|
//cmd2 = `${this.convert} "${tmpoutput}" -resize ${w}x${h} -size ${w}x${h} xc:"rgb(${rgb[0]},${rgb[1]},${rgb[2]})" +swap -compose Darken -composite "${tmpoutput}"`;
|
||||||
|
|
||||||
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
//ffmpeg -i "${video}" -ss 00:00:07.000 -vframes 1 "export-${time}.jpg"
|
||||||
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "export-%05d.tiff"
|
||||||
|
@ -109,7 +120,7 @@ class FFMPEG {
|
||||||
}
|
}
|
||||||
if (output && output.stdout) this.log.info(`"${output.stdout}"`);
|
if (output && output.stdout) this.log.info(`"${output.stdout}"`);
|
||||||
|
|
||||||
if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
/*if (this.convert && (rgb[0] !== 255 || rgb[1] !== 255 || rgb[2] !== 255)) {
|
||||||
try {
|
try {
|
||||||
this.log.info(cmd2);
|
this.log.info(cmd2);
|
||||||
output2 = await exec(cmd2);
|
output2 = await exec(cmd2);
|
||||||
|
@ -118,8 +129,8 @@ class FFMPEG {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output2 && output2.stdout) this.log.info(`"${output2.stdout}"`);
|
if (output2 && output2.stdout) this.log.info(`"${output2.stdout}"`);*/
|
||||||
return tmpoutput
|
return tmpoutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -131,17 +142,23 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {?}
|
* @returns {?}
|
||||||
**/
|
**/
|
||||||
public async frames (video : string, obj : any) {
|
public async frames (state : FilmoutState) {
|
||||||
|
const video : string = state.path;
|
||||||
|
const w : number = state.info.width;
|
||||||
|
const h : number = state.info.height;
|
||||||
const tmppath : string = this.TMPDIR;
|
const tmppath : string = this.TMPDIR;
|
||||||
let ext : string = 'tif';
|
let ext : string = 'png';
|
||||||
let tmpoutput : string;
|
let tmpoutput : string = join(tmppath, `${state.hash}-export-%08d.${ext}`);
|
||||||
|
let cmd : string;
|
||||||
|
let output : any;
|
||||||
|
let scale : string = '';
|
||||||
|
|
||||||
//if (system.platform !== 'nix') {
|
if (w && h) {
|
||||||
ext = 'png';
|
scale = `scale=${w}:${h}`;
|
||||||
//}
|
}
|
||||||
|
|
||||||
tmpoutput = join(tmppath, `export-%08d.${ext}`);
|
|
||||||
|
|
||||||
|
cmd = `${this.bin} -y -i "${video}" -vf "${scale}" -compression_algo raw -pix_fmt rgb24 -crf 0 "${tmpoutput}"`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await mkdir(tmppath);
|
await mkdir(tmppath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -149,6 +166,16 @@ class FFMPEG {
|
||||||
}
|
}
|
||||||
|
|
||||||
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
//ffmpeg -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.log.info(cmd);
|
||||||
|
output = await exec(cmd);
|
||||||
|
} catch (err) {
|
||||||
|
this.log.error(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,19 +185,13 @@ class FFMPEG {
|
||||||
*
|
*
|
||||||
* @returns {boolean} True if successful, false if not
|
* @returns {boolean} True if successful, false if not
|
||||||
**/
|
**/
|
||||||
public async clear (frame : number) {
|
public async clear (state : any) {
|
||||||
const padded : string = this.padded_frame(frame);
|
const padded : string = this.padded_frame(state.frame);
|
||||||
let ext : string = 'tif';
|
let ext : string = 'png';
|
||||||
let tmppath : string;
|
let tmppath : string;
|
||||||
let tmpoutput : string;
|
|
||||||
let cmd : string;
|
|
||||||
let fileExists : boolean;
|
let fileExists : boolean;
|
||||||
|
|
||||||
//if (system.platform !== 'nix') {
|
tmppath = join(this.TMPDIR, `${state.hash}-export-${padded}.${ext}`);
|
||||||
ext = 'png';
|
|
||||||
//}
|
|
||||||
|
|
||||||
tmppath = join(this.TMPDIR, `export-${padded}.${ext}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fileExists = await exists(tmppath);
|
fileExists = await exists(tmppath);
|
||||||
|
@ -178,7 +199,7 @@ class FFMPEG {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exists) return false;
|
if (!fileExists) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await unlink(tmppath);
|
await unlink(tmppath);
|
||||||
|
@ -202,6 +223,12 @@ class FFMPEG {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.log.error(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
|
files = files.filter((file : string) => {
|
||||||
|
if (file.indexOf('-export-') !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
if (files) {
|
if (files) {
|
||||||
files.forEach(async (file : string, index : any) => {
|
files.forEach(async (file : string, index : any) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { default as animated } from 'animated-gif-detector';
|
||||||
import { extname } from 'path';
|
import { extname } from 'path';
|
||||||
import { readFile } from 'fs-extra';
|
import { readFile } from 'fs-extra';
|
||||||
import { delay } from 'delay';
|
import { delay } from 'delay';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module FilmOut
|
* @module FilmOut
|
||||||
|
@ -71,7 +72,16 @@ class FilmOut {
|
||||||
this.ipc.on('filmout_close', this.close.bind(this));
|
this.ipc.on('filmout_close', this.close.bind(this));
|
||||||
this.ipc.on('preview', this.preview.bind(this));
|
this.ipc.on('preview', this.preview.bind(this));
|
||||||
this.ipc.on('preview_frame', this.previewFrame.bind(this));
|
this.ipc.on('preview_frame', this.previewFrame.bind(this));
|
||||||
this.ipc.on('display', this.onDisplay.bind(this));
|
this.ipc.on('display', this.onDisplay.bind(this));
|
||||||
|
this.ipc.on('pre_export', this.onPreExport.bind(this));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a hash of a string.
|
||||||
|
*
|
||||||
|
* @param {string} data Data to produce hash of
|
||||||
|
*/
|
||||||
|
private hash (data : string) {
|
||||||
|
return createHash('sha1').update(data).digest('hex');
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets filmout direction.
|
* Sets filmout direction.
|
||||||
|
@ -102,21 +112,17 @@ class FilmOut {
|
||||||
/**
|
/**
|
||||||
* Begin the process of exporting single frames from the video for display.
|
* Begin the process of exporting single frames from the video for display.
|
||||||
**/
|
**/
|
||||||
async start () {
|
async start () {
|
||||||
|
let path;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.ffmpeg.clearAll();
|
path = await this.ffmpeg.frame(this.state, this.light.state);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
await this.display.show(path);
|
||||||
await this.ffmpeg.frame(this.state, this.light.state);
|
|
||||||
} catch (err) {
|
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
await this.display.show(this.state.frame);
|
|
||||||
await delay(20);
|
await delay(20);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -161,6 +167,13 @@ class FilmOut {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.ffmpeg.clearAll();
|
||||||
|
} catch (err) {
|
||||||
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.still) {
|
if (this.state.still) {
|
||||||
try {
|
try {
|
||||||
info = await this.stillInfo(arg.path);
|
info = await this.stillInfo(arg.path);
|
||||||
|
@ -196,6 +209,7 @@ class FilmOut {
|
||||||
this.state.fileName = arg.fileName;
|
this.state.fileName = arg.fileName;
|
||||||
this.state.frames = frames;
|
this.state.frames = frames;
|
||||||
this.state.info = info;
|
this.state.info = info;
|
||||||
|
this.state.hash = this.hash(arg.path);
|
||||||
|
|
||||||
this.log.info(`Opened ${this.state.fileName}`, 'FILMOUT', true, true);
|
this.log.info(`Opened ${this.state.fileName}`, 'FILMOUT', true, true);
|
||||||
this.log.info(`Frames : ${frames}`, 'FILMOUT', true, true);
|
this.log.info(`Frames : ${frames}`, 'FILMOUT', true, true);
|
||||||
|
@ -203,6 +217,26 @@ class FilmOut {
|
||||||
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) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-export all frames from video for display.
|
||||||
|
*
|
||||||
|
* @param {object} evt IPC event
|
||||||
|
* @param {object} arg IPC args
|
||||||
|
*/
|
||||||
|
async onPreExport (evt : Event, arg : any) {
|
||||||
|
if (!this.state.path) {
|
||||||
|
return await this.ui.send('pre_export', { complete : false, err : 'No file to pre export.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.ffmpeg.frames(this.state);
|
||||||
|
} catch (err) {
|
||||||
|
return await this.ui.send('pre_export', { complete : false, err });
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.ui.send('pre_export', { complete : true });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if gif is animated, false if it is a still
|
* Return true if gif is animated, false if it is a still
|
||||||
*
|
*
|
||||||
|
@ -248,10 +282,11 @@ class FilmOut {
|
||||||
this.log.error(err, 'FILMOUT', true, true);;
|
this.log.error(err, 'FILMOUT', true, true);;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ui.send('preview_frame', { path, frame : arg.frame })
|
this.ui.send('preview_frame', { path, frame : arg.frame })
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
* Open a single frame in a display window to preview filmout.
|
||||||
*
|
*
|
||||||
* @param {object} evt Original event
|
* @param {object} evt Original event
|
||||||
* @param {object} arg Arguments from message
|
* @param {object} arg Arguments from message
|
||||||
|
@ -263,6 +298,7 @@ class FilmOut {
|
||||||
state.frame = arg.frame;
|
state.frame = arg.frame;
|
||||||
|
|
||||||
this.log.info(`Previewing frame ${state.frame} of ${state.fileName}`);
|
this.log.info(`Previewing frame ${state.frame} of ${state.fileName}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
path = await this.ffmpeg.frame(state, { color : [255, 255, 255] });
|
path = await this.ffmpeg.frame(state, { color : [255, 255, 255] });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -272,7 +308,7 @@ class FilmOut {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.display.open();
|
await this.display.open();
|
||||||
await this.display.show(arg.frame);
|
await this.display.show(path);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.log.error(err, 'FILMOUT', true, true);
|
this.log.error(err, 'FILMOUT', true, true);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue