Switch to node-pty-prebuilt-multiarch and upgrade Electron
This commit is contained in:
parent
3679e17d68
commit
be7b1533d7
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120
|
||||
}
|
2
app.js
2
app.js
|
@ -41,7 +41,7 @@ di.require('progress', 'ProgressBar');
|
|||
di.require('gif-encoder', 'GIFEncoder');
|
||||
di.require('inquirer');
|
||||
|
||||
di.set('pty', require('@faressoft/node-pty-prebuilt'));
|
||||
di.set('pty', require('node-pty-prebuilt-multiarch'));
|
||||
di.set('PNG', require('pngjs').PNG);
|
||||
di.set('spawn', require('child_process').spawn);
|
||||
di.set('utility', require('./utility.js'));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Render
|
||||
* Render a recording file as an animated gif image
|
||||
*
|
||||
*
|
||||
* @author Mohammad Fares <faressoft.com@gmail.com>
|
||||
*/
|
||||
|
||||
|
@ -13,37 +13,40 @@
|
|||
* @return {ProgressBar}
|
||||
*/
|
||||
function getProgressBar(operation, framesCount) {
|
||||
|
||||
return new di.ProgressBar(operation + ' ' + di.chalk.magenta('frame :current/:total') + ' :percent [:bar] :etas', {
|
||||
width: 30,
|
||||
total: framesCount
|
||||
});
|
||||
|
||||
return new di.ProgressBar(
|
||||
operation +
|
||||
" " +
|
||||
di.chalk.magenta("frame :current/:total") +
|
||||
" :percent [:bar] :etas",
|
||||
{
|
||||
width: 30,
|
||||
total: framesCount,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the recording data into render/data.json
|
||||
*
|
||||
*
|
||||
* @param {Object} recordingFile
|
||||
* @return {Promise}
|
||||
*/
|
||||
function writeRecordingData(recordingFile) {
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Write the data into data.json file in the root path of the app
|
||||
di.fs.writeFile(di.path.join(ROOT_PATH, 'render/data.json'), JSON.stringify(recordingFile.json), 'utf8', function(error) {
|
||||
di.fs.writeFile(
|
||||
di.path.join(ROOT_PATH, "render/data.json"),
|
||||
JSON.stringify(recordingFile.json),
|
||||
"utf8",
|
||||
function (error) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return reject(error);
|
||||
resolve();
|
||||
}
|
||||
|
||||
resolve();
|
||||
|
||||
});
|
||||
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,80 +56,72 @@ function writeRecordingData(recordingFile) {
|
|||
* @return {Promise} resolve with the parsed PNG image
|
||||
*/
|
||||
function loadPNG(path) {
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
di.fs.readFile(path, function(error, imageData) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
di.fs.readFile(path, function (error, imageData) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
new di.PNG().parse(imageData, function(error, data) {
|
||||
|
||||
new di.PNG().parse(imageData, function (error, data) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dimensions of the first rendered frame
|
||||
*
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function getFrameDimensions() {
|
||||
|
||||
// The path of the first rendered frame
|
||||
var framePath = di.path.join(ROOT_PATH, 'render/frames/0.png');
|
||||
var framePath = di.path.join(ROOT_PATH, "render/frames/0.png");
|
||||
|
||||
// Read and parse a PNG image file
|
||||
return loadPNG(framePath).then(function(png) {
|
||||
|
||||
return({
|
||||
return loadPNG(framePath).then(function (png) {
|
||||
return {
|
||||
width: png.width,
|
||||
height: png.height
|
||||
});
|
||||
|
||||
height: png.height,
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the frames into PNG images
|
||||
*
|
||||
*
|
||||
* @param {Array} records [{delay, content}, ...]
|
||||
* @param {Object} options {step}
|
||||
* @return {Promise}
|
||||
*/
|
||||
function renderFrames(records, options) {
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
// The number of frames
|
||||
var framesCount = records.length;
|
||||
|
||||
// Create a progress bar
|
||||
var progressBar = getProgressBar('Rendering', Math.ceil(framesCount / options.step));
|
||||
var progressBar = getProgressBar(
|
||||
"Rendering",
|
||||
Math.ceil(framesCount / options.step)
|
||||
);
|
||||
|
||||
// Execute the rendering process
|
||||
var render = di.spawn(di.electron, [di.path.join(ROOT_PATH, 'render/index.js'), options.step], {detached: false});
|
||||
var render = di.spawn(
|
||||
di.electron,
|
||||
[di.path.join(ROOT_PATH, "render/index.js"), options.step],
|
||||
{ detached: false }
|
||||
);
|
||||
|
||||
render.stderr.on('data', function(error) {
|
||||
render.stderr.on("data", function (error) {
|
||||
render.kill();
|
||||
reject(new Error(error));
|
||||
});
|
||||
|
||||
render.stdout.on('data', function(data) {
|
||||
|
||||
render.stdout.on("data", function (data) {
|
||||
// Is not a recordIndex (to skip Electron's logs or new lines)
|
||||
if (di.is.not.number(parseInt(data.toString()))) {
|
||||
return;
|
||||
|
@ -138,25 +133,20 @@ function renderFrames(records, options) {
|
|||
if (progressBar.complete) {
|
||||
resolve();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the rendered frames into an animated GIF image
|
||||
*
|
||||
*
|
||||
* @param {Array} records [{delay, content}, ...]
|
||||
* @param {Object} options {quality, repeat, step, outputFile}
|
||||
* @param {Object} frameDimensions {width, height}
|
||||
* @return {Promise}
|
||||
*/
|
||||
function mergeFrames(records, options, frameDimensions) {
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
// The number of frames
|
||||
var framesCount = records.length;
|
||||
|
||||
|
@ -164,11 +154,14 @@ function mergeFrames(records, options, frameDimensions) {
|
|||
var stepsCounter = 0;
|
||||
|
||||
// Create a progress bar
|
||||
var progressBar = getProgressBar('Merging', Math.ceil(framesCount / options.step));
|
||||
var progressBar = getProgressBar(
|
||||
"Merging",
|
||||
Math.ceil(framesCount / options.step)
|
||||
);
|
||||
|
||||
// The gif image
|
||||
var gif = new di.GIFEncoder(frameDimensions.width, frameDimensions.height, {
|
||||
highWaterMark: 5 * 1024 * 1024
|
||||
highWaterMark: 5 * 1024 * 1024,
|
||||
});
|
||||
|
||||
// Pipe
|
||||
|
@ -184,101 +177,91 @@ function mergeFrames(records, options, frameDimensions) {
|
|||
gif.writeHeader();
|
||||
|
||||
// Foreach frame
|
||||
di.async.eachOfSeries(records, function(frame, index, callback) {
|
||||
di.async.eachOfSeries(
|
||||
records,
|
||||
function (frame, index, callback) {
|
||||
if (stepsCounter != 0) {
|
||||
stepsCounter = (stepsCounter + 1) % options.step;
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (stepsCounter != 0) {
|
||||
stepsCounter = (stepsCounter + 1) % options.step;
|
||||
return callback();
|
||||
|
||||
// The path of the rendered frame
|
||||
var framePath = di.path.join(
|
||||
ROOT_PATH,
|
||||
"render/frames",
|
||||
index + ".png"
|
||||
);
|
||||
|
||||
// Read and parse the rendered frame
|
||||
loadPNG(framePath)
|
||||
.then(function (png) {
|
||||
progressBar.tick();
|
||||
|
||||
// Set the duration (the delay of the next frame)
|
||||
// The % is used to take the delay of the first frame
|
||||
// as the duration of the last frame
|
||||
gif.setDelay(records[(index + 1) % framesCount].delay);
|
||||
|
||||
// Add frames
|
||||
gif.addFrame(png.data);
|
||||
|
||||
// Next
|
||||
callback();
|
||||
})
|
||||
.catch(function (error) {
|
||||
callback(error);
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// Write the footer
|
||||
gif.finish();
|
||||
resolve();
|
||||
}
|
||||
|
||||
stepsCounter = (stepsCounter + 1) % options.step;
|
||||
|
||||
// The path of the rendered frame
|
||||
var framePath = di.path.join(ROOT_PATH, 'render/frames', index + '.png');
|
||||
|
||||
// Read and parse the rendered frame
|
||||
loadPNG(framePath).then(function(png) {
|
||||
|
||||
progressBar.tick();
|
||||
|
||||
// Set the duration (the delay of the next frame)
|
||||
// The % is used to take the delay of the first frame
|
||||
// as the duration of the last frame
|
||||
gif.setDelay(records[(index + 1) % framesCount].delay);
|
||||
|
||||
// Add frames
|
||||
gif.addFrame(png.data);
|
||||
|
||||
// Next
|
||||
callback();
|
||||
|
||||
}).catch(function(error) {
|
||||
|
||||
callback(error);
|
||||
|
||||
});
|
||||
|
||||
}, function(error) {
|
||||
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// Write the footer
|
||||
gif.finish();
|
||||
resolve();
|
||||
|
||||
});
|
||||
|
||||
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the temporary rendered PNG images
|
||||
*
|
||||
* Delete the temporary rendered PNG images
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
function cleanup() {
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
di.fs.emptyDir(di.path.join(ROOT_PATH, 'render/frames'), function(error) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
di.fs.emptyDir(di.path.join(ROOT_PATH, "render/frames"), function (error) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed after the command completes its task
|
||||
*
|
||||
*
|
||||
* @param {String} outputFile the path of the rendered image
|
||||
*/
|
||||
function done(outputFile) {
|
||||
|
||||
console.log('\n' + di.chalk.green('Successfully Rendered'));
|
||||
console.log('The animated GIF image is saved into the file:');
|
||||
console.log("\n" + di.chalk.green("Successfully Rendered"));
|
||||
console.log("The animated GIF image is saved into the file:");
|
||||
console.log(di.chalk.magenta(outputFile));
|
||||
process.exit();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The command's main function
|
||||
*
|
||||
*
|
||||
* @param {Object} argv
|
||||
*/
|
||||
function command(argv) {
|
||||
|
||||
// Frames
|
||||
var records = argv.recordingFile.json.records;
|
||||
var config = argv.recordingFile.json.config;
|
||||
|
@ -287,17 +270,20 @@ function command(argv) {
|
|||
var framesCount = records.length;
|
||||
|
||||
// The path of the output file
|
||||
var outputFile = di.utility.resolveFilePath('render' + (new Date()).getTime(), 'gif');
|
||||
var outputFile = di.utility.resolveFilePath(
|
||||
"render" + new Date().getTime(),
|
||||
"gif"
|
||||
);
|
||||
|
||||
// For adjusting (calculating) the frames delays
|
||||
var adjustFramesDelaysOptions = {
|
||||
frameDelay: config.frameDelay,
|
||||
maxIdleTime: config.maxIdleTime
|
||||
maxIdleTime: config.maxIdleTime,
|
||||
};
|
||||
|
||||
// For rendering the frames into PNG images
|
||||
var renderingOptions = {
|
||||
step: argv.step
|
||||
step: argv.step,
|
||||
};
|
||||
|
||||
// For merging the rendered frames into an animated GIF image
|
||||
|
@ -305,7 +291,7 @@ function command(argv) {
|
|||
quality: config.quality,
|
||||
repeat: config.repeat,
|
||||
step: argv.step,
|
||||
outputFile: outputFile
|
||||
outputFile: outputFile,
|
||||
};
|
||||
|
||||
// Overwrite the quality of the rendered image
|
||||
|
@ -320,35 +306,37 @@ function command(argv) {
|
|||
}
|
||||
|
||||
// Tasks
|
||||
di.asyncPromises.waterfall([
|
||||
di.asyncPromises
|
||||
.waterfall([
|
||||
// Remove all previously rendered frames
|
||||
cleanup,
|
||||
|
||||
// Remove all previously rendered frames
|
||||
cleanup,
|
||||
// Write the recording data into render/data.json
|
||||
di._.partial(writeRecordingData, argv.recordingFile),
|
||||
|
||||
// Write the recording data into render/data.json
|
||||
di._.partial(writeRecordingData, argv.recordingFile),
|
||||
// Render the frames into PNG images
|
||||
di._.partial(renderFrames, records, renderingOptions),
|
||||
|
||||
// Render the frames into PNG images
|
||||
di._.partial(renderFrames, records, renderingOptions),
|
||||
// Adjust frames delays
|
||||
di._.partial(
|
||||
di.commands.play.adjustFramesDelays,
|
||||
records,
|
||||
adjustFramesDelaysOptions
|
||||
),
|
||||
|
||||
// Adjust frames delays
|
||||
di._.partial(di.commands.play.adjustFramesDelays, records, adjustFramesDelaysOptions),
|
||||
// Get the dimensions of the first rendered frame
|
||||
di._.partial(getFrameDimensions),
|
||||
|
||||
// Get the dimensions of the first rendered frame
|
||||
di._.partial(getFrameDimensions),
|
||||
|
||||
// Merge the rendered frames into an animated GIF image
|
||||
di._.partial(mergeFrames, records, mergingOptions),
|
||||
|
||||
// Delete the temporary rendered PNG images
|
||||
cleanup
|
||||
|
||||
]).then(function() {
|
||||
|
||||
done(outputFile);
|
||||
|
||||
}).catch(di.errorHandler);
|
||||
// Merge the rendered frames into an animated GIF image
|
||||
di._.partial(mergeFrames, records, mergingOptions),
|
||||
|
||||
// Delete the temporary rendered PNG images
|
||||
cleanup,
|
||||
])
|
||||
.then(function () {
|
||||
done(outputFile);
|
||||
})
|
||||
.catch(di.errorHandler);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
|
@ -359,13 +347,13 @@ function command(argv) {
|
|||
* Command's usage
|
||||
* @type {String}
|
||||
*/
|
||||
module.exports.command = 'render <recordingFile>';
|
||||
module.exports.command = "render <recordingFile>";
|
||||
|
||||
/**
|
||||
* Command's description
|
||||
* @type {String}
|
||||
*/
|
||||
module.exports.describe = 'Render a recording file as an animated gif image';
|
||||
module.exports.describe = "Render a recording file as an animated gif image";
|
||||
|
||||
/**
|
||||
* Command's handler function
|
||||
|
@ -375,42 +363,40 @@ module.exports.handler = command;
|
|||
|
||||
/**
|
||||
* Builder
|
||||
*
|
||||
*
|
||||
* @param {Object} yargs
|
||||
*/
|
||||
module.exports.builder = function(yargs) {
|
||||
|
||||
module.exports.builder = function (yargs) {
|
||||
// Define the recordingFile argument
|
||||
yargs.positional('recordingFile', {
|
||||
describe: 'The recording file',
|
||||
type: 'string',
|
||||
coerce: di.utility.loadYAML
|
||||
yargs.positional("recordingFile", {
|
||||
describe: "The recording file",
|
||||
type: "string",
|
||||
coerce: di.utility.loadYAML,
|
||||
});
|
||||
|
||||
// Define the output option
|
||||
yargs.option('o', {
|
||||
alias: 'output',
|
||||
type: 'string',
|
||||
describe: 'A name for the output file',
|
||||
yargs.option("o", {
|
||||
alias: "output",
|
||||
type: "string",
|
||||
describe: "A name for the output file",
|
||||
requiresArg: true,
|
||||
coerce: di._.partial(di.utility.resolveFilePath, di._, 'gif')
|
||||
coerce: di._.partial(di.utility.resolveFilePath, di._, "gif"),
|
||||
});
|
||||
|
||||
// Define the quality option
|
||||
yargs.option('q', {
|
||||
alias: 'quality',
|
||||
type: 'number',
|
||||
describe: 'The quality of the rendered image (1 - 100)',
|
||||
requiresArg: true
|
||||
yargs.option("q", {
|
||||
alias: "quality",
|
||||
type: "number",
|
||||
describe: "The quality of the rendered image (1 - 100)",
|
||||
requiresArg: true,
|
||||
});
|
||||
|
||||
// Define the quality option
|
||||
yargs.option('s', {
|
||||
alias: 'step',
|
||||
type: 'number',
|
||||
describe: 'To reduce the number of rendered frames (step > 1)',
|
||||
yargs.option("s", {
|
||||
alias: "step",
|
||||
type: "number",
|
||||
describe: "To reduce the number of rendered frames (step > 1)",
|
||||
requiresArg: true,
|
||||
default: 1
|
||||
default: 1,
|
||||
});
|
||||
|
||||
};
|
||||
|
|
16
package.json
16
package.json
|
@ -14,8 +14,8 @@
|
|||
"terminalizer": "bin/app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "NODE_ENV=development webpack --colors --watch",
|
||||
"build": "NODE_ENV=production webpack --optimize-minimize --progress --colors",
|
||||
"dev": "NODE_ENV=development webpack --watch",
|
||||
"build": "NODE_ENV=production webpack --progress",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -41,13 +41,12 @@
|
|||
"pty"
|
||||
],
|
||||
"dependencies": {
|
||||
"@faressoft/node-pty-prebuilt": "^0.9.0",
|
||||
"async": "^2.6.3",
|
||||
"async-promises": "^0.2.2",
|
||||
"chalk": "^2.4.2",
|
||||
"death": "^1.1.0",
|
||||
"deepmerge": "^2.2.1",
|
||||
"electron": "^15.5.5",
|
||||
"electron": "17",
|
||||
"flowa": "^4.0.2",
|
||||
"fs-extra": "^5.0.0",
|
||||
"gif-encoder": "^0.6.1",
|
||||
|
@ -55,6 +54,7 @@
|
|||
"is_js": "^0.9.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.15",
|
||||
"node-pty-prebuilt-multiarch": "^0.10.1-pre.5",
|
||||
"performance-now": "^2.1.0",
|
||||
"pngjs": "^3.4.0",
|
||||
"progress": "^2.0.3",
|
||||
|
@ -66,13 +66,13 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"ajv": "^6.12.0",
|
||||
"clean-webpack-plugin": "^0.1.19",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"jquery": "^3.4.1",
|
||||
"mini-css-extract-plugin": "^0.4.5",
|
||||
"mini-css-extract-plugin": "^2.6.1",
|
||||
"terminalizer-player": "^0.4.1",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"xterm": "^3.14.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
/**
|
||||
* Render the frames into PNG images
|
||||
* An electron app, takes one command line argument `step`
|
||||
*
|
||||
*
|
||||
* @author Mohammad Fares <faressoft.com@gmail.com>
|
||||
*/
|
||||
|
||||
var path = require('path'),
|
||||
app = require('electron').app,
|
||||
BrowserWindow = require('electron').BrowserWindow,
|
||||
ipcMain = require('electron').ipcMain,
|
||||
os = require('os');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { app } = require('electron');
|
||||
const { BrowserWindow } = require('electron');
|
||||
const ipcMain = require('electron').ipcMain;
|
||||
const os = require('os');
|
||||
|
||||
let mainWindow = null;
|
||||
|
||||
/**
|
||||
* The temporary rendering directory's path
|
||||
* @type {String}
|
||||
*/
|
||||
var renderDir = path.join(__dirname, 'frames');
|
||||
|
||||
/**
|
||||
* The step option
|
||||
* To reduce the number of rendered frames (step > 1)
|
||||
* @type {Number}
|
||||
*/
|
||||
global.step = process.argv[2] || 1;
|
||||
|
||||
/**
|
||||
* The temporary rendering directory's path
|
||||
* @type {String}
|
||||
*/
|
||||
global.renderDir = path.join(__dirname, 'frames');
|
||||
var step = process.argv[2] || 1;
|
||||
|
||||
// Hide the Dock for macOS
|
||||
if (os.platform() == 'darwin') {
|
||||
|
@ -36,33 +39,53 @@ app.on('ready', createWindow);
|
|||
* Create a hidden browser window and load the rendering page
|
||||
*/
|
||||
function createWindow() {
|
||||
|
||||
// Create a browser window
|
||||
var win = new BrowserWindow({
|
||||
mainWindow = new BrowserWindow({
|
||||
show: false,
|
||||
width: 8000,
|
||||
height: 8000,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
});
|
||||
|
||||
// Load index.html
|
||||
win.loadURL('file://' + __dirname + '/index.html');
|
||||
|
||||
// Load index.html
|
||||
mainWindow.loadURL('file://' + __dirname + '/index.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback function for the event:
|
||||
* When a frame is captured
|
||||
*
|
||||
* getOptions to request the options that need
|
||||
* to be passed to the renderer
|
||||
*
|
||||
* @param {Object} event
|
||||
* @param {Number} recordIndex
|
||||
*/
|
||||
ipcMain.on('captured', function(event, recordIndex) {
|
||||
ipcMain.handle('getOptions', function () {
|
||||
return { step };
|
||||
});
|
||||
|
||||
console.log(recordIndex);
|
||||
|
||||
/**
|
||||
* A callback function for the event:
|
||||
* capturePage
|
||||
*
|
||||
* @param {Object} event
|
||||
*/
|
||||
ipcMain.handle('capturePage', async function (event, captureRect, frameIndex) {
|
||||
const img = await mainWindow.webContents.capturePage(captureRect);
|
||||
const outputPath = path.join(renderDir, frameIndex + '.png');
|
||||
fs.writeFileSync(outputPath, img.toPNG());
|
||||
console.log(frameIndex);
|
||||
});
|
||||
|
||||
/**
|
||||
* A callback function for the event:
|
||||
* Close
|
||||
*
|
||||
* @param {Object} event
|
||||
* @param {String} error
|
||||
*/
|
||||
ipcMain.on('close', function (event, error) {
|
||||
mainWindow.close();
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -72,8 +95,6 @@ ipcMain.on('captured', function(event, recordIndex) {
|
|||
* @param {Object} event
|
||||
* @param {String} error
|
||||
*/
|
||||
ipcMain.on('error', function(event, error) {
|
||||
|
||||
ipcMain.on('error', function (event, error) {
|
||||
process.stderr.write(error);
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('app', {
|
||||
close() {
|
||||
return ipcRenderer.send('close');
|
||||
},
|
||||
getOptions() {
|
||||
return ipcRenderer.invoke('getOptions');
|
||||
},
|
||||
capturePage(captureRect, frameIndex) {
|
||||
console.log('prelaod > capturePage');
|
||||
return ipcRenderer.invoke('capturePage', captureRect, frameIndex);
|
||||
},
|
||||
});
|
||||
|
||||
// Catch all unhandled errors
|
||||
window.onerror = function (error) {
|
||||
ipcRenderer.send('error', error);
|
||||
};
|
|
@ -1,19 +1,11 @@
|
|||
/**
|
||||
* Terminalizer
|
||||
*
|
||||
*
|
||||
* @author Mohammad Fares <faressoft.com@gmail.com>
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
async = require('async'),
|
||||
remote = require('electron').remote,
|
||||
ipcRenderer = require('electron').ipcRenderer,
|
||||
terminalizerPlayer = require('terminalizer-player');
|
||||
var currentWindow = remote.getCurrentWindow(),
|
||||
capturePage = currentWindow.webContents.capturePage,
|
||||
step = remote.getGlobal('step'),
|
||||
renderDir = remote.getGlobal('renderDir');
|
||||
import async from 'async';
|
||||
import 'terminalizer-player';
|
||||
|
||||
// Styles
|
||||
import '../css/app.css';
|
||||
|
@ -26,119 +18,98 @@ import 'xterm/dist/xterm.css';
|
|||
*/
|
||||
var stepsCounter = 0;
|
||||
|
||||
/**
|
||||
* Rendering options
|
||||
*/
|
||||
var options = {};
|
||||
|
||||
/**
|
||||
* A callback function for the event:
|
||||
* When the document is loaded
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
|
||||
$(document).ready(async () => {
|
||||
options = await app.getOptions();
|
||||
|
||||
// Initialize the terminalizer plugin
|
||||
$('#terminal').terminalizer({
|
||||
recordingFile: 'data.json',
|
||||
autoplay: true,
|
||||
controls: false
|
||||
controls: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* A callback function for the event:
|
||||
* When the terminal playing is started
|
||||
*/
|
||||
$('#terminal').one('playingStarted', function() {
|
||||
|
||||
$('#terminal').one('playingStarted', function () {
|
||||
var terminalizer = $('#terminal').data('terminalizer');
|
||||
|
||||
// Pause the playing
|
||||
terminalizer.pause();
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* A callback function for the event:
|
||||
* When the terminal playing is paused
|
||||
*/
|
||||
$('#terminal').one('playingPaused', function() {
|
||||
|
||||
$('#terminal').one('playingPaused', function () {
|
||||
var terminalizer = $('#terminal').data('terminalizer');
|
||||
|
||||
// Reset the terminal
|
||||
terminalizer._terminal.reset();
|
||||
|
||||
// When the terminal's reset is done
|
||||
// When the terminal's reset is done
|
||||
$('#terminal').one('rendered', render);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Render each frame and capture it
|
||||
*/
|
||||
function render() {
|
||||
|
||||
var terminalizer = $('#terminal').data('terminalizer');
|
||||
var framesCount = terminalizer.getFramesCount();
|
||||
|
||||
// Foreach frame
|
||||
async.timesSeries(framesCount, function(frameIndex, next) {
|
||||
async.timesSeries(
|
||||
framesCount,
|
||||
function (frameIndex, next) {
|
||||
terminalizer._renderFrame(frameIndex, true, function () {
|
||||
capture(frameIndex, next);
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
terminalizer._renderFrame(frameIndex, true, function() {
|
||||
capture(frameIndex, next);
|
||||
});
|
||||
|
||||
}, function(error) {
|
||||
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
app.close();
|
||||
}
|
||||
|
||||
currentWindow.close();
|
||||
|
||||
});
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the current frame
|
||||
*
|
||||
*
|
||||
* @param {Number} frameIndex
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function capture(frameIndex, callback) {
|
||||
|
||||
var width = $('#terminal').width();
|
||||
var height = $('#terminal').height();
|
||||
var captureRect = {x: 0, y: 0, width: width, height: height};
|
||||
var captureRect = { x: 0, y: 0, width: width, height: height };
|
||||
|
||||
if (stepsCounter != 0) {
|
||||
stepsCounter = (stepsCounter + 1) % step;
|
||||
stepsCounter = (stepsCounter + 1) % options.step;
|
||||
return callback();
|
||||
}
|
||||
|
||||
stepsCounter = (stepsCounter + 1) % step;
|
||||
|
||||
capturePage(captureRect).then((img) => {
|
||||
|
||||
var outputPath = path.join(renderDir, frameIndex + '.png');
|
||||
|
||||
fs.writeFileSync(outputPath, img.toPNG());
|
||||
ipcRenderer.send('captured', frameIndex);
|
||||
callback();
|
||||
|
||||
}).catch((err) => {
|
||||
|
||||
throw new err;
|
||||
|
||||
});
|
||||
stepsCounter = (stepsCounter + 1) % options.step;
|
||||
|
||||
app
|
||||
.capturePage(captureRect, frameIndex)
|
||||
.then(callback)
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch all unhandled errors
|
||||
*
|
||||
* @param {String} error
|
||||
*/
|
||||
window.onerror = function(error) {
|
||||
|
||||
ipcRenderer.send('error', error);
|
||||
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@ const path = require('path');
|
|||
|
||||
// Extract CSS into separate files
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
// Global variables
|
||||
const globals = {
|
||||
|
@ -11,36 +11,35 @@ const globals = {
|
|||
jQuery: 'jquery',
|
||||
Terminal: ['xterm', 'Terminal'],
|
||||
'window.jQuery': 'jquery',
|
||||
'window.$': 'jquery'
|
||||
'window.$': 'jquery',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
target: 'electron-renderer',
|
||||
entry: {
|
||||
app: './render/src/js/app.js'
|
||||
app: './render/src/js/app.js',
|
||||
},
|
||||
output: {
|
||||
filename: 'js/[name].js',
|
||||
path: path.resolve(__dirname, 'render/dist'),
|
||||
publicPath: '/dist/'
|
||||
publicPath: '/dist/',
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(['./render/dist'], {verbose: false}),
|
||||
new CleanWebpackPlugin({
|
||||
cleanBeforeEveryBuildPatterns: [path.join(__dirname, 'render/dist')],
|
||||
}),
|
||||
new webpack.ProvidePlugin(globals),
|
||||
new MiniCssExtractPlugin({filename: 'css/[name].css'}),
|
||||
new webpack.NoEmitOnErrorsPlugin()
|
||||
new MiniCssExtractPlugin({ filename: 'css/[name].css' }),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
// CSS
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{loader: MiniCssExtractPlugin.loader},
|
||||
{loader: 'css-loader'}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
use: [{ loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue