Compare commits
11 Commits
Author | SHA1 | Date |
---|---|---|
Matt McWilliams | 43169174a9 | |
Matt McWilliams | 4dbdc4e3da | |
Matt McWilliams | 5da0bcec11 | |
Matt McWilliams | 1ceeaad33a | |
Matt McWilliams | 6c12d4961c | |
Matt McWilliams | f569a27e32 | |
Matt McWilliams | d2b27620ca | |
Matt McWilliams | 54057021a4 | |
Matt McWilliams | c0fcdd62da | |
Matt McWilliams | f4e799d647 | |
Matt McWilliams | 501bcf8603 |
26
README.md
26
README.md
|
@ -4,6 +4,8 @@ Node script to generate flicker videos by interweaving frames from multiple vide
|
||||||
|
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
## Git URL [git.sixteenmillimeter.com/16mm/frameloom](https://git.sixteenmillimeter.com/16mm/frameloom)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
This script relies on `ffmpeg` to export and stitch video back together
|
This script relies on `ffmpeg` to export and stitch video back together
|
||||||
|
@ -21,13 +23,15 @@ chmod +x frameloom
|
||||||
|
|
||||||
## Basic Usage
|
## Basic Usage
|
||||||
|
|
||||||
```./frameloom -i /path/to/video1:/path/to/video2 -o /path/to/output```
|
```bash
|
||||||
|
./frameloom -i /path/to/video1:/path/to/video2 -o /path/to/output
|
||||||
|
```
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
Run `./frameloom -h` to display help screen.
|
Run `./frameloom -h` to display help screen.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
Usage: frameloom [options]
|
Usage: frameloom [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
@ -39,11 +43,19 @@ Options:
|
||||||
-t, --tmp [dir] Specify tmp directory for exporting frames
|
-t, --tmp [dir] Specify tmp directory for exporting frames
|
||||||
-a, --avconv Specify avconv if preferred to ffmpeg
|
-a, --avconv Specify avconv if preferred to ffmpeg
|
||||||
-R, --random Randomize frames. Ignores pattern if included
|
-R, --random Randomize frames. Ignores pattern if included
|
||||||
-h, --help output usage information
|
-s, --spin Randomly rotate frames before rendering
|
||||||
|
-e, --exec Command to execute on every frame. Specify {{i}} and {{o}} if the command requires
|
||||||
|
it, otherwise frame path will be appended to command
|
||||||
|
-q, --quiet Suppresses all log messages
|
||||||
|
-h, --help display help for command
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO
|
## License
|
||||||
|
|
||||||
* Generate example videos automatically
|
Copyright 2018-2021 M McWilliams
|
||||||
* Publish example videos
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -8,6 +8,10 @@
|
||||||
<dt><a href="#delay">delay(ms)</a> ⇒ <code>Promise</code></dt>
|
<dt><a href="#delay">delay(ms)</a> ⇒ <code>Promise</code></dt>
|
||||||
<dd><p>Delays process for specified amount of time in milliseconds.</p>
|
<dd><p>Delays process for specified amount of time in milliseconds.</p>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><a href="#log">log()</a></dt>
|
||||||
|
<dd><p>Log function wrapper that can silences logs when
|
||||||
|
QUIET == true</p>
|
||||||
|
</dd>
|
||||||
<dt><a href="#zeroPad">zeroPad(i, max)</a> ⇒ <code>string</code></dt>
|
<dt><a href="#zeroPad">zeroPad(i, max)</a> ⇒ <code>string</code></dt>
|
||||||
<dd><p>Pads a numerical value with preceding zeros to make strings same length.</p>
|
<dd><p>Pads a numerical value with preceding zeros to make strings same length.</p>
|
||||||
</dd>
|
</dd>
|
||||||
|
@ -15,15 +19,18 @@
|
||||||
<dd><p>Shuffles an array into a random state.</p>
|
<dd><p>Shuffles an array into a random state.</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#clear">clear()</a></dt>
|
<dt><a href="#clear">clear()</a></dt>
|
||||||
<dd><p>Clears the temporary directory of all files.
|
<dd><p>Clears the temporary directory of all files.
|
||||||
Establishes a directory if none exists.</p>
|
Establishes a directory if none exists.</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#frames">frames(video, order)</a> ⇒ <code>string</code></dt>
|
<dt><a href="#frames">frames(video, order, avconv)</a> ⇒ <code>string</code></dt>
|
||||||
<dd><p>Exports all frames from video. Appends number to the string
|
<dd><p>Exports all frames from video. Appends number to the string
|
||||||
to keep frames in alternating order to be quickly stitched together
|
to keep frames in alternating order to be quickly stitched together
|
||||||
or re-sorted.</p>
|
or re-sorted.</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#weave">weave(pattern, realtime)</a></dt>
|
<dt><a href="#subExec">subExec(cmd)</a></dt>
|
||||||
|
<dd><p>Shells out to run a sub command on every frame to perform effects</p>
|
||||||
|
</dd>
|
||||||
|
<dt><a href="#weave">weave(pattern, realtime, random)</a></dt>
|
||||||
<dd><p>Re-arranges the frames into the order specified in the pattern.
|
<dd><p>Re-arranges the frames into the order specified in the pattern.
|
||||||
Calls <code>patternSort()</code> to perform the rename and unlink actions</p>
|
Calls <code>patternSort()</code> to perform the rename and unlink actions</p>
|
||||||
</dd>
|
</dd>
|
||||||
|
@ -36,7 +43,7 @@
|
||||||
<dt><a href="#randomSort">randomSort(list, pattern, realtime)</a></dt>
|
<dt><a href="#randomSort">randomSort(list, pattern, realtime)</a></dt>
|
||||||
<dd><p>Ramdomly sort frames for re-stitching.</p>
|
<dd><p>Ramdomly sort frames for re-stitching.</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#render">render(output)</a></dt>
|
<dt><a href="#render">render(output, avconv)</a></dt>
|
||||||
<dd><p>Render the frames into a video using ffmpeg.</p>
|
<dd><p>Render the frames into a video using ffmpeg.</p>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="#main">main(arg)</a></dt>
|
<dt><a href="#main">main(arg)</a></dt>
|
||||||
|
@ -70,6 +77,13 @@ Delays process for specified amount of time in milliseconds.
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| ms | <code>integer</code> | Milliseconds to delay for |
|
| ms | <code>integer</code> | Milliseconds to delay for |
|
||||||
|
|
||||||
|
<a name="log"></a>
|
||||||
|
|
||||||
|
## log()
|
||||||
|
Log function wrapper that can silences logs when
|
||||||
|
QUIET == true
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
<a name="zeroPad"></a>
|
<a name="zeroPad"></a>
|
||||||
|
|
||||||
## zeroPad(i, max) ⇒ <code>string</code>
|
## zeroPad(i, max) ⇒ <code>string</code>
|
||||||
|
@ -97,13 +111,13 @@ Shuffles an array into a random state.
|
||||||
<a name="clear"></a>
|
<a name="clear"></a>
|
||||||
|
|
||||||
## clear()
|
## clear()
|
||||||
Clears the temporary directory of all files.
|
Clears the temporary directory of all files.
|
||||||
Establishes a directory if none exists.
|
Establishes a directory if none exists.
|
||||||
|
|
||||||
**Kind**: global function
|
**Kind**: global function
|
||||||
<a name="frames"></a>
|
<a name="frames"></a>
|
||||||
|
|
||||||
## frames(video, order) ⇒ <code>string</code>
|
## frames(video, order, avconv) ⇒ <code>string</code>
|
||||||
Exports all frames from video. Appends number to the string
|
Exports all frames from video. Appends number to the string
|
||||||
to keep frames in alternating order to be quickly stitched together
|
to keep frames in alternating order to be quickly stitched together
|
||||||
or re-sorted.
|
or re-sorted.
|
||||||
|
@ -115,10 +129,22 @@ Exports all frames from video. Appends number to the string
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| video | <code>string</code> | String representing path to video |
|
| video | <code>string</code> | String representing path to video |
|
||||||
| order | <code>integer</code> | Integer to be appended to pathname of file |
|
| order | <code>integer</code> | Integer to be appended to pathname of file |
|
||||||
|
| avconv | <code>boolean</code> | Whether or not to use avconv instead of ffmpeg |
|
||||||
|
|
||||||
|
<a name="subExec"></a>
|
||||||
|
|
||||||
|
## subExec(cmd)
|
||||||
|
Shells out to run a sub command on every frame to perform effects
|
||||||
|
|
||||||
|
**Kind**: global function
|
||||||
|
|
||||||
|
| Param | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| cmd | <code>string</code> | Command to execute on every frame |
|
||||||
|
|
||||||
<a name="weave"></a>
|
<a name="weave"></a>
|
||||||
|
|
||||||
## weave(pattern, realtime)
|
## weave(pattern, realtime, random)
|
||||||
Re-arranges the frames into the order specified in the pattern.
|
Re-arranges the frames into the order specified in the pattern.
|
||||||
Calls `patternSort()` to perform the rename and unlink actions
|
Calls `patternSort()` to perform the rename and unlink actions
|
||||||
|
|
||||||
|
@ -128,6 +154,7 @@ Re-arranges the frames into the order specified in the pattern.
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| pattern | <code>array</code> | Pattern of the frames per input |
|
| pattern | <code>array</code> | Pattern of the frames per input |
|
||||||
| realtime | <code>boolean</code> | Flag to turn on or off realtime behavior (drop frames / number of vids) |
|
| realtime | <code>boolean</code> | Flag to turn on or off realtime behavior (drop frames / number of vids) |
|
||||||
|
| random | <code>boolean</code> | Whether or not to randomize frames |
|
||||||
|
|
||||||
<a name="altSort"></a>
|
<a name="altSort"></a>
|
||||||
|
|
||||||
|
@ -170,7 +197,7 @@ Ramdomly sort frames for re-stitching.
|
||||||
|
|
||||||
<a name="render"></a>
|
<a name="render"></a>
|
||||||
|
|
||||||
## render(output)
|
## render(output, avconv)
|
||||||
Render the frames into a video using ffmpeg.
|
Render the frames into a video using ffmpeg.
|
||||||
|
|
||||||
**Kind**: global function
|
**Kind**: global function
|
||||||
|
@ -178,6 +205,7 @@ Render the frames into a video using ffmpeg.
|
||||||
| Param | Type | Description |
|
| Param | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| output | <code>string</code> | Path to export the video to |
|
| output | <code>string</code> | Path to export the video to |
|
||||||
|
| avconv | <code>boolean</code> | Whether or not to use avconv in place of ffmpeg |
|
||||||
|
|
||||||
<a name="main"></a>
|
<a name="main"></a>
|
||||||
|
|
||||||
|
|
79
frameloom
79
frameloom
|
@ -1,15 +1,15 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
'use strict';
|
||||||
const execRaw = require('child_process').exec;
|
const execRaw = require('child_process').exec;
|
||||||
const os = require('os');
|
const { tmpdir } = require('os');
|
||||||
const path = require('path');
|
const { join, extname } = require('path');
|
||||||
const program = require('commander');
|
const program = require('commander');
|
||||||
const fs = require('fs-extra');
|
const { move, exists, unlink, readdir, mkdir } = require('fs-extra');
|
||||||
const pkg = require('./package.json');
|
const { version } = require('./package.json');
|
||||||
const OUTPUT_RE = new RegExp('{{o}}', 'g');
|
const OUTPUT_RE = new RegExp('{{o}}', 'g');
|
||||||
const INPUT_RE = new RegExp('{{i}}', 'g');
|
const INPUT_RE = new RegExp('{{i}}', 'g');
|
||||||
let QUIET = false;
|
let QUIET = false;
|
||||||
let TMPDIR = os.tmpdir() || '/tmp';
|
let TMPDIR = tmpdir() || '/tmp';
|
||||||
let TMPPATH;
|
let TMPPATH;
|
||||||
/**
|
/**
|
||||||
* Shells out to execute a command with async/await.
|
* Shells out to execute a command with async/await.
|
||||||
|
@ -97,14 +97,14 @@ function randomInt(min, max) {
|
||||||
**/
|
**/
|
||||||
async function clear() {
|
async function clear() {
|
||||||
let cmd = `rm -r "${TMPPATH}"`;
|
let cmd = `rm -r "${TMPPATH}"`;
|
||||||
let exists;
|
let dirExists;
|
||||||
try {
|
try {
|
||||||
exists = await fs.exists(TMPPATH);
|
dirExists = await exists(TMPPATH);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log('Error checking if file exists', err);
|
log('Error checking if file exists', err);
|
||||||
}
|
}
|
||||||
if (exists) {
|
if (dirExists) {
|
||||||
log(`Clearing temp directory "${TMPPATH}"`);
|
log(`Clearing temp directory "${TMPPATH}"`);
|
||||||
try {
|
try {
|
||||||
await exec(cmd);
|
await exec(cmd);
|
||||||
|
@ -115,7 +115,7 @@ async function clear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await fs.mkdir(TMPPATH);
|
await mkdir(TMPPATH);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
if (err.code !== 'EEXIST') {
|
if (err.code !== 'EEXIST') {
|
||||||
|
@ -140,7 +140,7 @@ async function frames(video, order, avconv) {
|
||||||
let exe = avconv ? 'avconv' : 'ffmpeg';
|
let exe = avconv ? 'avconv' : 'ffmpeg';
|
||||||
let tmpoutput;
|
let tmpoutput;
|
||||||
let cmd;
|
let cmd;
|
||||||
tmpoutput = path.join(TMPPATH, `export-%05d_${order}.${ext}`);
|
tmpoutput = join(TMPPATH, `export-%05d_${order}.${ext}`);
|
||||||
cmd = `${exe} -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`;
|
cmd = `${exe} -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`;
|
||||||
log(`Exporting ${video} as single frames...`);
|
log(`Exporting ${video} as single frames...`);
|
||||||
try {
|
try {
|
||||||
|
@ -150,7 +150,7 @@ async function frames(video, order, avconv) {
|
||||||
log('Error exporting video', err);
|
log('Error exporting video', err);
|
||||||
return process.exit(3);
|
return process.exit(3);
|
||||||
}
|
}
|
||||||
return path.join(TMPPATH, `export-%05d_${order}`);
|
return join(TMPPATH, `export-%05d_${order}`);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Shells out to run a sub command on every frame to perform effects
|
* Shells out to run a sub command on every frame to perform effects
|
||||||
|
@ -163,7 +163,7 @@ async function subExec(cmd) {
|
||||||
let frameCmd;
|
let frameCmd;
|
||||||
let framePath;
|
let framePath;
|
||||||
try {
|
try {
|
||||||
frames = await fs.readdir(TMPPATH);
|
frames = await readdir(TMPPATH);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log('Error reading tmp directory', err);
|
log('Error reading tmp directory', err);
|
||||||
|
@ -173,7 +173,7 @@ async function subExec(cmd) {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
for (let frame of frames) {
|
for (let frame of frames) {
|
||||||
framePath = path.join(TMPPATH, frame);
|
framePath = join(TMPPATH, frame);
|
||||||
if (cmd.indexOf('{{i}}') !== -1 || cmd.indexOf('{{o}}')) {
|
if (cmd.indexOf('{{i}}') !== -1 || cmd.indexOf('{{o}}')) {
|
||||||
frameCmd = cmd.replace(INPUT_RE, framePath)
|
frameCmd = cmd.replace(INPUT_RE, framePath)
|
||||||
.replace(OUTPUT_RE, framePath);
|
.replace(OUTPUT_RE, framePath);
|
||||||
|
@ -204,7 +204,7 @@ async function weave(pattern, realtime, random) {
|
||||||
let alt = false;
|
let alt = false;
|
||||||
log('Weaving frames...');
|
log('Weaving frames...');
|
||||||
try {
|
try {
|
||||||
frames = await fs.readdir(TMPPATH);
|
frames = await readdir(TMPPATH);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log('Error reading tmp directory', err);
|
log('Error reading tmp directory', err);
|
||||||
|
@ -268,7 +268,7 @@ async function altSort(list, pattern, realtime) {
|
||||||
let oldPath;
|
let oldPath;
|
||||||
let newName;
|
let newName;
|
||||||
let newPath;
|
let newPath;
|
||||||
let ext = path.extname(list[0]);
|
let ext = extname(list[0]);
|
||||||
let x;
|
let x;
|
||||||
let i;
|
let i;
|
||||||
for (x = 0; x < pattern.length; x++) {
|
for (x = 0; x < pattern.length; x++) {
|
||||||
|
@ -298,12 +298,12 @@ async function altSort(list, pattern, realtime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
oldName = String(groups[patternIndexes[i]][0]);
|
oldName = String(groups[patternIndexes[i]][0]);
|
||||||
oldPath = path.join(TMPPATH, oldName);
|
oldPath = join(TMPPATH, oldName);
|
||||||
groups[patternIndexes[i]].shift();
|
groups[patternIndexes[i]].shift();
|
||||||
if (skip) {
|
if (skip) {
|
||||||
log(`Skipping ${oldName}`);
|
log(`Skipping ${oldName}`);
|
||||||
try {
|
try {
|
||||||
await fs.unlink(oldPath);
|
await unlink(oldPath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log('Error deleting frame', err);
|
log('Error deleting frame', err);
|
||||||
|
@ -311,10 +311,10 @@ async function altSort(list, pattern, realtime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
newName = `./render_${zeroPad(frameCount)}${ext}`;
|
newName = `./render_${zeroPad(frameCount)}${ext}`;
|
||||||
newPath = path.join(TMPPATH, newName);
|
newPath = join(TMPPATH, newName);
|
||||||
log(`Renaming ${oldName} -> ${newName}`);
|
log(`Renaming ${oldName} -> ${newName}`);
|
||||||
try {
|
try {
|
||||||
await fs.move(oldPath, newPath);
|
await move(oldPath, newPath);
|
||||||
newList.push(newName);
|
newList.push(newName);
|
||||||
frameCount++;
|
frameCount++;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ async function standardSort(list, pattern, realtime) {
|
||||||
let step;
|
let step;
|
||||||
let skipCount;
|
let skipCount;
|
||||||
let skip;
|
let skip;
|
||||||
let ext = path.extname(list[0]);
|
let ext = extname(list[0]);
|
||||||
let oldPath;
|
let oldPath;
|
||||||
let newName;
|
let newName;
|
||||||
let newPath;
|
let newPath;
|
||||||
|
@ -356,11 +356,11 @@ async function standardSort(list, pattern, realtime) {
|
||||||
skipCount = pattern.length;
|
skipCount = pattern.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
oldPath = path.join(TMPPATH, list[i]);
|
oldPath = join(TMPPATH, list[i]);
|
||||||
if (skip) {
|
if (skip) {
|
||||||
log(`Skipping ${list[i]}`);
|
log(`Skipping ${list[i]}`);
|
||||||
try {
|
try {
|
||||||
await fs.unlink(oldPath);
|
await unlink(oldPath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log('Error deleting frame', err);
|
log('Error deleting frame', err);
|
||||||
|
@ -368,10 +368,10 @@ async function standardSort(list, pattern, realtime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
newName = `./render_${zeroPad(frameCount)}${ext}`;
|
newName = `./render_${zeroPad(frameCount)}${ext}`;
|
||||||
newPath = path.join(TMPPATH, newName);
|
newPath = join(TMPPATH, newName);
|
||||||
log(`Renaming ${list[i]} -> ${newName}`);
|
log(`Renaming ${list[i]} -> ${newName}`);
|
||||||
try {
|
try {
|
||||||
await fs.move(oldPath, newPath);
|
await move(oldPath, newPath);
|
||||||
newList.push(newName);
|
newList.push(newName);
|
||||||
frameCount++;
|
frameCount++;
|
||||||
}
|
}
|
||||||
|
@ -391,7 +391,7 @@ async function standardSort(list, pattern, realtime) {
|
||||||
**/
|
**/
|
||||||
async function randomSort(list, pattern, realtime) {
|
async function randomSort(list, pattern, realtime) {
|
||||||
let frameCount = 0;
|
let frameCount = 0;
|
||||||
let ext = path.extname(list[0]);
|
let ext = extname(list[0]);
|
||||||
let oldPath;
|
let oldPath;
|
||||||
let newName;
|
let newName;
|
||||||
let newPath;
|
let newPath;
|
||||||
|
@ -405,10 +405,10 @@ async function randomSort(list, pattern, realtime) {
|
||||||
list = list.slice(0, removeLen);
|
list = list.slice(0, removeLen);
|
||||||
log(`Skipping extra frames...`);
|
log(`Skipping extra frames...`);
|
||||||
for (let i = 0; i < remove.length; i++) {
|
for (let i = 0; i < remove.length; i++) {
|
||||||
oldPath = path.join(TMPPATH, remove[i]);
|
oldPath = join(TMPPATH, remove[i]);
|
||||||
log(`Skipping ${list[i]}`);
|
log(`Skipping ${list[i]}`);
|
||||||
try {
|
try {
|
||||||
await fs.unlink(oldPath);
|
await unlink(oldPath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log('Error deleting frame', err);
|
log('Error deleting frame', err);
|
||||||
|
@ -416,12 +416,12 @@ async function randomSort(list, pattern, realtime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
oldPath = path.join(TMPPATH, list[i]);
|
oldPath = join(TMPPATH, list[i]);
|
||||||
newName = `./render_${zeroPad(frameCount)}${ext}`;
|
newName = `./render_${zeroPad(frameCount)}${ext}`;
|
||||||
newPath = path.join(TMPPATH, newName);
|
newPath = join(TMPPATH, newName);
|
||||||
log(`Renaming ${list[i]} -> ${newName}`);
|
log(`Renaming ${list[i]} -> ${newName}`);
|
||||||
try {
|
try {
|
||||||
await fs.move(oldPath, newPath);
|
await move(oldPath, newPath);
|
||||||
newList.push(newName);
|
newList.push(newName);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
@ -440,7 +440,7 @@ async function spinFrames() {
|
||||||
let rotate;
|
let rotate;
|
||||||
console.log('Spinning frames...');
|
console.log('Spinning frames...');
|
||||||
try {
|
try {
|
||||||
frames = await fs.readdir(TMPPATH);
|
frames = await readdir(TMPPATH);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error('Error reading tmp directory', err);
|
console.error('Error reading tmp directory', err);
|
||||||
|
@ -451,7 +451,7 @@ async function spinFrames() {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
for (let frame of frames) {
|
for (let frame of frames) {
|
||||||
framePath = path.join(TMPPATH, frame);
|
framePath = join(TMPPATH, frame);
|
||||||
rotate = '';
|
rotate = '';
|
||||||
flip = '';
|
flip = '';
|
||||||
flop = '';
|
flop = '';
|
||||||
|
@ -487,7 +487,7 @@ async function spinFrames() {
|
||||||
**/
|
**/
|
||||||
async function render(output, avconv) {
|
async function render(output, avconv) {
|
||||||
//process.exit()
|
//process.exit()
|
||||||
let frames = path.join(TMPPATH, `render_%05d.tif`);
|
let frames = join(TMPPATH, `render_%05d.tif`);
|
||||||
let exe = avconv ? 'avconv' : 'ffmpeg';
|
let exe = avconv ? 'avconv' : 'ffmpeg';
|
||||||
let resolution = '1920x1080'; //TODO: make variable/argument
|
let resolution = '1920x1080'; //TODO: make variable/argument
|
||||||
//TODO: make object configurable with shorthand names
|
//TODO: make object configurable with shorthand names
|
||||||
|
@ -512,7 +512,8 @@ async function render(output, avconv) {
|
||||||
*
|
*
|
||||||
* @param {object} arg Object containing all arguments
|
* @param {object} arg Object containing all arguments
|
||||||
**/
|
**/
|
||||||
async function main(arg) {
|
async function main(program) {
|
||||||
|
const arg = program.opts();
|
||||||
let input = arg.input.split(':');
|
let input = arg.input.split(':');
|
||||||
let output = arg.output;
|
let output = arg.output;
|
||||||
let pattern = [];
|
let pattern = [];
|
||||||
|
@ -521,7 +522,7 @@ async function main(arg) {
|
||||||
let random = false;
|
let random = false;
|
||||||
let e = false;
|
let e = false;
|
||||||
let exe = arg.avconv ? 'avconv' : 'ffmpeg';
|
let exe = arg.avconv ? 'avconv' : 'ffmpeg';
|
||||||
let exists;
|
let fileExists;
|
||||||
console.time('frameloom');
|
console.time('frameloom');
|
||||||
if (input.length < 2) {
|
if (input.length < 2) {
|
||||||
log('Must provide more than 1 input', {});
|
log('Must provide more than 1 input', {});
|
||||||
|
@ -558,13 +559,13 @@ async function main(arg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
exists = await exec(`which ${exe}`);
|
fileExists = await exec(`which ${exe}`);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
log(`Error checking for ${exe}`);
|
log(`Error checking for ${exe}`);
|
||||||
process.exit(11);
|
process.exit(11);
|
||||||
}
|
}
|
||||||
if (!exists || exists === '' || exists.indexOf(exe) === -1) {
|
if (!fileExists || fileExists === '' || fileExists.indexOf(exe) === -1) {
|
||||||
log(`${exe} is required and is not installed. Please install ${exe} to use frameloom.`);
|
log(`${exe} is required and is not installed. Please install ${exe} to use frameloom.`);
|
||||||
process.exit(12);
|
process.exit(12);
|
||||||
}
|
}
|
||||||
|
@ -574,7 +575,7 @@ async function main(arg) {
|
||||||
}
|
}
|
||||||
if (arg.realtime)
|
if (arg.realtime)
|
||||||
realtime = true;
|
realtime = true;
|
||||||
TMPPATH = path.join(TMPDIR, 'frameloom');
|
TMPPATH = join(TMPDIR, 'frameloom');
|
||||||
try {
|
try {
|
||||||
await clear();
|
await clear();
|
||||||
}
|
}
|
||||||
|
@ -634,7 +635,7 @@ async function main(arg) {
|
||||||
console.timeEnd('frameloom');
|
console.timeEnd('frameloom');
|
||||||
}
|
}
|
||||||
program
|
program
|
||||||
.version(pkg.version)
|
.version(version)
|
||||||
.option('-i, --input [files]', 'Specify input videos with paths seperated by colon')
|
.option('-i, --input [files]', 'Specify input videos with paths seperated by colon')
|
||||||
.option('-o, --output [file]', 'Specify output path of video')
|
.option('-o, --output [file]', 'Specify output path of video')
|
||||||
.option('-p, --pattern [pattern]', 'Specify a pattern for the flicker 1:1 is standard')
|
.option('-p, --pattern [pattern]', 'Specify a pattern for the flicker 1:1 is standard')
|
||||||
|
|
35
frameloom.sh
35
frameloom.sh
|
@ -1,35 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Simple shell script version of frameloom
|
|
||||||
# Only creates a 1:1 pattern between 2 videos
|
|
||||||
|
|
||||||
# Usage : sh frameloom.sh examples/A.mp4 examples/B.mp4 examples/OUTPUT.mp4
|
|
||||||
|
|
||||||
TMPDIR=/tmp/frameloom/
|
|
||||||
i=0
|
|
||||||
|
|
||||||
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] ;
|
|
||||||
then
|
|
||||||
echo "Not enough arguments supplied"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p $TMPDIR
|
|
||||||
|
|
||||||
#relies on the alphanumeric sorting that occurs when getting
|
|
||||||
#the list of files in the for loop below
|
|
||||||
ffmpeg -i "$1" -compression_algo raw -pix_fmt rgb24 "${TMPDIR}export-%05d_a.tif"
|
|
||||||
ffmpeg -i "$2" -compression_algo raw -pix_fmt rgb24 "${TMPDIR}export-%05d_b.tif"
|
|
||||||
|
|
||||||
#rm -r $TMP
|
|
||||||
|
|
||||||
for filename in ${TMPDIR}*.tif; do
|
|
||||||
value=`printf %05d $i`
|
|
||||||
#echo $filename
|
|
||||||
#echo "${TMPDIR}render_${value}.tif"
|
|
||||||
mv "$filename" "${TMPDIR}render_${value}.tif"
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
|
|
||||||
ffmpeg -r 30 -f image2 -s 1920x1080 -i "${TMPDIR}render_%05d.tif" -c:v prores_ks -profile:v 3 -y "$3"
|
|
||||||
|
|
||||||
rm -r $TMPDIR
|
|
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
|
@ -16,15 +16,15 @@
|
||||||
"author": "sixteenmillimeter",
|
"author": "sixteenmillimeter",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^2.19.0",
|
"commander": "^7.2.0",
|
||||||
"fs-extra": "^7.0.1"
|
"fs-extra": "^9.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^11.13.0",
|
"@types/node": "^14.14.36",
|
||||||
"jsdoc-to-markdown": "^4.0.1",
|
"jsdoc-to-markdown": "^7.0.1",
|
||||||
"pkg": "^4.4.0",
|
"pkg": "^4.5.1",
|
||||||
"qunit": "^2.8.0",
|
"qunit": "^2.14.1",
|
||||||
"typescript": "^3.4.1"
|
"typescript": "^4.2.3"
|
||||||
},
|
},
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const execRaw = require('child_process').exec
|
const execRaw = require('child_process').exec
|
||||||
const os = require('os')
|
const { tmpdir } = require('os')
|
||||||
const path = require('path')
|
const { join, extname } = require('path')
|
||||||
const program = require('commander')
|
const program = require('commander')
|
||||||
const fs = require('fs-extra')
|
const { move, exists, unlink, readdir, mkdir } = require('fs-extra')
|
||||||
|
|
||||||
const pkg : any = require('./package.json')
|
const { version } = require('./package.json')
|
||||||
|
|
||||||
const OUTPUT_RE : RegExp = new RegExp('{{o}}', 'g')
|
const OUTPUT_RE : RegExp = new RegExp('{{o}}', 'g')
|
||||||
const INPUT_RE : RegExp = new RegExp('{{i}}', 'g')
|
const INPUT_RE : RegExp = new RegExp('{{i}}', 'g')
|
||||||
|
|
||||||
let QUIET : boolean = false
|
let QUIET : boolean = false
|
||||||
let TMPDIR : string = os.tmpdir() || '/tmp'
|
let TMPDIR : string = tmpdir() || '/tmp'
|
||||||
let TMPPATH : string
|
let TMPPATH : string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,7 +25,7 @@ let TMPPATH : string
|
||||||
*
|
*
|
||||||
* @returns {Promise} Promise containing the complete stdio
|
* @returns {Promise} Promise containing the complete stdio
|
||||||
**/
|
**/
|
||||||
async function exec (cmd : string) {
|
async function exec (cmd : string) : Promise<string> {
|
||||||
return new Promise((resolve : any, reject : any) => {
|
return new Promise((resolve : any, reject : any) => {
|
||||||
return execRaw(cmd, { maxBuffer : 500 * 1024 * 1024}, (err : any, stdio : string, stderr : string) => {
|
return execRaw(cmd, { maxBuffer : 500 * 1024 * 1024}, (err : any, stdio : string, stderr : string) => {
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
|
@ -40,7 +40,7 @@ async function exec (cmd : string) {
|
||||||
*
|
*
|
||||||
* @returns {Promise} Promise that resolves after set time
|
* @returns {Promise} Promise that resolves after set time
|
||||||
**/
|
**/
|
||||||
async function delay (ms : number) {
|
async function delay (ms : number) : Promise<any> {
|
||||||
return new Promise((resolve : any, reject : any) =>{
|
return new Promise((resolve : any, reject : any) =>{
|
||||||
return setTimeout(resolve, ms)
|
return setTimeout(resolve, ms)
|
||||||
})
|
})
|
||||||
|
@ -49,7 +49,7 @@ async function delay (ms : number) {
|
||||||
* Log function wrapper that can silences logs when
|
* Log function wrapper that can silences logs when
|
||||||
* QUIET == true
|
* QUIET == true
|
||||||
*/
|
*/
|
||||||
function log (msg : string, err : any = false) {
|
function log (msg : string, err : any = false) : boolean {
|
||||||
if (QUIET) return false
|
if (QUIET) return false
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(msg, err)
|
console.error(msg, err)
|
||||||
|
@ -102,15 +102,15 @@ function randomInt (min : number, max : number) {
|
||||||
**/
|
**/
|
||||||
async function clear () {
|
async function clear () {
|
||||||
let cmd : string = `rm -r "${TMPPATH}"`
|
let cmd : string = `rm -r "${TMPPATH}"`
|
||||||
let exists : boolean
|
let dirExists : boolean
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exists = await fs.exists(TMPPATH)
|
dirExists = await exists(TMPPATH)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error checking if file exists', err)
|
log('Error checking if file exists', err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists) {
|
if (dirExists) {
|
||||||
log(`Clearing temp directory "${TMPPATH}"`)
|
log(`Clearing temp directory "${TMPPATH}"`)
|
||||||
try {
|
try {
|
||||||
await exec(cmd)
|
await exec(cmd)
|
||||||
|
@ -121,7 +121,7 @@ async function clear () {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.mkdir(TMPPATH)
|
await mkdir(TMPPATH)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code !== 'EEXIST') {
|
if (err.code !== 'EEXIST') {
|
||||||
log('Error making directory', err)
|
log('Error making directory', err)
|
||||||
|
@ -141,13 +141,13 @@ async function clear () {
|
||||||
*
|
*
|
||||||
* @returns {string} String with the export order, not sure why I did this
|
* @returns {string} String with the export order, not sure why I did this
|
||||||
**/
|
**/
|
||||||
async function frames (video : string, order : number, avconv : boolean) {
|
async function frames (video : string, order : number, avconv : boolean) : Promise<string> {
|
||||||
let ext : string = 'tif'
|
let ext : string = 'tif'
|
||||||
let exe : string = avconv ? 'avconv' : 'ffmpeg'
|
let exe : string = avconv ? 'avconv' : 'ffmpeg'
|
||||||
let tmpoutput : string
|
let tmpoutput : string
|
||||||
let cmd : string
|
let cmd : string
|
||||||
|
|
||||||
tmpoutput = path.join(TMPPATH, `export-%05d_${order}.${ext}`)
|
tmpoutput = join(TMPPATH, `export-%05d_${order}.${ext}`)
|
||||||
|
|
||||||
cmd = `${exe} -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`
|
cmd = `${exe} -i "${video}" -compression_algo raw -pix_fmt rgb24 "${tmpoutput}"`
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ async function frames (video : string, order : number, avconv : boolean) {
|
||||||
return process.exit(3)
|
return process.exit(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.join(TMPPATH, `export-%05d_${order}`)
|
return join(TMPPATH, `export-%05d_${order}`)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Shells out to run a sub command on every frame to perform effects
|
* Shells out to run a sub command on every frame to perform effects
|
||||||
|
@ -174,7 +174,7 @@ async function subExec (cmd : string) {
|
||||||
let framePath : string
|
let framePath : string
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frames = await fs.readdir(TMPPATH)
|
frames = await readdir(TMPPATH)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error reading tmp directory', err)
|
log('Error reading tmp directory', err)
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ async function subExec (cmd : string) {
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let frame of frames) {
|
for (let frame of frames) {
|
||||||
framePath = path.join(TMPPATH, frame)
|
framePath = join(TMPPATH, frame)
|
||||||
if (cmd.indexOf('{{i}}') !== -1 || cmd.indexOf('{{o}}')) {
|
if (cmd.indexOf('{{i}}') !== -1 || cmd.indexOf('{{o}}')) {
|
||||||
frameCmd = cmd.replace(INPUT_RE, framePath)
|
frameCmd = cmd.replace(INPUT_RE, framePath)
|
||||||
.replace(OUTPUT_RE, framePath)
|
.replace(OUTPUT_RE, framePath)
|
||||||
|
@ -216,7 +216,7 @@ async function weave (pattern : number[], realtime : boolean, random : boolean)
|
||||||
log('Weaving frames...')
|
log('Weaving frames...')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frames = await fs.readdir(TMPPATH)
|
frames = await readdir(TMPPATH)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error reading tmp directory', err)
|
log('Error reading tmp directory', err)
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ async function altSort (list : string[], pattern : number[], realtime : boolean)
|
||||||
let oldPath : string
|
let oldPath : string
|
||||||
let newName : string
|
let newName : string
|
||||||
let newPath : string
|
let newPath : string
|
||||||
let ext : string = path.extname(list[0])
|
let ext : string = extname(list[0])
|
||||||
let x : number
|
let x : number
|
||||||
let i : number
|
let i : number
|
||||||
|
|
||||||
|
@ -313,14 +313,14 @@ async function altSort (list : string[], pattern : number[], realtime : boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldName = String(groups[patternIndexes[i]][0])
|
oldName = String(groups[patternIndexes[i]][0])
|
||||||
oldPath = path.join(TMPPATH, oldName)
|
oldPath = join(TMPPATH, oldName)
|
||||||
|
|
||||||
groups[patternIndexes[i]].shift()
|
groups[patternIndexes[i]].shift()
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
log(`Skipping ${oldName}`)
|
log(`Skipping ${oldName}`)
|
||||||
try {
|
try {
|
||||||
await fs.unlink(oldPath)
|
await unlink(oldPath)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error deleting frame', err)
|
log('Error deleting frame', err)
|
||||||
}
|
}
|
||||||
|
@ -328,11 +328,11 @@ async function altSort (list : string[], pattern : number[], realtime : boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
newName = `./render_${zeroPad(frameCount)}${ext}`
|
newName = `./render_${zeroPad(frameCount)}${ext}`
|
||||||
newPath = path.join(TMPPATH, newName)
|
newPath = join(TMPPATH, newName)
|
||||||
log(`Renaming ${oldName} -> ${newName}`)
|
log(`Renaming ${oldName} -> ${newName}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.move(oldPath, newPath)
|
await move(oldPath, newPath)
|
||||||
newList.push(newName)
|
newList.push(newName)
|
||||||
frameCount++
|
frameCount++
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -357,7 +357,7 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
|
||||||
let step : any
|
let step : any
|
||||||
let skipCount : number
|
let skipCount : number
|
||||||
let skip : boolean
|
let skip : boolean
|
||||||
let ext : string = path.extname(list[0])
|
let ext : string = extname(list[0])
|
||||||
let oldPath : string
|
let oldPath : string
|
||||||
let newName : string
|
let newName : string
|
||||||
let newPath : string
|
let newPath : string
|
||||||
|
@ -377,12 +377,12 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldPath = path.join(TMPPATH, list[i])
|
oldPath = join(TMPPATH, list[i])
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
log(`Skipping ${list[i]}`)
|
log(`Skipping ${list[i]}`)
|
||||||
try {
|
try {
|
||||||
await fs.unlink(oldPath)
|
await unlink(oldPath)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error deleting frame', err)
|
log('Error deleting frame', err)
|
||||||
}
|
}
|
||||||
|
@ -390,11 +390,11 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
|
||||||
}
|
}
|
||||||
|
|
||||||
newName = `./render_${zeroPad(frameCount)}${ext}`
|
newName = `./render_${zeroPad(frameCount)}${ext}`
|
||||||
newPath = path.join(TMPPATH, newName)
|
newPath = join(TMPPATH, newName)
|
||||||
log(`Renaming ${list[i]} -> ${newName}`)
|
log(`Renaming ${list[i]} -> ${newName}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.move(oldPath, newPath)
|
await move(oldPath, newPath)
|
||||||
newList.push(newName)
|
newList.push(newName)
|
||||||
frameCount++
|
frameCount++
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -416,7 +416,7 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
|
||||||
**/
|
**/
|
||||||
async function randomSort (list : string[], pattern : number[], realtime : boolean) {
|
async function randomSort (list : string[], pattern : number[], realtime : boolean) {
|
||||||
let frameCount : number = 0
|
let frameCount : number = 0
|
||||||
let ext : string = path.extname(list[0])
|
let ext : string = extname(list[0])
|
||||||
let oldPath : string
|
let oldPath : string
|
||||||
let newName : string
|
let newName : string
|
||||||
let newPath : string
|
let newPath : string
|
||||||
|
@ -433,10 +433,10 @@ async function randomSort (list : string[], pattern : number[], realtime : boole
|
||||||
|
|
||||||
log(`Skipping extra frames...`)
|
log(`Skipping extra frames...`)
|
||||||
for (let i : number = 0; i < remove.length; i++) {
|
for (let i : number = 0; i < remove.length; i++) {
|
||||||
oldPath = path.join(TMPPATH, remove[i])
|
oldPath = join(TMPPATH, remove[i])
|
||||||
log(`Skipping ${list[i]}`)
|
log(`Skipping ${list[i]}`)
|
||||||
try {
|
try {
|
||||||
await fs.unlink(oldPath)
|
await unlink(oldPath)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error deleting frame', err)
|
log('Error deleting frame', err)
|
||||||
}
|
}
|
||||||
|
@ -444,14 +444,14 @@ async function randomSort (list : string[], pattern : number[], realtime : boole
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i : number = 0; i < list.length; i++) {
|
for (let i : number = 0; i < list.length; i++) {
|
||||||
oldPath = path.join(TMPPATH, list[i])
|
oldPath = join(TMPPATH, list[i])
|
||||||
|
|
||||||
newName = `./render_${zeroPad(frameCount)}${ext}`
|
newName = `./render_${zeroPad(frameCount)}${ext}`
|
||||||
newPath = path.join(TMPPATH, newName)
|
newPath = join(TMPPATH, newName)
|
||||||
log(`Renaming ${list[i]} -> ${newName}`)
|
log(`Renaming ${list[i]} -> ${newName}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.move(oldPath, newPath)
|
await move(oldPath, newPath)
|
||||||
newList.push(newName)
|
newList.push(newName)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Error moving frame', err)
|
log('Error moving frame', err)
|
||||||
|
@ -474,7 +474,7 @@ async function spinFrames () {
|
||||||
console.log('Spinning frames...')
|
console.log('Spinning frames...')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frames = await fs.readdir(TMPPATH)
|
frames = await readdir(TMPPATH)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error reading tmp directory', err)
|
console.error('Error reading tmp directory', err)
|
||||||
}
|
}
|
||||||
|
@ -485,7 +485,7 @@ async function spinFrames () {
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let frame of frames) {
|
for (let frame of frames) {
|
||||||
framePath = path.join(TMPPATH, frame)
|
framePath = join(TMPPATH, frame)
|
||||||
rotate = ''
|
rotate = ''
|
||||||
flip = ''
|
flip = ''
|
||||||
flop = ''
|
flop = ''
|
||||||
|
@ -521,7 +521,7 @@ async function spinFrames () {
|
||||||
**/
|
**/
|
||||||
async function render (output : string, avconv : boolean) {
|
async function render (output : string, avconv : boolean) {
|
||||||
//process.exit()
|
//process.exit()
|
||||||
let frames : string = path.join(TMPPATH, `render_%05d.tif`)
|
let frames : string = join(TMPPATH, `render_%05d.tif`)
|
||||||
let exe : string = avconv ? 'avconv' : 'ffmpeg'
|
let exe : string = avconv ? 'avconv' : 'ffmpeg'
|
||||||
let resolution : string = '1920x1080' //TODO: make variable/argument
|
let resolution : string = '1920x1080' //TODO: make variable/argument
|
||||||
//TODO: make object configurable with shorthand names
|
//TODO: make object configurable with shorthand names
|
||||||
|
@ -547,7 +547,8 @@ async function render (output : string, avconv : boolean) {
|
||||||
*
|
*
|
||||||
* @param {object} arg Object containing all arguments
|
* @param {object} arg Object containing all arguments
|
||||||
**/
|
**/
|
||||||
async function main (arg : any) {
|
async function main (program : any) {
|
||||||
|
const arg = program.opts();
|
||||||
let input : string[] = arg.input.split(':')
|
let input : string[] = arg.input.split(':')
|
||||||
let output : string = arg.output
|
let output : string = arg.output
|
||||||
let pattern : any[] = []
|
let pattern : any[] = []
|
||||||
|
@ -556,7 +557,7 @@ async function main (arg : any) {
|
||||||
let random : boolean = false
|
let random : boolean = false
|
||||||
let e : any = false
|
let e : any = false
|
||||||
let exe : string = arg.avconv ? 'avconv' : 'ffmpeg'
|
let exe : string = arg.avconv ? 'avconv' : 'ffmpeg'
|
||||||
let exists : any
|
let fileExists : any
|
||||||
|
|
||||||
console.time('frameloom')
|
console.time('frameloom')
|
||||||
|
|
||||||
|
@ -602,13 +603,13 @@ async function main (arg : any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exists = await exec(`which ${exe}`)
|
fileExists = await exec(`which ${exe}`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(`Error checking for ${exe}`)
|
log(`Error checking for ${exe}`)
|
||||||
process.exit(11)
|
process.exit(11)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exists || exists === '' || exists.indexOf(exe) === -1) {
|
if (!fileExists || fileExists === '' || fileExists.indexOf(exe) === -1) {
|
||||||
log(`${exe} is required and is not installed. Please install ${exe} to use frameloom.`)
|
log(`${exe} is required and is not installed. Please install ${exe} to use frameloom.`)
|
||||||
process.exit(12)
|
process.exit(12)
|
||||||
}
|
}
|
||||||
|
@ -620,7 +621,7 @@ async function main (arg : any) {
|
||||||
|
|
||||||
if (arg.realtime) realtime = true;
|
if (arg.realtime) realtime = true;
|
||||||
|
|
||||||
TMPPATH = path.join(TMPDIR, 'frameloom');
|
TMPPATH = join(TMPDIR, 'frameloom');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await clear()
|
await clear()
|
||||||
|
@ -683,7 +684,7 @@ async function main (arg : any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
program
|
program
|
||||||
.version(pkg.version)
|
.version(version)
|
||||||
.option('-i, --input [files]', 'Specify input videos with paths seperated by colon')
|
.option('-i, --input [files]', 'Specify input videos with paths seperated by colon')
|
||||||
.option('-o, --output [file]', 'Specify output path of video')
|
.option('-o, --output [file]', 'Specify output path of video')
|
||||||
.option('-p, --pattern [pattern]', 'Specify a pattern for the flicker 1:1 is standard')
|
.option('-p, --pattern [pattern]', 'Specify a pattern for the flicker 1:1 is standard')
|
||||||
|
|
Loading…
Reference in New Issue