mediasnacks 0.22.2 → 0.22.3
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/package.json +1 -1
- package/src/dropdups.js +18 -13
- package/src/edgespic.js +4 -2
- package/src/flattendir.sh +20 -3
- package/src/framediff.sh +17 -3
- package/src/moov2front.js +1 -1
- package/src/play.js +24 -25
- package/src/prores.js +2 -4
- package/src/random.js +1 -1
- package/src/resize.js +0 -1
- package/src/seqcheck.js +0 -1
- package/src/ssim.js +0 -1
- package/src/utils/fs-utils.js +11 -1
- package/src/vconcat.sh +27 -3
- package/src/vdiff.sh +22 -3
package/package.json
CHANGED
package/src/dropdups.js
CHANGED
|
@@ -11,17 +11,22 @@ const PROFILE = PRORES_PROFILES.hq
|
|
|
11
11
|
|
|
12
12
|
const HELP = `
|
|
13
13
|
SYNOPSIS
|
|
14
|
-
mediasnacks dropdups [-n <
|
|
14
|
+
mediasnacks dropdups [-n <dup-frame-num>] <video>
|
|
15
15
|
|
|
16
16
|
DESCRIPTION
|
|
17
17
|
Removes sequentially duplicate frames and outputs ProRes 422 HQ.
|
|
18
18
|
|
|
19
19
|
OPTIONS
|
|
20
|
-
-n, --
|
|
21
|
-
|
|
22
|
-
Ex.A: Use n=2 when every other frame is repeated.
|
|
23
|
-
Ex.B: Use n=6 if e.g., a 25 fps got upped to 30 fps without interpolation.
|
|
20
|
+
-n, --dup-frame-num <n> Known frame interval to drop.
|
|
21
|
+
Default: n=0, which auto-detects repeated frames (slower)
|
|
24
22
|
-h, --help
|
|
23
|
+
|
|
24
|
+
EXAMPLES
|
|
25
|
+
Use n=2 when every other frame is repeated:
|
|
26
|
+
mediasnacks dropdups -n2 vid.mov
|
|
27
|
+
|
|
28
|
+
Use n=6 if e.g., a 25 fps got upped to 30 fps without interpolation.
|
|
29
|
+
mediasnacks dropdups -n6 vid.mov
|
|
25
30
|
`.trim()
|
|
26
31
|
|
|
27
32
|
|
|
@@ -29,7 +34,7 @@ async function main() {
|
|
|
29
34
|
await assertUserHasFFmpeg()
|
|
30
35
|
|
|
31
36
|
const { values, files } = await parseOptions({
|
|
32
|
-
'
|
|
37
|
+
'dup-frame-num': { short: 'n', type: 'string', default: '' },
|
|
33
38
|
help: { short: 'h', type: 'boolean' },
|
|
34
39
|
})
|
|
35
40
|
|
|
@@ -41,23 +46,23 @@ async function main() {
|
|
|
41
46
|
if (!files.length)
|
|
42
47
|
throw new Error('No video specified. See mediasnacks dropdups --help')
|
|
43
48
|
|
|
44
|
-
let
|
|
45
|
-
if (
|
|
46
|
-
throw new Error('Invalid
|
|
49
|
+
let dupFrameNum = values['dup-frame-num']
|
|
50
|
+
if (dupFrameNum && !Number.isInteger(+dupFrameNum))
|
|
51
|
+
throw new Error('Invalid -n. It must be a positive integer.')
|
|
47
52
|
|
|
48
53
|
console.log('Dropping Duplicate Frames…')
|
|
49
54
|
for (const file of files)
|
|
50
|
-
await dropdups(resolve(file),
|
|
55
|
+
await dropdups(resolve(file), dupFrameNum)
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
async function dropdups(video,
|
|
58
|
+
async function dropdups(video, dupFrameNum) {
|
|
54
59
|
await run('ffmpeg', [
|
|
55
60
|
'-v', 'error',
|
|
56
61
|
'-stats',
|
|
57
62
|
'-an',
|
|
58
63
|
'-i', video,
|
|
59
|
-
'-vf',
|
|
60
|
-
? `decimate=cycle=${
|
|
64
|
+
'-vf', dupFrameNum
|
|
65
|
+
? `decimate=cycle=${dupFrameNum}`
|
|
61
66
|
: 'mpdecimate,setpts=N/FRAME_RATE/TB',
|
|
62
67
|
'-fps_mode', 'cfr',
|
|
63
68
|
'-c:v', 'prores_ks',
|
package/src/edgespic.js
CHANGED
|
@@ -13,10 +13,12 @@ SYNOPSIS
|
|
|
13
13
|
|
|
14
14
|
DESCRIPTION
|
|
15
15
|
Extracts the first and last frames from each video and saves them to the 'edgepics/' subfolder.
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
OPTIONS
|
|
18
|
+
-w, --width Default:640 The aspect ratio is preserved.
|
|
17
19
|
|
|
18
20
|
EXAMPLES
|
|
19
|
-
mediasnacks edgespic
|
|
21
|
+
mediasnacks edgespic -w 800 *.mov
|
|
20
22
|
mediasnacks edgespic -w 600 'videos/**/*.mp4'
|
|
21
23
|
`.trim()
|
|
22
24
|
|
package/src/flattendir.sh
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
help() {
|
|
4
|
+
/bin/cat << EOF
|
|
5
|
+
SYNOPSIS
|
|
6
|
+
mediasnacks flattendir [folder]
|
|
7
|
+
|
|
8
|
+
DESCRIPTION
|
|
9
|
+
Moves unique files from subdirectories into the top-level folder, then
|
|
10
|
+
deletes empty directories. Defaults to the current working directory.
|
|
11
|
+
EOF
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
|
15
|
+
help
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [ $# -gt 0 ] && [ ! -d "$1" ]; then
|
|
20
|
+
help
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
6
23
|
|
|
7
24
|
DIR="${1:-$(pwd)}"
|
|
8
25
|
|
package/src/framediff.sh
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
/bin/cat << EOF
|
|
4
|
+
help() {
|
|
5
|
+
/bin/cat << EOF
|
|
6
6
|
SYNOPSIS
|
|
7
7
|
mediasnacks framediff <video>
|
|
8
8
|
|
|
9
9
|
DESCRIPTION
|
|
10
10
|
Runs FFplay with a video filter for diffing adjacent frames. Useful for
|
|
11
|
-
finding duplicate frames, which will show up
|
|
11
|
+
finding duplicate frames, which will show up as a black frame.
|
|
12
12
|
|
|
13
13
|
TIPS
|
|
14
14
|
Hit [s] to step frame-by-frame.
|
|
@@ -16,10 +16,24 @@ TIPS
|
|
|
16
16
|
SEE ALSO
|
|
17
17
|
mediasnacks detectdups, ffplay(1)
|
|
18
18
|
EOF
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
|
22
|
+
help
|
|
19
23
|
exit 0
|
|
20
24
|
fi
|
|
21
25
|
|
|
26
|
+
if [ ! -f "$1" ]; then
|
|
27
|
+
help
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
22
31
|
ffplay -v error "$1" -vf "
|
|
23
32
|
tblend=all_mode=difference,
|
|
24
33
|
format=gray
|
|
25
34
|
"
|
|
35
|
+
|
|
36
|
+
# Not rendering the frame number:
|
|
37
|
+
# 'drawtext=text=%{n}:x=20:y=20:fontcolor=white:fontsize=48'
|
|
38
|
+
# …because in homebrew ffmpeg 8 is not compiled with the req libs:
|
|
39
|
+
# https://ayosec.github.io/ffmpeg-filters-docs/8.0/Filters/Video/drawtext.html
|
package/src/moov2front.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
import { ffmpeg, assertUserHasFFmpeg } from './utils/subprocess.js'
|
|
4
3
|
import { uniqueFilenameFor, overwrite } from './utils/fs-utils.js'
|
|
5
4
|
import { parseOptions } from './utils/parseOptions.js'
|
|
@@ -20,6 +19,7 @@ NOTES
|
|
|
20
19
|
Files are overwritten.
|
|
21
20
|
`.trim()
|
|
22
21
|
|
|
22
|
+
|
|
23
23
|
async function main() {
|
|
24
24
|
await assertUserHasFFmpeg()
|
|
25
25
|
|
package/src/play.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { join } from 'node:path'
|
|
3
2
|
import { spawn } from 'node:child_process'
|
|
4
|
-
import { readdirSync } from 'node:fs'
|
|
5
3
|
import { parseOptions } from './utils/parseOptions.js'
|
|
4
|
+
import { findFiles } from './utils/fs-utils.js'
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
const HELP = `
|
|
9
8
|
SYNOPSIS
|
|
10
|
-
mediasnacks play [--no-recursive] [
|
|
9
|
+
mediasnacks play [--no-recursive] [query ...]
|
|
11
10
|
|
|
12
11
|
DESCRIPTION
|
|
13
12
|
Plays a filtered playlist with mpv.
|
|
@@ -29,33 +28,33 @@ async function main() {
|
|
|
29
28
|
process.exit(0)
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
: ''
|
|
35
|
-
|
|
31
|
+
const files = findFiles({
|
|
32
|
+
dir: '.',
|
|
33
|
+
regex: new RegExp(positionals.join('|'), 'i'),
|
|
34
|
+
recursive: values.recursive,
|
|
35
|
+
ignoredDirs: ['.fcpbundle/']
|
|
36
|
+
})
|
|
36
37
|
|
|
37
|
-
if (!files.length)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (!files.length)
|
|
39
|
+
throw new Error('No matching files found.')
|
|
40
|
+
|
|
41
|
+
play(files)
|
|
42
|
+
}
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
function play(files) {
|
|
45
|
+
const mpv = spawn('mpv', ['--playlist=-'], {
|
|
43
46
|
detached: true,
|
|
44
47
|
stdio: ['pipe', 'ignore', 'ignore']
|
|
45
48
|
})
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
&& !entry.name.startsWith('.')
|
|
56
|
-
&& !IGNORED_DIRS.some(d => entry.parentPath.includes(d))
|
|
57
|
-
&& regex.test(entry.name))
|
|
58
|
-
.map(entry => join(entry.parentPath, entry.name))
|
|
49
|
+
mpv.stdin.end(files.join('\n'))
|
|
50
|
+
mpv.unref()
|
|
51
|
+
|
|
52
|
+
mpv.on('error', function (err) {
|
|
53
|
+
if (err.code === 'ENOENT')
|
|
54
|
+
console.error('Error: MPV is not installed')
|
|
55
|
+
else
|
|
56
|
+
console.log(err)
|
|
57
|
+
})
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
main().catch(err => {
|
package/src/prores.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
import { resolve, parse, join } from 'node:path'
|
|
4
|
-
|
|
5
3
|
import { parseOptions } from './utils/parseOptions.js'
|
|
6
4
|
import { assertUserHasFFmpeg, run } from './utils/subprocess.js'
|
|
7
5
|
|
|
@@ -23,7 +21,7 @@ DESCRIPTION
|
|
|
23
21
|
|
|
24
22
|
OPTIONS
|
|
25
23
|
-p, --profile <n> ProRes profile (default: 3 (422 HQ))
|
|
26
|
-
-h, --help
|
|
24
|
+
-h, --help
|
|
27
25
|
|
|
28
26
|
EXAMPLES
|
|
29
27
|
mediasnacks prores video.mov
|
|
@@ -37,8 +35,8 @@ async function main() {
|
|
|
37
35
|
await assertUserHasFFmpeg()
|
|
38
36
|
|
|
39
37
|
const { values, files } = await parseOptions({
|
|
40
|
-
help: { short: 'h', type: 'boolean' },
|
|
41
38
|
profile: { short: 'p', type: 'string', default: String(PRORES_PROFILES.hq) },
|
|
39
|
+
help: { short: 'h', type: 'boolean' },
|
|
42
40
|
})
|
|
43
41
|
|
|
44
42
|
if (values.help) {
|
package/src/random.js
CHANGED
package/src/resize.js
CHANGED
package/src/seqcheck.js
CHANGED
package/src/ssim.js
CHANGED
package/src/utils/fs-utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { lstatSync } from 'node:fs'
|
|
2
1
|
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { lstatSync, readdirSync } from 'node:fs'
|
|
3
3
|
import { mkdir, unlink, rename } from 'node:fs/promises'
|
|
4
4
|
import { dirname, extname, join } from 'node:path'
|
|
5
5
|
|
|
@@ -33,3 +33,13 @@ export async function mkDir(path) {
|
|
|
33
33
|
throw err
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
export function findFiles({ dir, regex, recursive, ignoredDirs }) {
|
|
38
|
+
return readdirSync(dir, { withFileTypes: true, recursive })
|
|
39
|
+
.filter(entry =>
|
|
40
|
+
entry.isFile()
|
|
41
|
+
&& !entry.name.startsWith('.')
|
|
42
|
+
&& !ignoredDirs.some(d => entry.parentPath.includes(d))
|
|
43
|
+
&& regex.test(entry.name))
|
|
44
|
+
.map(entry => join(entry.parentPath, entry.name))
|
|
45
|
+
}
|
package/src/vconcat.sh
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
help() {
|
|
4
5
|
cat << EOF
|
|
6
|
+
SYNOPSIS
|
|
7
|
+
mediasnacks vconcat <video1> <video2> …
|
|
8
|
+
|
|
9
|
+
DESCRIPTION
|
|
10
|
+
Concatenates video files using FFmpeg's without re-encoding.
|
|
11
|
+
All videos must have compatible codecs and resolutions.
|
|
12
|
+
|
|
5
13
|
EXAMPLES
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
mediasnacks vconcat vid1.mov vid2.mov
|
|
15
|
+
mediasnacks vconcat *.mp4
|
|
8
16
|
EOF
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
|
20
|
+
help
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if [ "$#" -lt 2 ]; then
|
|
25
|
+
help
|
|
9
26
|
exit 1
|
|
10
27
|
fi
|
|
11
28
|
|
|
29
|
+
for arg in "$@"; do
|
|
30
|
+
if [ ! -f "$arg" ]; then
|
|
31
|
+
help
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
done
|
|
35
|
+
|
|
12
36
|
list_file=$(mktemp -p .)
|
|
13
37
|
for file in "$@"; do
|
|
14
38
|
fname=$(printf '%s' "$file" | sed "s/'/'\\\\''/g") # Escape single quotes
|
package/src/vdiff.sh
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
help() {
|
|
5
|
+
/bin/cat << EOF
|
|
6
|
+
SYNOPSIS
|
|
7
|
+
mediasnacks vdiff <video1> <video2>
|
|
8
|
+
|
|
9
|
+
DESCRIPTION
|
|
10
|
+
Diffs two video files using FFplay with a blend filter. Videos must have
|
|
11
|
+
the same resolution and ideally the same framerate.
|
|
12
|
+
EOF
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
|
16
|
+
help
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [ $# -lt 2 ] || [ ! -f "$1" ] || [ ! -f "$2" ]; then
|
|
21
|
+
help
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
5
25
|
video1="$1"
|
|
6
26
|
video2="$2"
|
|
7
|
-
|
|
8
27
|
ffprobe -v error "$video1" || exit 1
|
|
9
28
|
ffprobe -v error "$video2" || exit 1
|
|
10
29
|
|