mediasnacks 0.22.5 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/avif.js +1 -1
- package/src/cli.js +3 -2
- package/src/detectdups.js +4 -10
- package/src/dropdups.js +4 -6
- package/src/edgespic.js +1 -1
- package/src/flattendir.sh +3 -9
- package/src/framediff.sh +3 -9
- package/src/gif.sh +19 -8
- package/src/hev1tohvc1.js +8 -2
- package/src/moov2front.js +8 -1
- package/src/play.js +2 -1
- package/src/png.sh +20 -0
- package/src/prores.js +40 -19
- package/src/qdir.js +20 -13
- package/src/random.js +3 -5
- package/src/resize.js +1 -1
- package/src/seqcheck.js +1 -1
- package/src/sqcrop.js +1 -1
- package/src/ssim.js +13 -6
- package/src/utils/subprocess.js +3 -4
- package/src/vconcat.sh +5 -14
- package/src/vdiff.sh +3 -9
- package/src/vsplit.js +1 -1
- package/src/vtrim.js +1 -1
package/README.md
CHANGED
package/package.json
CHANGED
package/src/avif.js
CHANGED
package/src/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import pkgJSON from '../package.json' with { type: 'json' }
|
|
|
8
8
|
|
|
9
9
|
const COMMANDS = {
|
|
10
10
|
avif: ['avif.js', 'Converts images to AVIF'],
|
|
11
|
+
png: ['png.sh', 'Optimizes PNG images with oxipng'],
|
|
11
12
|
sqcrop: ['sqcrop.js', 'Square crops images\n'],
|
|
12
13
|
|
|
13
14
|
resize: ['resize.js', 'Resizes videos or images'],
|
|
@@ -59,11 +60,11 @@ function main() {
|
|
|
59
60
|
|
|
60
61
|
if (opt === '-v' || opt === '--version') {
|
|
61
62
|
console.log(pkgJSON.version)
|
|
62
|
-
|
|
63
|
+
return
|
|
63
64
|
}
|
|
64
65
|
if (opt === '-h' || opt === '--help') {
|
|
65
66
|
console.log(HELP)
|
|
66
|
-
|
|
67
|
+
return
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
if (!opt) {
|
package/src/detectdups.js
CHANGED
|
@@ -41,7 +41,7 @@ async function main() {
|
|
|
41
41
|
|
|
42
42
|
if (values.help) {
|
|
43
43
|
console.log(HELP)
|
|
44
|
-
|
|
44
|
+
return
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
if (files.length !== 1)
|
|
@@ -62,15 +62,9 @@ async function main() {
|
|
|
62
62
|
? Number(values.duration)
|
|
63
63
|
: vDur > 60 ? 20 : vDur
|
|
64
64
|
|
|
65
|
-
if (isNaN(seek) || seek < 0)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (isNaN(duration) || duration < 1)
|
|
69
|
-
throw new Error(`Invalid --duration value: ${values.duration}`)
|
|
70
|
-
|
|
71
|
-
if ((seek + duration) > vDur)
|
|
72
|
-
throw new Error(`Invalid analysis range. Exceeds video duration: ${vDur}`)
|
|
73
|
-
|
|
65
|
+
if (isNaN(seek) || seek < 0) throw new Error(`Invalid --seek value: ${values.seek}`)
|
|
66
|
+
if (isNaN(duration) || duration < 1) throw new Error(`Invalid --duration value: ${values.duration}`)
|
|
67
|
+
if ((seek + duration) > vDur) throw new Error(`Invalid analysis range. Exceeds video duration: ${vDur}`)
|
|
74
68
|
|
|
75
69
|
const dups = await detectdups(files[0], seek, duration)
|
|
76
70
|
const h = deltaHistogram(dups)
|
package/src/dropdups.js
CHANGED
|
@@ -4,10 +4,10 @@ import { resolve, parse, format } from 'node:path'
|
|
|
4
4
|
|
|
5
5
|
import { parseOptions } from './utils/parseOptions.js'
|
|
6
6
|
import { ffmpeg, assertUserHasFFmpeg, run } from './utils/subprocess.js'
|
|
7
|
-
import {
|
|
7
|
+
import { ProresProfiles } from './prores.js'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const PROFILE =
|
|
10
|
+
const PROFILE = ProresProfiles.default
|
|
11
11
|
|
|
12
12
|
const HELP = `
|
|
13
13
|
SYNOPSIS
|
|
@@ -40,7 +40,7 @@ async function main() {
|
|
|
40
40
|
|
|
41
41
|
if (values.help) {
|
|
42
42
|
console.log(HELP)
|
|
43
|
-
|
|
43
|
+
return
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (!files.length)
|
|
@@ -65,9 +65,7 @@ async function dropdups(video, dupFrameNum) {
|
|
|
65
65
|
? `decimate=cycle=${dupFrameNum}`
|
|
66
66
|
: 'mpdecimate,setpts=N/FRAME_RATE/TB',
|
|
67
67
|
'-fps_mode', 'cfr',
|
|
68
|
-
'-c:v', 'prores_ks',
|
|
69
|
-
'-profile:v', PROFILE,
|
|
70
|
-
'-pix_fmt', 'yuv422p10le',
|
|
68
|
+
'-c:v', 'prores_ks', '-profile:v', PROFILE,
|
|
71
69
|
makeOutputPath(video)
|
|
72
70
|
])
|
|
73
71
|
}
|
package/src/edgespic.js
CHANGED
package/src/flattendir.sh
CHANGED
|
@@ -9,17 +9,11 @@ DESCRIPTION
|
|
|
9
9
|
Moves unique files from subdirectories into the top-level folder, then
|
|
10
10
|
deletes empty directories. Defaults to the current working directory.
|
|
11
11
|
EOF
|
|
12
|
+
exit "${1:-0}"
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
exit 0
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
if [ $# -gt 0 ] && [ ! -d "$1" ]; then
|
|
20
|
-
help
|
|
21
|
-
exit 1
|
|
22
|
-
fi
|
|
15
|
+
[ "$1" = "-h" ] && help
|
|
16
|
+
[ $# -gt 0 ] && [ ! -d "$1" ] && help 1
|
|
23
17
|
|
|
24
18
|
DIR="${1:-$(pwd)}"
|
|
25
19
|
|
package/src/framediff.sh
CHANGED
|
@@ -16,17 +16,11 @@ TIPS
|
|
|
16
16
|
SEE ALSO
|
|
17
17
|
mediasnacks detectdups, ffplay(1)
|
|
18
18
|
EOF
|
|
19
|
+
exit "${1:-0}"
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
exit 0
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
if [ ! -f "$1" ]; then
|
|
27
|
-
help
|
|
28
|
-
exit 1
|
|
29
|
-
fi
|
|
22
|
+
[ "$1" = "-h" ] && help
|
|
23
|
+
[ ! -f "$1" ] && help 1
|
|
30
24
|
|
|
31
25
|
ffplay -v error "$1" -vf "
|
|
32
26
|
tblend=all_mode=difference,
|
package/src/gif.sh
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
# Converts to GIF
|
|
4
|
-
|
|
5
3
|
FPS=10
|
|
6
4
|
WIDTH=600
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
help() {
|
|
7
|
+
/bin/cat << EOF
|
|
8
|
+
SYNOPSIS
|
|
9
|
+
mediasnacks gif [--fps <number>] [-w | --width <pixels>] <file>
|
|
10
|
+
|
|
11
|
+
DESCRIPTION
|
|
12
|
+
Converts video to GIF
|
|
13
|
+
|
|
14
|
+
OPTIONS
|
|
15
|
+
--fps Default: $FPS
|
|
16
|
+
-w,--width Default: $WIDTH
|
|
17
|
+
EOF
|
|
18
|
+
exit "${1:-0}"
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
while [ $# -gt 0 ]; do
|
|
@@ -15,18 +23,21 @@ while [ $# -gt 0 ]; do
|
|
|
15
23
|
--fps)
|
|
16
24
|
FPS="$2";
|
|
17
25
|
shift 2 ;;
|
|
18
|
-
|
|
26
|
+
|
|
27
|
+
--width|-w)
|
|
19
28
|
WIDTH="$2"
|
|
20
29
|
shift 2 ;;
|
|
30
|
+
|
|
21
31
|
--help|-h)
|
|
22
|
-
|
|
32
|
+
help ;;
|
|
33
|
+
|
|
23
34
|
*)
|
|
24
35
|
file="$1"
|
|
25
36
|
shift ;;
|
|
26
37
|
esac
|
|
27
38
|
done
|
|
28
39
|
|
|
29
|
-
[ -z "$file" ] &&
|
|
40
|
+
[ -z "$file" ] && help 1
|
|
30
41
|
|
|
31
42
|
ffmpeg -v error -i "$file" \
|
|
32
43
|
-vf "fps=${FPS},scale=${WIDTH}:-1" \
|
package/src/hev1tohvc1.js
CHANGED
|
@@ -19,12 +19,18 @@ DESCRIPTION
|
|
|
19
19
|
async function main() {
|
|
20
20
|
await assertUserHasFFmpeg()
|
|
21
21
|
|
|
22
|
-
const { files } = await parseOptions(
|
|
22
|
+
const { values, files } = await parseOptions({
|
|
23
|
+
help: { short: 'h', type: 'boolean' }
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (values.help) {
|
|
27
|
+
console.log(HELP)
|
|
28
|
+
return
|
|
29
|
+
}
|
|
23
30
|
|
|
24
31
|
if (!files.length)
|
|
25
32
|
throw new Error(HELP)
|
|
26
33
|
|
|
27
|
-
console.log('HEV1 to HVC1…')
|
|
28
34
|
for (const file of files)
|
|
29
35
|
await hev1tohvc1(file)
|
|
30
36
|
}
|
package/src/moov2front.js
CHANGED
|
@@ -23,7 +23,14 @@ NOTES
|
|
|
23
23
|
async function main() {
|
|
24
24
|
await assertUserHasFFmpeg()
|
|
25
25
|
|
|
26
|
-
const { files } = await parseOptions(
|
|
26
|
+
const { values, files } = await parseOptions({
|
|
27
|
+
help: { short: 'h', type: 'boolean' }
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (values.help) {
|
|
31
|
+
console.log(HELP)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
if (!files.length)
|
|
29
36
|
throw new Error(HELP)
|
package/src/play.js
CHANGED
|
@@ -25,7 +25,7 @@ async function main() {
|
|
|
25
25
|
|
|
26
26
|
if (values.help) {
|
|
27
27
|
console.log(HELP)
|
|
28
|
-
|
|
28
|
+
return
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const files = findFiles({
|
|
@@ -54,6 +54,7 @@ function play(files) {
|
|
|
54
54
|
console.error('Error: MPV is not installed')
|
|
55
55
|
else
|
|
56
56
|
console.log(err)
|
|
57
|
+
process.exit(1)
|
|
57
58
|
})
|
|
58
59
|
}
|
|
59
60
|
|
package/src/png.sh
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
help() {
|
|
4
|
+
/bin/cat << EOF
|
|
5
|
+
SYNOPSIS
|
|
6
|
+
mediasnacks png <img1> [img2 ...]
|
|
7
|
+
|
|
8
|
+
DESCRIPTION
|
|
9
|
+
Losslessly optimizes PNG images with oxipng at max level.
|
|
10
|
+
|
|
11
|
+
EXAMPLE
|
|
12
|
+
mediasnacks png *.png
|
|
13
|
+
EOF
|
|
14
|
+
exit "${1:-0}"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
[ "$1" = "-h" ] && help
|
|
18
|
+
[ $# -eq 0 ] && help 1
|
|
19
|
+
|
|
20
|
+
oxipng --opt max "$@"
|
package/src/prores.js
CHANGED
|
@@ -3,13 +3,24 @@ import { resolve, parse, join } from 'node:path'
|
|
|
3
3
|
import { parseOptions } from './utils/parseOptions.js'
|
|
4
4
|
import { assertUserHasFFmpeg, run } from './utils/subprocess.js'
|
|
5
5
|
|
|
6
|
-
export const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
export const ProresProfiles = new class {
|
|
7
|
+
// https://github.com/oyvindln/vhs-decode/wiki/ProRes-The-Definitive-FFmpeg-Guide#profiles-can-be-the-following
|
|
8
|
+
profiles = {
|
|
9
|
+
// 10-bit
|
|
10
|
+
0: 'proxy',
|
|
11
|
+
1: 'lt',
|
|
12
|
+
2: 'standard',
|
|
13
|
+
3: 'hq',
|
|
14
|
+
|
|
15
|
+
// 12-bit
|
|
16
|
+
4: '4444',
|
|
17
|
+
5: '4444xq'
|
|
18
|
+
}
|
|
19
|
+
default = 3
|
|
20
|
+
|
|
21
|
+
list = () => Object.keys(this.profiles)
|
|
22
|
+
isValid = n => Object.hasOwn(this.profiles, n)
|
|
23
|
+
table = () => Object.entries(this.profiles)
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
const HELP = `
|
|
@@ -17,15 +28,21 @@ SYNOPSIS
|
|
|
17
28
|
mediasnacks prores [options] <video>
|
|
18
29
|
|
|
19
30
|
DESCRIPTION
|
|
20
|
-
Converts a video to ProRes
|
|
31
|
+
Converts a video to ProRes
|
|
21
32
|
|
|
22
33
|
OPTIONS
|
|
23
|
-
-p, --profile <n>
|
|
34
|
+
-p, --profile <n> Default: 3 (422 HQ 10-bit)
|
|
35
|
+
-s, --start <time> Start time (e.g. 5.0). Default: beginning.
|
|
36
|
+
-e, --end <time> End time (e.g. 0:10.0). Default: end of video.
|
|
24
37
|
-h, --help
|
|
25
38
|
|
|
39
|
+
PROFILES
|
|
40
|
+
${ProresProfiles.table().map(([num, name]) =>
|
|
41
|
+
` ${num}: ${name}`).join('\n')}
|
|
42
|
+
|
|
26
43
|
EXAMPLES
|
|
27
44
|
mediasnacks prores video.mov
|
|
28
|
-
mediasnacks prores
|
|
45
|
+
mediasnacks prores -p2 *.mov
|
|
29
46
|
|
|
30
47
|
Both output a file named: video.prores.mov
|
|
31
48
|
`.trim()
|
|
@@ -35,15 +52,20 @@ async function main() {
|
|
|
35
52
|
await assertUserHasFFmpeg()
|
|
36
53
|
|
|
37
54
|
const { values, files } = await parseOptions({
|
|
38
|
-
profile: { short: 'p', type: 'string', default: String(
|
|
55
|
+
profile: { short: 'p', type: 'string', default: String(ProresProfiles.default) },
|
|
56
|
+
start: { short: 's', type: 'string', default: '' },
|
|
57
|
+
end: { short: 'e', type: 'string', default: '' },
|
|
39
58
|
help: { short: 'h', type: 'boolean' },
|
|
40
59
|
})
|
|
41
60
|
|
|
42
61
|
if (values.help) {
|
|
43
62
|
console.log(HELP)
|
|
44
|
-
|
|
63
|
+
return
|
|
45
64
|
}
|
|
46
65
|
|
|
66
|
+
if (!ProresProfiles.isValid(Number(values.profile)))
|
|
67
|
+
throw new Error('Invalid profile. Must be one of: ' + ProresProfiles.list().join(','))
|
|
68
|
+
|
|
47
69
|
if (files.length !== 1)
|
|
48
70
|
throw new Error('Expected 1 argument: video file. See mediasnacks prores --help')
|
|
49
71
|
|
|
@@ -51,20 +73,19 @@ async function main() {
|
|
|
51
73
|
const { name, dir } = parse(video)
|
|
52
74
|
const output = join(dir, `${name}.prores.mov`)
|
|
53
75
|
|
|
54
|
-
|
|
55
|
-
await prores(video, values.profile, output)
|
|
76
|
+
await prores(video, values.start, values.end, values.profile, output)
|
|
56
77
|
}
|
|
57
78
|
|
|
58
|
-
async function prores(video, profile, output) {
|
|
79
|
+
async function prores(video, start, end, profile, output) {
|
|
59
80
|
await run('ffmpeg', [
|
|
60
81
|
'-v', 'error',
|
|
61
82
|
'-stats',
|
|
83
|
+
start ? ['-ss', start] : [],
|
|
84
|
+
end ? ['-to', end] : [],
|
|
62
85
|
'-i', video,
|
|
63
|
-
'-c:v', 'prores_ks',
|
|
64
|
-
'-profile:v', profile,
|
|
65
|
-
'-pix_fmt', 'yuv422p10le',
|
|
86
|
+
'-c:v', 'prores_ks', '-profile:v', profile,
|
|
66
87
|
output
|
|
67
|
-
])
|
|
88
|
+
].flat())
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
if (import.meta.main)
|
package/src/qdir.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
import { spawn } from 'node:child_process'
|
|
5
|
-
import { parseArgs } from 'node:util'
|
|
6
5
|
import { readdir, writeFile, unlink, rename } from 'node:fs/promises'
|
|
7
6
|
|
|
8
7
|
import { isFile } from './utils/fs-utils.js'
|
|
8
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
const HELP = `
|
|
@@ -14,20 +14,30 @@ SYNOPSIS
|
|
|
14
14
|
|
|
15
15
|
DESCRIPTION
|
|
16
16
|
Sequentially runs all *.sh files in a folder (cwd by default).
|
|
17
|
+
Completed scripts get renamed with a ".done" extension,
|
|
18
|
+
or to ".failed.$exitCode"
|
|
17
19
|
`.trim()
|
|
18
20
|
|
|
19
21
|
|
|
22
|
+
function filter(f) {
|
|
23
|
+
return f.endsWith('.sh')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function newExt(exitCode) {
|
|
27
|
+
return exitCode === 0
|
|
28
|
+
? '.done'
|
|
29
|
+
: `.failed.${exitCode}`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
20
33
|
async function main() {
|
|
21
|
-
const { values, positionals } =
|
|
22
|
-
|
|
23
|
-
help: { short: 'h', type: 'boolean' },
|
|
24
|
-
},
|
|
25
|
-
allowPositionals: true,
|
|
34
|
+
const { values, positionals } = await parseOptions({
|
|
35
|
+
help: { short: 'h', type: 'boolean' }
|
|
26
36
|
})
|
|
27
37
|
|
|
28
38
|
if (values.help) {
|
|
29
39
|
console.log(HELP)
|
|
30
|
-
|
|
40
|
+
return
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
const dir = positionals[0] || process.cwd()
|
|
@@ -55,12 +65,9 @@ export async function qdir(dir, pollIntervalMs = 10_000) {
|
|
|
55
65
|
|
|
56
66
|
const jobName = job.split('/').pop()
|
|
57
67
|
await writeFile(lock, jobName, 'utf8')
|
|
58
|
-
|
|
59
68
|
try {
|
|
60
69
|
const exitCode = await runShell(job)
|
|
61
|
-
await rename(job, job + (exitCode
|
|
62
|
-
? '.done'
|
|
63
|
-
: `.failed.${exitCode}`))
|
|
70
|
+
await rename(job, job + newExt(exitCode))
|
|
64
71
|
}
|
|
65
72
|
finally {
|
|
66
73
|
await unlink(lock).catch(() => {})
|
|
@@ -71,8 +78,8 @@ export async function qdir(dir, pollIntervalMs = 10_000) {
|
|
|
71
78
|
async function getNextJob(dir) {
|
|
72
79
|
const entries = await readdir(dir, { withFileTypes: true })
|
|
73
80
|
const scripts = entries
|
|
74
|
-
.filter(
|
|
75
|
-
.map(
|
|
81
|
+
.filter(entry => entry.isFile() && filter(entry.name))
|
|
82
|
+
.map(entry => entry.name)
|
|
76
83
|
.sort()
|
|
77
84
|
return scripts.length
|
|
78
85
|
? join(dir, scripts[0])
|
package/src/random.js
CHANGED
|
@@ -14,10 +14,8 @@ DESCRIPTION
|
|
|
14
14
|
`.trim()
|
|
15
15
|
|
|
16
16
|
async function main() {
|
|
17
|
-
if (process.platform !== 'darwin')
|
|
18
|
-
|
|
19
|
-
process.exit(1)
|
|
20
|
-
}
|
|
17
|
+
if (process.platform !== 'darwin')
|
|
18
|
+
throw new Error('Error: This command is only supported on macOS.')
|
|
21
19
|
|
|
22
20
|
const { values } = await parseOptions({
|
|
23
21
|
recursive: { short: 'r', type: 'boolean' },
|
|
@@ -26,7 +24,7 @@ async function main() {
|
|
|
26
24
|
|
|
27
25
|
if (values.help) {
|
|
28
26
|
console.log(HELP)
|
|
29
|
-
|
|
27
|
+
return
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
spawn('open', [pickRandomFile('.', values.recursive)])
|
package/src/resize.js
CHANGED
package/src/seqcheck.js
CHANGED
package/src/sqcrop.js
CHANGED
package/src/ssim.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { ffmpeg } from './utils/subprocess.js'
|
|
3
|
+
import { parseOptions } from './utils/parseOptions.js'
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
const HELP = `
|
|
@@ -7,19 +8,25 @@ SYNOPSIS
|
|
|
7
8
|
mediasnacks ssim <img1> <img2>
|
|
8
9
|
|
|
9
10
|
DESCRIPTION
|
|
10
|
-
Computes the Structural Similarity Index (SSIM) between two images using
|
|
11
|
+
Computes the Structural Similarity Index (SSIM) between two images using FFmpeg.
|
|
11
12
|
`.trim()
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
async function main() {
|
|
15
|
-
const
|
|
16
|
-
|
|
16
|
+
const { values, positionals } = await parseOptions({
|
|
17
|
+
help: { short: 'h', type: 'boolean' }
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (values.help) {
|
|
17
21
|
console.log(HELP)
|
|
18
|
-
|
|
22
|
+
return
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
if (positionals.length !== 2)
|
|
26
|
+
throw new Error('Expected two images')
|
|
27
|
+
|
|
28
|
+
const score = await ssim(...positionals)
|
|
29
|
+
console.log(score.toString())
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
export async function ssim(img1, img2) {
|
package/src/utils/subprocess.js
CHANGED
|
@@ -39,10 +39,9 @@ async function runSilently(program, args) {
|
|
|
39
39
|
|
|
40
40
|
export async function run(program, args) {
|
|
41
41
|
return new Promise((resolve, reject) => {
|
|
42
|
-
const p = spawn(program, args)
|
|
43
|
-
p.stdout.
|
|
44
|
-
p.stderr.
|
|
45
|
-
|
|
42
|
+
const p = spawn(program, args, { stdio: ['inherit', 'pipe', 'pipe'] })
|
|
43
|
+
p.stdout.pipe(process.stdout)
|
|
44
|
+
p.stderr.pipe(process.stderr)
|
|
46
45
|
p.on('error', reject)
|
|
47
46
|
p.on('close', code => {
|
|
48
47
|
if (code === 0)
|
package/src/vconcat.sh
CHANGED
|
@@ -7,30 +7,21 @@ SYNOPSIS
|
|
|
7
7
|
mediasnacks vconcat <video1> <video2> …
|
|
8
8
|
|
|
9
9
|
DESCRIPTION
|
|
10
|
-
Concatenates video files using FFmpeg
|
|
10
|
+
Concatenates video files using FFmpeg without re-encoding.
|
|
11
11
|
All videos must have compatible codecs and resolutions.
|
|
12
12
|
|
|
13
13
|
EXAMPLES
|
|
14
14
|
mediasnacks vconcat vid1.mov vid2.mov
|
|
15
15
|
mediasnacks vconcat *.mp4
|
|
16
16
|
EOF
|
|
17
|
+
exit "${1:-0}"
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
exit 0
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
if [ "$#" -lt 2 ]; then
|
|
25
|
-
help
|
|
26
|
-
exit 1
|
|
27
|
-
fi
|
|
20
|
+
[ "$1" = "-h" ] && help
|
|
21
|
+
[ "$#" -lt 2 ] && help 1
|
|
28
22
|
|
|
29
23
|
for arg in "$@"; do
|
|
30
|
-
|
|
31
|
-
help
|
|
32
|
-
exit 1
|
|
33
|
-
fi
|
|
24
|
+
[ ! -f "$arg" ] && help 1
|
|
34
25
|
done
|
|
35
26
|
|
|
36
27
|
list_file=$(mktemp -p .)
|
package/src/vdiff.sh
CHANGED
|
@@ -10,17 +10,11 @@ DESCRIPTION
|
|
|
10
10
|
Diffs two video files using FFplay with a blend filter.
|
|
11
11
|
Videos must have the same resolution.
|
|
12
12
|
EOF
|
|
13
|
+
exit "${1:-0}"
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
exit 0
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
if [ $# -lt 2 ] || [ ! -f "$1" ] || [ ! -f "$2" ]; then
|
|
21
|
-
help
|
|
22
|
-
exit 1
|
|
23
|
-
fi
|
|
16
|
+
[ "$1" = "-h" ] && help
|
|
17
|
+
[ $# -lt 2 ] || [ ! -f "$1" ] || [ ! -f "$2" ] && help 1
|
|
24
18
|
|
|
25
19
|
video1="$1"
|
|
26
20
|
video2="$2"
|
package/src/vsplit.js
CHANGED