Compare commits

...

11 Commits
v1.0.3 ... main

7 changed files with 4082 additions and 2014 deletions

View File

@ -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
This script relies on `ffmpeg` to export and stitch video back together
@ -21,13 +23,15 @@ chmod +x frameloom
## 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
Run `./frameloom -h` to display help screen.
```
```bash
Usage: frameloom [options]
Options:
@ -39,11 +43,19 @@ Options:
-t, --tmp [dir] Specify tmp directory for exporting frames
-a, --avconv Specify avconv if preferred to ffmpeg
-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
* Publish example videos
Copyright 2018-2021 M McWilliams
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.

View File

@ -8,6 +8,10 @@
<dt><a href="#delay">delay(ms)</a><code>Promise</code></dt>
<dd><p>Delays process for specified amount of time in milliseconds.</p>
</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>
<dd><p>Pads a numerical value with preceding zeros to make strings same length.</p>
</dd>
@ -15,15 +19,18 @@
<dd><p>Shuffles an array into a random state.</p>
</dd>
<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>
</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
to keep frames in alternating order to be quickly stitched together
or re-sorted.</p>
</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.
Calls <code>patternSort()</code> to perform the rename and unlink actions</p>
</dd>
@ -36,7 +43,7 @@
<dt><a href="#randomSort">randomSort(list, pattern, realtime)</a></dt>
<dd><p>Ramdomly sort frames for re-stitching.</p>
</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>
<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 |
<a name="log"></a>
## log()
Log function wrapper that can silences logs when
QUIET == true
**Kind**: global function
<a name="zeroPad"></a>
## zeroPad(i, max) ⇒ <code>string</code>
@ -97,13 +111,13 @@ Shuffles an array into a random state.
<a name="clear"></a>
## clear()
Clears the temporary directory of all files.
Clears the temporary directory of all files.
Establishes a directory if none exists.
**Kind**: global function
<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
to keep frames in alternating order to be quickly stitched together
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 |
| 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>
## weave(pattern, realtime)
## weave(pattern, realtime, random)
Re-arranges the frames into the order specified in the pattern.
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 |
| 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>
@ -170,7 +197,7 @@ Ramdomly sort frames for re-stitching.
<a name="render"></a>
## render(output)
## render(output, avconv)
Render the frames into a video using ffmpeg.
**Kind**: global function
@ -178,6 +205,7 @@ Render the frames into a video using ffmpeg.
| Param | Type | Description |
| --- | --- | --- |
| 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>

View File

