mediasnacks 0.22.5 → 0.23.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 +1 -1
- 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 -1
- package/src/moov2front.js +8 -1
- package/src/play.js +2 -1
- package/src/png.sh +20 -0
- package/src/prores.js +1 -1
- 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
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,7 +19,14 @@ 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)
|
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
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