mediasnacks 0.12.2 → 0.14.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/.zsh/completions/_mediasnacks +3 -2
- package/README.md +2 -1
- package/package.json +1 -1
- package/src/cli.js +2 -1
- package/src/utils/parseOptions.test.js +3 -3
- package/src/vsplit.sh +50 -0
- package/src/vtrim.sh +35 -0
- package/tests/avif.test.js +4 -11
- package/tests/fixtures/60fps.mp4 +0 -0
- package/tests/fixtures/60fps_1.mp4 +0 -0
- package/tests/fixtures/60fps_2.mp4 +0 -0
- package/tests/fixtures/60fps_3.mp4 +0 -0
- package/tests/fixtures/60fps_4.mp4 +0 -0
- package/tests/fixtures/60fps_5.mp4 +0 -0
- package/tests/fixtures/60fps_6.mp4 +0 -0
- package/tests/utils.js +6 -0
- package/tests/vsplit.test.js +22 -0
- package/tests/vtrim.test.js +20 -0
- package/src/vcut.sh +0 -28
|
@@ -12,13 +12,14 @@ _mediasnacks_commands=(
|
|
|
12
12
|
'framediff:ffplay with a filter for diffing adjacent frames'
|
|
13
13
|
'vdiff:Plays a video with the difference of two videos'
|
|
14
14
|
'vconcat:Concatenates videos'
|
|
15
|
+
'vtrim:Trims video from start to end time'
|
|
15
16
|
'dlaudio: yt-dlp best audio'
|
|
16
17
|
'dlvideo: yt-dlp best video'
|
|
17
18
|
'unemoji:Removes emojis from filenames'
|
|
18
19
|
'rmcover:Removes cover art'
|
|
19
20
|
'curltime:Measures request response timings'
|
|
20
21
|
'gif:Video to GIF'
|
|
21
|
-
'
|
|
22
|
+
'vsplit:Split video at multiple time points'
|
|
22
23
|
'flattendir:Moves unique files to the top dir and deletes empty dirs'
|
|
23
24
|
)
|
|
24
25
|
|
|
@@ -29,7 +30,7 @@ fi
|
|
|
29
30
|
|
|
30
31
|
local cmd="$words[2]"
|
|
31
32
|
case "$cmd" in
|
|
32
|
-
avif|resize|sqcrop|moov2front|dropdups|seqcheck|hev1tohvc1|framediff|vdiff|vconcat|dlaudio|dlvideo|unemoji|rmcover|curltime|gif|
|
|
33
|
+
avif|resize|sqcrop|moov2front|dropdups|seqcheck|hev1tohvc1|framediff|vdiff|vconcat|vtrim|dlaudio|dlvideo|unemoji|rmcover|curltime|gif|vsplit|flattendir)
|
|
33
34
|
_files
|
|
34
35
|
;;
|
|
35
36
|
qdir)
|
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Commands:
|
|
|
23
23
|
- `framediff`: Plays a video of adjacent frames diff
|
|
24
24
|
- `vdiff`: Plays a video with the difference of two videos
|
|
25
25
|
- `vconcat`: Concatenates videos
|
|
26
|
+
- `vtrim`: Trims video from start to end time
|
|
26
27
|
|
|
27
28
|
- `dlaudio`: yt-dlp best audio
|
|
28
29
|
- `dlvideo`: yt-dlp best video
|
|
@@ -32,7 +33,7 @@ Commands:
|
|
|
32
33
|
|
|
33
34
|
- `curltime`: Measures request response timings
|
|
34
35
|
- `gif`: Video to GIF
|
|
35
|
-
- `
|
|
36
|
+
- `vsplit`: Split video at multiple time points
|
|
36
37
|
|
|
37
38
|
- `flattendir`: Moves unique files to the top dir and deletes empty dirs
|
|
38
39
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -20,6 +20,7 @@ const COMMANDS = {
|
|
|
20
20
|
vdiff: ['vdiff.sh', 'Plays a video with the difference of two videos'],
|
|
21
21
|
|
|
22
22
|
vconcat: ['vconcat.sh', 'Concatenates videos'],
|
|
23
|
+
vtrim: ['vtrim.sh', 'Trims video from start to end time'],
|
|
23
24
|
|
|
24
25
|
dlaudio: ['dlaudio.sh', 'yt-dlp best audio'],
|
|
25
26
|
dlvideo: ['dlvideo.sh', 'yt-dlp best video'],
|
|
@@ -29,7 +30,7 @@ const COMMANDS = {
|
|
|
29
30
|
|
|
30
31
|
curltime: ['curltime.sh', 'Measures request response timings'],
|
|
31
32
|
gif: ['gif.sh', 'Video to GIF'],
|
|
32
|
-
|
|
33
|
+
vsplit: ['vsplit.sh', 'Split video at multiple time points'],
|
|
33
34
|
flattendir: ['flattendir.sh', 'Moves all files to top dir and deletes dirs']
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
import { tmpdir } from 'node:os'
|
|
3
3
|
import { equal, deepEqual } from 'node:assert/strict'
|
|
4
|
-
import { mkdtemp, writeFile,
|
|
4
|
+
import { mkdtemp, writeFile, rmdir } from 'node:fs/promises'
|
|
5
5
|
import { test, describe, before, after } from 'node:test'
|
|
6
6
|
|
|
7
7
|
import { parseOptions } from './parseOptions.js'
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
describe('
|
|
10
|
+
describe('parseOptions', () => {
|
|
11
11
|
let testDir
|
|
12
12
|
let inTmpDir = f => join(testDir, f)
|
|
13
13
|
const testFiles = ['file1.png', 'file2.png', 'file3.png']
|
|
@@ -18,7 +18,7 @@ describe('parseArgsWithGlobs', () => {
|
|
|
18
18
|
await writeFile(inTmpDir(file), '')
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
after(() =>
|
|
21
|
+
after(() => rmdir(testDir, { recursive: true }))
|
|
22
22
|
|
|
23
23
|
test('parses args and globs files', async () => {
|
|
24
24
|
const { values, files } = await parseOptions({
|
package/src/vsplit.sh
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
|
|
3
|
+
if [ "$#" -lt 2 ]; then
|
|
4
|
+
cat << EOF
|
|
5
|
+
Usage:
|
|
6
|
+
$(basename $0) <split-time-1> [split-time-2 ...] <video-file>
|
|
7
|
+
|
|
8
|
+
Examples:
|
|
9
|
+
$(basename $0) 123.45 video.mp4
|
|
10
|
+
$(basename $0) 60 120 180 video.mov
|
|
11
|
+
$(basename $0) 00:01:00 00:02:00 00:03:00 video.mkv
|
|
12
|
+
EOF
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
VIDEO="${@[-1]}"
|
|
17
|
+
SPLITS=("${@[1,-2]}")
|
|
18
|
+
|
|
19
|
+
if [ ! -f "$VIDEO" ]; then
|
|
20
|
+
echo "Error: file not found: $VIDEO"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
DIRNAME=$(dirname "$VIDEO")
|
|
25
|
+
BASENAME=$(basename "$VIDEO")
|
|
26
|
+
EXT="${BASENAME##*.}"
|
|
27
|
+
NAME="${BASENAME%.*}"
|
|
28
|
+
|
|
29
|
+
N_CLIPS=$((${#SPLITS[@]} + 1))
|
|
30
|
+
|
|
31
|
+
for (( i=1; i<=$N_CLIPS; i++ )); do
|
|
32
|
+
outfile="$DIRNAME/${NAME}_${i}.$EXT"
|
|
33
|
+
|
|
34
|
+
if [ $i -eq 1 ]; then # First clip: [start, first_split]
|
|
35
|
+
ffmpeg -v error -y -i "$VIDEO" \
|
|
36
|
+
-t "${SPLITS[1]}" \
|
|
37
|
+
-c copy "$outfile"
|
|
38
|
+
|
|
39
|
+
elif [ $i -eq $N_CLIPS ]; then # Last clip: [last_split, end]
|
|
40
|
+
ffmpeg -v error -y -i "$VIDEO" \
|
|
41
|
+
-ss "${SPLITS[-1]}" \
|
|
42
|
+
-c copy "$outfile"
|
|
43
|
+
|
|
44
|
+
else # Middle clip: [split[i-1], split[i]]
|
|
45
|
+
ffmpeg -v error -y -i "$VIDEO" \
|
|
46
|
+
-ss "${SPLITS[$((i-1))]}" \
|
|
47
|
+
-to "${SPLITS[$i]}" \
|
|
48
|
+
-c copy "$outfile"
|
|
49
|
+
fi
|
|
50
|
+
done
|
package/src/vtrim.sh
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
|
|
3
|
+
if [ "$#" -ne 3 ]; then
|
|
4
|
+
cat << EOF
|
|
5
|
+
Usage:
|
|
6
|
+
$(basename $0) <start-time> <end-time> <video-file>
|
|
7
|
+
|
|
8
|
+
Examples:
|
|
9
|
+
$(basename $0) 10 30 input.mp4
|
|
10
|
+
$(basename $0) 00:00:10 00:00:30 input.mkv
|
|
11
|
+
$(basename $0) 1:23.5 2:45.0 video.mov
|
|
12
|
+
EOF
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
START="$1"
|
|
17
|
+
END="$2"
|
|
18
|
+
VIDEO="$3"
|
|
19
|
+
|
|
20
|
+
if [ ! -f "$VIDEO" ]; then
|
|
21
|
+
echo "Error: file not found: $VIDEO"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
BASENAME=$(basename "$VIDEO")
|
|
26
|
+
DIRNAME=$(dirname "$VIDEO")
|
|
27
|
+
EXT="${BASENAME##*.}"
|
|
28
|
+
NAME="${BASENAME%.*}"
|
|
29
|
+
|
|
30
|
+
outfile="$DIRNAME/${NAME}.trim.$EXT"
|
|
31
|
+
|
|
32
|
+
ffmpeg -v error -y -i "$VIDEO" \
|
|
33
|
+
-ss "$START" \
|
|
34
|
+
-to "$END" \
|
|
35
|
+
-c copy "$outfile"
|
package/tests/avif.test.js
CHANGED
|
@@ -3,20 +3,13 @@ import { test } from 'node:test'
|
|
|
3
3
|
import { equal } from 'node:assert/strict'
|
|
4
4
|
import { tmpdir } from 'node:os'
|
|
5
5
|
import { execSync } from 'node:child_process'
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { mkdtempSync } from 'node:fs'
|
|
7
|
+
|
|
8
|
+
import { sha1 } from './utils.js'
|
|
8
9
|
|
|
9
|
-
function sha1(filePath) {
|
|
10
|
-
return createHash('sha1').update(readFileSync(filePath)).digest('hex')
|
|
11
|
-
}
|
|
12
10
|
|
|
13
11
|
test('PNG to AVIF', () => {
|
|
14
12
|
const tmp = mkdtempSync(join(tmpdir(), 'avif-test-'))
|
|
15
|
-
|
|
16
|
-
execSync(`src/cli.js avif --output-dir ${tmp} tests/fixtures/lenna.png`, {
|
|
17
|
-
cwd: process.cwd(),
|
|
18
|
-
stdio: 'inherit'
|
|
19
|
-
})
|
|
20
|
-
|
|
13
|
+
execSync(`src/cli.js avif --output-dir ${tmp} tests/fixtures/lenna.png` )
|
|
21
14
|
equal(sha1(join(tmp, 'lenna.avif')), sha1('tests/fixtures/lenna.avif'))
|
|
22
15
|
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tests/utils.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { equal } from 'node:assert/strict'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { execSync } from 'node:child_process'
|
|
6
|
+
import { mkdtempSync, cpSync } from 'node:fs'
|
|
7
|
+
import { sha1 } from './utils.js'
|
|
8
|
+
|
|
9
|
+
test('vsplit splits video at multiple time points', () => {
|
|
10
|
+
const tmp = mkdtempSync(join(tmpdir(), 'vsplit-test-'))
|
|
11
|
+
|
|
12
|
+
const inputFile = join(tmp, '60fps.mp4')
|
|
13
|
+
cpSync('tests/fixtures/60fps.mp4', inputFile)
|
|
14
|
+
|
|
15
|
+
execSync(`src/cli.js vsplit 5 10 15 20 25 ${inputFile}`)
|
|
16
|
+
|
|
17
|
+
for (let i = 1; i <= 6; i++) {
|
|
18
|
+
const generatedFile = join(tmp, `60fps_${i}.mp4`)
|
|
19
|
+
const expectedFile = `tests/fixtures/60fps_${i}.mp4`
|
|
20
|
+
equal(sha1(generatedFile), sha1(expectedFile), `60fps_${i}.mp4 hash should match expected`)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { equal } from 'node:assert/strict'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { execSync } from 'node:child_process'
|
|
6
|
+
import { mkdtempSync, cpSync } from 'node:fs'
|
|
7
|
+
import { sha1 } from './utils.js'
|
|
8
|
+
|
|
9
|
+
test('vtrim trims video from start to end time', () => {
|
|
10
|
+
const tmp = mkdtempSync(join(tmpdir(), 'vtrim-test-'))
|
|
11
|
+
|
|
12
|
+
const inputFile = join(tmp, '60fps.mp4')
|
|
13
|
+
cpSync('tests/fixtures/60fps.mp4', inputFile)
|
|
14
|
+
|
|
15
|
+
execSync(`src/cli.js vtrim 5 10 ${inputFile}`)
|
|
16
|
+
|
|
17
|
+
const out = join(tmp, '60fps.trim.mp4')
|
|
18
|
+
const expected = 'tests/fixtures/60fps_2.mp4'
|
|
19
|
+
equal(sha1(out), sha1(expected), 'Trimmed video (5-10s) should match 60fps_2.mp4')
|
|
20
|
+
})
|
package/src/vcut.sh
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/bin/zsh
|
|
2
|
-
|
|
3
|
-
if [ "$#" -ne 2 ]; then
|
|
4
|
-
echo "Usage: $0 <video-file> <split-time>"
|
|
5
|
-
echo "Example:"
|
|
6
|
-
echo " $0 input.mp4 123.45"
|
|
7
|
-
echo " $0 input.mkv 00:02:03.500"
|
|
8
|
-
exit 1
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
INPUT="$1"
|
|
12
|
-
SPLIT="$2"
|
|
13
|
-
|
|
14
|
-
if [ ! -f "$INPUT" ]; then
|
|
15
|
-
echo "Error: file not found: $INPUT"
|
|
16
|
-
exit 1
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
BASENAME=$(basename "$INPUT")
|
|
20
|
-
DIRNAME=$(dirname "$INPUT")
|
|
21
|
-
EXT="${BASENAME##*.}"
|
|
22
|
-
NAME="${BASENAME%.*}"
|
|
23
|
-
|
|
24
|
-
OUT1="$DIRNAME/${NAME}_a.$EXT"
|
|
25
|
-
OUT2="$DIRNAME/${NAME}_b.$EXT"
|
|
26
|
-
|
|
27
|
-
ffmpeg -v error -y -i "$INPUT" -t "$SPLIT" -c copy "$OUT1"
|
|
28
|
-
ffmpeg -v error -y -ss "$SPLIT" -i "$INPUT" -c copy "$OUT2"
|