@ -1,15 +1,15 @@
#!/usr/bin/env node
'use strict';
const execRaw = require('child_process').exec;
const os = require('os');
const path = require('path');
const { tmpdir } = require('os');
const { join, extname } = require('path');
const program = require('commander');
const fs = require('fs-extra');
const pkg = require('./package.json');
const { move, exists, unlink, readdir, mkdir } = require('fs-extra');
const { version } = require('./package.json');
const OUTPUT_RE = new RegExp('{{o}}', 'g');
const INPUT_RE = new RegExp('{{i}}', 'g');
let QUIET = false;
let TMPDIR = os.tmpdir() || '/tmp';
let TMPDIR = tmpdir() || '/tmp';
let TMPPATH;
/**
* Shells out to execute a command with async/await.
@ -97,14 +97,14 @@ function randomInt(min, max) {
**/
async function clear() {
let cmd = `rm -r "${TMPPATH}"`;
let exists;
let dirExists;
try {
exists = await fs.exists(TMPPATH);
dirExists = await exists(TMPPATH);
}
catch (err) {
log('Error checking if file exists', err);
}
if (exists) {
if (dirExists) {
log(`Clearing temp directory "${TMPPATH}"`);
try {
await exec(cmd);
@ -115,7 +115,7 @@ async function clear() {
}
}
try {
await fs.mkdir(TMPPATH);
await mkdir(TMPPATH);
}
catch (err) {
if (err.code !== 'EEXIST') {
@ -140,7 +140,7 @@ async function frames(video, order, avconv) {
let exe = avconv ? 'avconv' : 'ffmpeg';
let tmpoutput;
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}"`;
log(`Exporting ${video} as single frames...`);
try {
@ -150,7 +150,7 @@ async function frames(video, order, avconv) {
log('Error exporting video', err);
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
@ -163,7 +163,7 @@ async function subExec(cmd) {
let frameCmd;
let framePath;
try {
frames = await fs.readdir(TMPPATH);
frames = await readdir(TMPPATH);
}
catch (err) {
log('Error reading tmp directory', err);
@ -173,7 +173,7 @@ async function subExec(cmd) {
return true;
});
for (let frame of frames) {
framePath = path.join(TMPPATH, frame);
framePath = join(TMPPATH, frame);
if (cmd.indexOf('{{i}}') !== -1 || cmd.indexOf('{{o}}')) {
frameCmd = cmd.replace(INPUT_RE, framePath)
.replace(OUTPUT_RE, framePath);
@ -204,7 +204,7 @@ async function weave(pattern, realtime, random) {
let alt = false;
log('Weaving frames...');
try {
frames = await fs.readdir(TMPPATH);
frames = await readdir(TMPPATH);
}
catch (err) {
log('Error reading tmp directory', err);
@ -268,7 +268,7 @@ async function altSort(list, pattern, realtime) {
let oldPath;
let newName;
let newPath;
let ext = path.extname(list[0]);
let ext = extname(list[0]);
let x;
let i;
for (x = 0; x < pattern.length; x++) {
@ -298,12 +298,12 @@ async function altSort(list, pattern, realtime) {
continue;
}
oldName = String(groups[patternIndexes[i]][0]);
oldPath = path.join(TMPPATH, oldName);
oldPath = join(TMPPATH, oldName);
groups[patternIndexes[i]].shift();
if (skip) {
log(`Skipping ${oldName}`);
try {
await fs.unlink(oldPath);
await unlink(oldPath);
}
catch (err) {
log('Error deleting frame', err);
@ -311,10 +311,10 @@ async function altSort(list, pattern, realtime) {
continue;
}
newName = `./render_${zeroPad(frameCount)}${ext}`;
newPath = path.join(TMPPATH, newName);
newPath = join(TMPPATH, newName);
log(`Renaming ${oldName} -> ${newName}`);
try {
await fs.move(oldPath, newPath);
await move(oldPath, newPath);
newList.push(newName);
frameCount++;
}
@ -339,7 +339,7 @@ async function standardSort(list, pattern, realtime) {
let step;
let skipCount;
let skip;
let ext = path.extname(list[0]);
let ext = extname(list[0]);
let oldPath;
let newName;
let newPath;
@ -356,11 +356,11 @@ async function standardSort(list, pattern, realtime) {
skipCount = pattern.length;
}
}
oldPath = path.join(TMPPATH, list[i]);
oldPath = join(TMPPATH, list[i]);
if (skip) {
log(`Skipping ${list[i]}`);
try {
await fs.unlink(oldPath);
await unlink(oldPath);
}
catch (err) {
log('Error deleting frame', err);
@ -368,10 +368,10 @@ async function standardSort(list, pattern, realtime) {
continue;
}
newName = `./render_${zeroPad(frameCount)}${ext}`;
newPath = path.join(TMPPATH, newName);
newPath = join(TMPPATH, newName);
log(`Renaming ${list[i]} -> ${newName}`);
try {
await fs.move(oldPath, newPath);
await move(oldPath, newPath);
newList.push(newName);
frameCount++;
}
@ -391,7 +391,7 @@ async function standardSort(list, pattern, realtime) {
**/
async function randomSort(list, pattern, realtime) {
let frameCount = 0;
let ext = path.extname(list[0]);
let ext = extname(list[0]);
let oldPath;
let newName;
let newPath;
@ -405,10 +405,10 @@ async function randomSort(list, pattern, realtime) {
list = list.slice(0, removeLen);
log(`Skipping extra frames...`);
for (let i = 0; i < remove.length; i++) {
oldPath = path.join(TMPPATH, remove[i]);
oldPath = join(TMPPATH, remove[i]);
log(`Skipping ${list[i]}`);
try {
await fs.unlink(oldPath);
await unlink(oldPath);
}
catch (err) {
log('Error deleting frame', err);
@ -416,12 +416,12 @@ async function randomSort(list, pattern, realtime) {
}
}
for (let i = 0; i < list.length; i++) {
oldPath = path.join(TMPPATH, list[i]);
oldPath = join(TMPPATH, list[i]);
newName = `./render_${zeroPad(frameCount)}${ext}`;
newPath = path.join(TMPPATH, newName);
newPath = join(TMPPATH, newName);
log(`Renaming ${list[i]} -> ${newName}`);
try {
await fs.move(oldPath, newPath);
await move(oldPath, newPath);
newList.push(newName);
}
catch (err) {
@ -440,7 +440,7 @@ async function spinFrames() {
let rotate;
console.log('Spinning frames...');
try {
frames = await fs.readdir(TMPPATH);
frames = await readdir(TMPPATH);
}
catch (err) {
console.error('Error reading tmp directory', err);
@ -451,7 +451,7 @@ async function spinFrames() {
return true;
});
for (let frame of frames) {
framePath = path.join(TMPPATH, frame);
framePath = join(TMPPATH, frame);
rotate = '';
flip = '';
flop = '';
@ -487,7 +487,7 @@ async function spinFrames() {
**/
async function render(output, avconv) {
//process.exit()
let frames = path.join(TMPPATH, `render_%05d.tif`);
let frames = join(TMPPATH, `render_%05d.tif`);
let exe = avconv ? 'avconv' : 'ffmpeg';
let resolution = '1920x1080'; //TODO: make variable/argument
//TODO: make object configurable with shorthand names
@ -512,7 +512,8 @@ async function render(output, avconv) {
*
* @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 output = arg.output;
let pattern = [];
@ -521,7 +522,7 @@ async function main(arg) {
let random = false;
let e = false;
let exe = arg.avconv ? 'avconv' : 'ffmpeg';
let exists;
let fileExists;
console.time('frameloom');
if (input.length < 2) {
log('Must provide more than 1 input', {});
@ -558,13 +559,13 @@ async function main(arg) {
}
}
try {
exists = await exec(`which ${exe}`);
fileExists = await exec(`which ${exe}`);
}
catch (err) {
log(`Error checking for ${exe}`);
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.`);
process.exit(12);
}
@ -574,7 +575,7 @@ async function main(arg) {
}
if (arg.realtime)
realtime = true;
TMPPATH = path.join(TMPDIR, 'frameloom');
TMPPATH = join(TMPDIR, 'frameloom');
try {
await clear();
}
@ -634,7 +635,7 @@ async function main(arg) {
console.timeEnd('frameloom');
}
program
.version(pkg.version)
.version(version)
.option('-i, --input [files]', 'Specify input videos with paths seperated by colon')
.option('-o, --output [file]', 'Specify output path of video')
.option('-p, --pattern [pattern]', 'Specify a pattern for the flicker 1:1 is standard')

View File

@ -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

5811
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,15 @@
"author": "sixteenmillimeter",
"license": "MIT",
"dependencies": {
"commander": "^2.19.0",
"fs-extra": "^7.0.1"
"commander": "^7.2.0",
"fs-extra": "^9.1.0"
},
"devDependencies": {
"@types/node": "^11.13.0",
"jsdoc-to-markdown": "^4.0.1",
"pkg": "^4.4.0",
"qunit": "^2.8.0",
"typescript": "^3.4.1"
"@types/node": "^14.14.36",
"jsdoc-to-markdown": "^7.0.1",
"pkg": "^4.5.1",
"qunit": "^2.14.1",
"typescript": "^4.2.3"
},
"pkg": {
"scripts": [

View File

@ -3,18 +3,18 @@
'use strict'
const execRaw = require('child_process').exec
const os = require('os')
const path = require('path')
const { tmpdir } = require('os')
const { join, extname } = require('path')
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 INPUT_RE : RegExp = new RegExp('{{i}}', 'g')
let QUIET : boolean = false
let TMPDIR : string = os.tmpdir() || '/tmp'
let TMPDIR : string = tmpdir() || '/tmp'
let TMPPATH : string
/**
@ -25,7 +25,7 @@ let TMPPATH : string
*
* @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 execRaw(cmd, { maxBuffer : 500 * 1024 * 1024}, (err : any, stdio : string, stderr : string) => {
if (err) return reject(err)
@ -40,7 +40,7 @@ async function exec (cmd : string) {
*
* @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 setTimeout(resolve, ms)
})
@ -49,7 +49,7 @@ async function delay (ms : number) {
* Log function wrapper that can silences logs when
* QUIET == true
*/
function log (msg : string, err : any = false) {
function log (msg : string, err : any = false) : boolean {
if (QUIET) return false
if (err) {
console.error(msg, err)
@ -102,15 +102,15 @@ function randomInt (min : number, max : number) {
**/
async function clear () {
let cmd : string = `rm -r "${TMPPATH}"`
let exists : boolean
let dirExists : boolean
try {
exists = await fs.exists(TMPPATH)
dirExists = await exists(TMPPATH)
} catch (err) {
log('Error checking if file exists', err)
}
if (exists) {
if (dirExists) {
log(`Clearing temp directory "${TMPPATH}"`)
try {
await exec(cmd)
@ -121,7 +121,7 @@ async function clear () {
}
try {
await fs.mkdir(TMPPATH)
await mkdir(TMPPATH)
} catch (err) {
if (err.code !== 'EEXIST') {
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
**/
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 exe : string = avconv ? 'avconv' : 'ffmpeg'
let tmpoutput : 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}"`
@ -160,7 +160,7 @@ async function frames (video : string, order : number, avconv : boolean) {
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
@ -174,7 +174,7 @@ async function subExec (cmd : string) {
let framePath : string
try {
frames = await fs.readdir(TMPPATH)
frames = await readdir(TMPPATH)
} catch (err) {
log('Error reading tmp directory', err)
}
@ -184,7 +184,7 @@ async function subExec (cmd : string) {
})
for (let frame of frames) {
framePath = path.join(TMPPATH, frame)
framePath = join(TMPPATH, frame)
if (cmd.indexOf('{{i}}') !== -1 || cmd.indexOf('{{o}}')) {
frameCmd = cmd.replace(INPUT_RE, framePath)
.replace(OUTPUT_RE, framePath)
@ -216,7 +216,7 @@ async function weave (pattern : number[], realtime : boolean, random : boolean)
log('Weaving frames...')
try {
frames = await fs.readdir(TMPPATH)
frames = await readdir(TMPPATH)
} catch (err) {
log('Error reading tmp directory', err)
}
@ -275,7 +275,7 @@ async function altSort (list : string[], pattern : number[], realtime : boolean)
let oldPath : string
let newName : string
let newPath : string
let ext : string = path.extname(list[0])
let ext : string = extname(list[0])
let x : number
let i : number
@ -313,14 +313,14 @@ async function altSort (list : string[], pattern : number[], realtime : boolean)
}
oldName = String(groups[patternIndexes[i]][0])
oldPath = path.join(TMPPATH, oldName)
oldPath = join(TMPPATH, oldName)
groups[patternIndexes[i]].shift()
if (skip) {
log(`Skipping ${oldName}`)
try {
await fs.unlink(oldPath)
await unlink(oldPath)
} catch (err) {
log('Error deleting frame', err)
}
@ -328,11 +328,11 @@ async function altSort (list : string[], pattern : number[], realtime : boolean)
}
newName = `./render_${zeroPad(frameCount)}${ext}`
newPath = path.join(TMPPATH, newName)
newPath = join(TMPPATH, newName)
log(`Renaming ${oldName} -> ${newName}`)
try {
await fs.move(oldPath, newPath)
await move(oldPath, newPath)
newList.push(newName)
frameCount++
} catch (err) {
@ -357,7 +357,7 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
let step : any
let skipCount : number
let skip : boolean
let ext : string = path.extname(list[0])
let ext : string = extname(list[0])
let oldPath : string
let newName : 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) {
log(`Skipping ${list[i]}`)
try {
await fs.unlink(oldPath)
await unlink(oldPath)
} catch (err) {
log('Error deleting frame', err)
}
@ -390,11 +390,11 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
}
newName = `./render_${zeroPad(frameCount)}${ext}`
newPath = path.join(TMPPATH, newName)
newPath = join(TMPPATH, newName)
log(`Renaming ${list[i]} -> ${newName}`)
try {
await fs.move(oldPath, newPath)
await move(oldPath, newPath)
newList.push(newName)
frameCount++
} catch (err) {
@ -416,7 +416,7 @@ async function standardSort (list : string[], pattern : number[], realtime : boo
**/
async function randomSort (list : string[], pattern : number[], realtime : boolean) {
let frameCount : number = 0
let ext : string = path.extname(list[0])
let ext : string = extname(list[0])
let oldPath : string
let newName : string
let newPath : string
@ -433,10 +433,10 @@ async function randomSort (list : string[], pattern : number[], realtime : boole
log(`Skipping extra frames...`)
for (let i : number = 0; i < remove.length; i++) {
oldPath = path.join(TMPPATH, remove[i])
oldPath = join(TMPPATH, remove[i])
log(`Skipping ${list[i]}`)
try {
await fs.unlink(oldPath)
await unlink(oldPath)
} catch (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++) {
oldPath = path.join(TMPPATH, list[i])
oldPath = join(TMPPATH, list[i])
newName = `./render_${zeroPad(frameCount)}${ext}`
newPath = path.join(TMPPATH, newName)
newPath = join(TMPPATH, newName)
log(`Renaming ${list[i]} -> ${newName}`)
try {
await fs.move(oldPath, newPath)
await move(oldPath, newPath)
newList.push(newName)
} catch (err) {
log('Error moving frame', err)
@ -474,7 +474,7 @@ async function spinFrames () {
console.log('Spinning frames...')
try {
frames = await fs.readdir(TMPPATH)
frames = await readdir(TMPPATH)
} catch (err) {
console.error('Error reading tmp directory', err)
}
@ -485,7 +485,7 @@ async function spinFrames () {
})
for (let frame of frames) {
framePath = path.join(TMPPATH, frame)
framePath = join(TMPPATH, frame)
rotate = ''
flip = ''
flop = ''
@ -521,7 +521,7 @@ async function spinFrames () {
**/
async function render (output : string, avconv : boolean) {
//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 resolution : string = '1920x1080' //TODO: make variable/argument
//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
**/
async function main (arg : any) {
async function main (program : any) {
const arg = program.opts();
let input : string[] = arg.input.split(':')
let output : string = arg.output
let pattern : any[] = []
@ -556,7 +557,7 @@ async function main (arg : any) {
let random : boolean = false
let e : any = false
let exe : string = arg.avconv ? 'avconv' : 'ffmpeg'
let exists : any
let fileExists : any
console.time('frameloom')
@ -602,13 +603,13 @@ async function main (arg : any) {
}
try {
exists = await exec(`which ${exe}`)
fileExists = await exec(`which ${exe}`)
} catch (err) {
log(`Error checking for ${exe}`)
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.`)
process.exit(12)
}
@ -620,7 +621,7 @@ async function main (arg : any) {
if (arg.realtime) realtime = true;
TMPPATH = path.join(TMPDIR, 'frameloom');
TMPPATH = join(TMPDIR, 'frameloom');
try {
await clear()
@ -683,7 +684,7 @@ async function main (arg : any) {
}
program
.version(pkg.version)
.version(version)
.option('-i, --input [files]', 'Specify input videos with paths seperated by colon')
.option('-o, --output [file]', 'Specify output path of video')
.option('-p, --pattern [pattern]', 'Specify a pattern for the flicker 1:1 is standard')