mediasnacks 0.26.0 → 0.28.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/index.js CHANGED
@@ -1,11 +1,14 @@
1
1
  export { avif } from './src/avif.js'
2
2
  export { countframes } from './src/countframes.js'
3
3
  export { detectdups } from './src/detectdups.js'
4
+ export { dlaudio } from './src/dlaudio.js'
5
+ export { dlvideo } from './src/dlvideo.js'
4
6
  export { dropdups } from './src/dropdups.js'
5
7
  export { edgespic } from './src/edgespic.js'
6
8
  export { frameseq } from './src/frameseq.js'
7
9
  export { hev1tohvc1 } from './src/hev1tohvc1.js'
8
10
  export { moov2front } from './src/moov2front.js'
11
+ export { png } from './src/png.js'
9
12
  export { play } from './src/play.js'
10
13
  export { prores } from './src/prores.js'
11
14
  export { qdir } from './src/qdir.js'
@@ -14,5 +17,6 @@ export { resize } from './src/resize.js'
14
17
  export { seqcheck } from './src/seqcheck.js'
15
18
  export { sqcrop } from './src/sqcrop.js'
16
19
  export { ssim } from './src/ssim.js'
20
+ export { unemoji } from './src/unemoji.js'
17
21
  export { vsplit } from './src/vsplit.js'
18
22
  export { vtrim } from './src/vtrim.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasnacks",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Utilities for optimizing and preparing videos and images",
5
5
  "license": "MIT",
6
6
  "author": "Eric Fortis",
package/src/avif.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { join, basename, dirname } from 'node:path'
2
2
  import { parseOptions } from './utils/parseOptions.js'
3
3
  import { replaceExt, lstat } from './utils/fs-utils.js'
4
- import { ffmpeg, assertUserHasFFmpeg } from './utils/subprocess.js'
4
+ import { ffmpeg } from './utils/subprocess.js'
5
5
 
6
6
 
7
7
  const HELP = `
@@ -14,45 +14,33 @@ DESCRIPTION
14
14
  EXAMPLES
15
15
  mediasnacks avif -y '*.png'
16
16
  mediasnacks avif --outdir=foo/ 'a/**/*.png'
17
- `.trim()
18
-
17
+ `
19
18
 
20
19
  export default async function main() {
21
- await assertUserHasFFmpeg()
22
-
23
- const { values, files } = await parseOptions({
20
+ const { values, files } = await parseOptions(HELP, {
24
21
  outdir: { type: 'string', default: '' },
25
22
  overwrite: { short: 'y', type: 'boolean' },
26
- help: { short: 'h', type: 'boolean' },
27
23
  })
28
24
 
29
- if (values.help) {
30
- console.log(HELP)
31
- return
32
- }
33
-
34
25
  if (!files.length)
35
- throw new Error('No images specified. See mediasnacks avif --help')
36
-
37
- console.log('AVIF…')
38
- for (const file of files)
39
- try {
40
- await avif({
41
- file,
42
- outFile: join(values.outdir || dirname(file), replaceExt(basename(file), 'avif')),
43
- overwrite: values.overwrite
44
- })
45
- console.log(file)
46
- }
47
- catch (err) {
48
- console.error(err?.message || err)
49
- }
26
+ throw 'Invalid input image'
27
+
28
+ for (const file of files) {
29
+ await avif({
30
+ file,
31
+ outFile: join(values.outdir || dirname(file), replaceExt(basename(file), 'avif')),
32
+ overwrite: values.overwrite
33
+ })
34
+ console.log(file)
35
+ }
50
36
  }
51
37
 
52
38
  export async function avif({ file, outFile, overwrite = false }) {
53
39
  const stAvif = lstat(outFile)
54
- if (!overwrite && stAvif?.isFile()) throw new Error(`output file exists but --overwrite=false. ${file}`)
55
- if (stAvif?.mtimeMs > lstat(file)?.mtimeMs) throw new Error(`avif is newer. ${file}`)
40
+ if (!overwrite) {
41
+ if (stAvif?.isFile()) throw `output file exists: ${file}`
42
+ if (stAvif?.mtimeMs > lstat(file)?.mtimeMs) throw `avif is newer: ${file}`
43
+ }
56
44
 
57
45
  await ffmpeg([
58
46
  '-y',
package/src/cli.js CHANGED
@@ -8,7 +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
+ png: ['./png.js', 'Optimizes PNG images with oxipng'],
12
12
  sqcrop: ['./sqcrop.js', 'Square crops images\n'],
13
13
 
14
14
  resize: ['./resize.js', 'Resizes videos or images'],
@@ -16,7 +16,7 @@ const COMMANDS = {
16
16
  frameseq: ['./frameseq.js', 'Converts video to sequence of PNGs'],
17
17
  countframes: ['./countframes.js', 'Counts frames in a video'],
18
18
  ssim: ['./ssim.js', 'Computes SSIM between two images'],
19
- gif: ['./gif.sh', 'Video to GIF\n'],
19
+ gif: ['./gif.js', 'Video to GIF\n'],
20
20
 
21
21
  detectdups: ['./detectdups.js', 'Detects duplicate frames in a video'],
22
22
  dropdups: ['./dropdups.js', 'Removes duplicate frames in a video'],
@@ -35,10 +35,10 @@ const COMMANDS = {
35
35
  openrand: ['./openrand.js', 'Opens a random file (macOS only)'],
36
36
  play: ['./play.js', 'Plays filtered playlist with mpv\n'],
37
37
 
38
- dlaudio: ['./dlaudio.sh', 'yt-dlp best audio'],
39
- dlvideo: ['./dlvideo.sh', 'yt-dlp best video\n'],
38
+ dlaudio: ['./dlaudio.js', 'yt-dlp best audio'],
39
+ dlvideo: ['./dlvideo.js', 'yt-dlp best video\n'],
40
40
 
41
- unemoji: ['./unemoji.sh', 'Removes emojis from filenames'],
41
+ unemoji: ['./unemoji.js', 'Removes emojis from filenames'],
42
42
  rmcover: ['./rmcover.sh', 'Removes cover art'],
43
43
  }
44
44
 
@@ -60,27 +60,24 @@ ${commandsSummary().map(([cmd, desc]) =>
60
60
  async function main() {
61
61
  const [, , opt, ...args] = process.argv
62
62
 
63
- if (opt === '-v' || opt === '--version') {
64
- console.log(pkgJSON.version)
65
- return
66
- }
67
- if (opt === '-h' || opt === '--help') {
68
- console.log(HELP)
69
- return
70
- }
63
+ switch (opt) {
64
+ case '-v':
65
+ case '--version':
66
+ console.log(pkgJSON.version)
67
+ return
71
68
 
72
- if (!opt) {
73
- console.log(HELP)
74
- process.exit(1)
75
- }
76
- if (!Object.hasOwn(COMMANDS, opt)) {
77
- console.error(`'${opt}' is not a command. See mediasnacks --help\n`)
78
- process.exit(1)
69
+ case '-h':
70
+ case '--help':
71
+ console.log(HELP)
72
+ return
79
73
  }
80
74
 
75
+ if (!opt) throw HELP
76
+ if (!Object.hasOwn(COMMANDS, opt)) throw `'${opt}' is not a command. See mediasnacks --help\n`
77
+
81
78
  const cmd = COMMANDS[opt][0]
82
79
  if (cmd.endsWith('.js'))
83
- (await import(cmd)).default()
80
+ await (await import(cmd)).default()
84
81
  else
85
82
  spawn(join(import.meta.dirname, cmd), args, { stdio: 'inherit' })
86
83
  .on('exit', process.exit)
@@ -1,5 +1,4 @@
1
1
  import { parseOptions } from './utils/parseOptions.js'
2
- import { assertUserHasFFmpeg } from './utils/subprocess.js'
3
2
  import { videoAttrs } from './utils/videoAttrs.js'
4
3
  import { parseTimecode } from './utils/parseTimecode.js'
5
4
 
@@ -19,27 +18,20 @@ OPTIONS
19
18
  EXAMPLES
20
19
  mediasnacks countframes --start=1:30.16 --end=60 video.mov
21
20
  mediasnacks countframes --fps=12 video.mov
22
- `.trim()
21
+ `
23
22
 
24
23
 
25
24
  export default async function main() {
26
- await assertUserHasFFmpeg()
27
-
28
- const { values, files } = await parseOptions({
29
- fps: { type: 'string', default: '' },
30
- start: { short: 's', type: 'string', default: '' },
31
- end: { short: 'e', type: 'string', default: '' },
32
- help: { short: 'h', type: 'boolean' }
25
+ const { values, files } = await parseOptions(HELP, {
26
+ fps: { type: 'string' },
27
+ start: { short: 's', type: 'string' },
28
+ end: { short: 'e', type: 'string' },
33
29
  })
34
30
 
35
- if (values.help) {
36
- console.log(HELP)
37
- return
38
- }
39
-
40
31
  const { fps, start, end } = values
41
32
  const video = files[0]
42
- if (!video) throw new Error('No video file specified')
33
+ if (!video)
34
+ throw 'No video file specified'
43
35
 
44
36
  const n = await countframes({ video, fps, start, end })
45
37
  console.log(String(n))
@@ -48,10 +40,9 @@ export default async function main() {
48
40
 
49
41
  export async function countframes({ video, fps, start, end }) {
50
42
  const v = await videoAttrs(video)
51
- const videoDuration = parseFloat(v.duration || 0)
43
+ const duration = parseFloat(v.duration || 0)
52
44
  const startSecs = start ? parseTimecode(start) : 0
53
- const endSecs = end ? parseTimecode(end) : videoDuration
54
- const durationLimit = Math.max(0, endSecs - startSecs)
45
+ const endSecs = end ? parseTimecode(end) : duration
55
46
  const actualFps = fps ? Number(fps) : eval(v.r_frame_rate)
56
- return Math.ceil(durationLimit * actualFps)
47
+ return Math.ceil(Math.max(0, endSecs - startSecs) * actualFps)
57
48
  }
package/src/detectdups.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { parseOptions } from './utils/parseOptions.js'
2
- import { ffmpeg, assertUserHasFFmpeg } from './utils/subprocess.js'
2
+ import { ffmpeg } from './utils/subprocess.js'
3
3
  import { videoAttrs } from './utils/videoAttrs.js'
4
4
 
5
5
  const STDEV_THRESHOLD = 0.2
@@ -22,32 +22,25 @@ OPTIONS
22
22
  -s, --seek <sec> Video start time for detection
23
23
  -d, --duration <sec> Analyze this many seconds of video
24
24
  -v, --verbose
25
- -h, --help
26
25
 
27
26
  SEE ALSO
28
27
  mediasnacks framediff
29
- `.trim()
28
+ `
30
29
 
31
30
 
32
31
  export default async function main() {
33
- await assertUserHasFFmpeg()
34
-
35
- const { values, files } = await parseOptions({
32
+ const { values, files } = await parseOptions(HELP, {
36
33
  seek: { short: 's', type: 'string', },
37
34
  duration: { short: 'd', type: 'string' },
38
- help: { short: 'h', type: 'boolean' }
39
35
  })
40
36
 
41
- if (values.help) {
42
- console.log(HELP)
43
- return
44
- }
45
-
46
- if (files.length !== 1) throw new Error('Invalid input file. One video file must be specified. See mediasnacks detectdups --help')
37
+ if (files.length !== 1)
38
+ throw 'Invalid input file. One video file must be specified.'
47
39
 
48
40
  const video = files[0]
49
41
  const v = await videoAttrs(video)
50
- if (v.codec_type !== 'video') throw new Error('Invalid input file. Must be a video.')
42
+ if (v.codec_type !== 'video')
43
+ throw 'Invalid input file. Must be a video.'
51
44
 
52
45
  const vDur = Number(v.duration)
53
46
 
@@ -59,9 +52,9 @@ export default async function main() {
59
52
  ? Number(values.duration)
60
53
  : vDur > 60 ? 20 : vDur
61
54
 
62
- if (isNaN(seek) || seek < 0) throw new Error(`Invalid --seek value: ${values.seek}`)
63
- if (isNaN(duration) || duration < 1) throw new Error(`Invalid --duration value: ${values.duration}`)
64
- if ((seek + duration) > vDur) throw new Error(`Invalid analysis range. Exceeds video duration: ${vDur}`)
55
+ if (isNaN(seek) || seek < 0) throw `Invalid --seek value: ${values.seek}`
56
+ if (isNaN(duration) || duration < 1) throw `Invalid --duration value: ${values.duration}`
57
+ if ((seek + duration) > vDur) throw `Invalid analysis range. Exceeds video duration: ${vDur}`
65
58
 
66
59
  const dups = await detectdups({ video: files[0], seek, duration })
67
60
  const h = deltaHistogram(dups)
package/src/dlaudio.js ADDED
@@ -0,0 +1,34 @@
1
+ import { parseOptions } from './utils/parseOptions.js'
2
+ import { runSilently } from './utils/subprocess.js'
3
+ import { unemoji } from './unemoji.js'
4
+
5
+
6
+ const HELP = `
7
+ SYNOPSIS
8
+ mediasnacks dlaudio <url>
9
+
10
+ DESCRIPTION
11
+ yt-dlp best m4a
12
+ `
13
+
14
+ export default async function main() {
15
+ const { values, positionals } = await parseOptions(HELP)
16
+
17
+ if (!positionals[0])
18
+ throw 'Missing URL'
19
+
20
+ const f = await dlaudio(positionals[0])
21
+ console.log(f)
22
+ }
23
+
24
+ export async function dlaudio(url) {
25
+ const f = (await runSilently('yt-dlp', [
26
+ '--print', 'filename',
27
+ '--no-simulate',
28
+ '-o', '%(title)s.%(ext)s',
29
+ '-f', 'bestaudio[ext=m4a]/bestaudio',
30
+ url
31
+ ])).stdout.trim()
32
+
33
+ return await unemoji(f) || f
34
+ }
package/src/dlvideo.js ADDED
@@ -0,0 +1,28 @@
1
+ import { parseOptions } from './utils/parseOptions.js'
2
+ import { run } from './utils/subprocess.js'
3
+
4
+
5
+ const HELP = `
6
+ SYNOPSIS
7
+ mediasnacks dlvideo <url>
8
+
9
+ DESCRIPTION
10
+ yt-dlp best mp4
11
+ `
12
+
13
+ export default async function main() {
14
+ const { values, positionals } = await parseOptions(HELP)
15
+
16
+ if (!positionals[0])
17
+ throw 'Missing URL'
18
+
19
+ await dlvideo(positionals[0])
20
+ }
21
+
22
+ export async function dlvideo(url) {
23
+ await run('yt-dlp', [
24
+ '-o', '%(title)s.%(ext)s',
25
+ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4',
26
+ url
27
+ ])
28
+ }
package/src/dropdups.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { resolve, parse, format } from 'node:path'
2
2
  import { parseOptions } from './utils/parseOptions.js'
3
- import { ffmpeg, assertUserHasFFmpeg, run } from './utils/subprocess.js'
3
+ import { ffmpeg, run } from './utils/subprocess.js'
4
4
  import { ProresProfiles } from './prores.js'
5
5
 
6
6
 
@@ -16,7 +16,6 @@ DESCRIPTION
16
16
  OPTIONS
17
17
  -n, --dup-frame-num <n> Known frame interval to drop.
18
18
  Default: n=0, which auto-detects repeated frames (slower)
19
- -h, --help
20
19
 
21
20
  EXAMPLES
22
21
  Use n=2 when every other frame is repeated:
@@ -24,30 +23,21 @@ EXAMPLES
24
23
 
25
24
  Use n=6 if e.g., a 25 fps got upped to 30 fps without interpolation.
26
25
  mediasnacks dropdups -n6 vid.mov
27
- `.trim()
26
+ `
28
27
 
29
28
 
30
29
  export default async function main() {
31
- await assertUserHasFFmpeg()
32
-
33
- const { values, files } = await parseOptions({
34
- 'dup-frame-num': { short: 'n', type: 'string', default: '' },
35
- help: { short: 'h', type: 'boolean' },
30
+ const { values, files } = await parseOptions(HELP, {
31
+ 'dup-frame-num': { short: 'n', type: 'string' },
36
32
  })
37
33
 
38
- if (values.help) {
39
- console.log(HELP)
40
- return
41
- }
42
-
43
34
  if (!files.length)
44
- throw new Error('No video specified. See mediasnacks dropdups --help')
35
+ throw 'No video specified.'
45
36
 
46
37
  let dupFrameNum = values['dup-frame-num']
47
38
  if (dupFrameNum && !Number.isInteger(+dupFrameNum))
48
- throw new Error('Invalid -n. It must be a positive integer.')
39
+ throw 'Invalid -n. It must be a positive integer.'
49
40
 
50
- console.log('Dropping Duplicate Frames…')
51
41
  for (const file of files)
52
42
  await dropdups(resolve(file), dupFrameNum)
53
43
  }
package/src/edgespic.js CHANGED
@@ -3,46 +3,38 @@ import { basename, extname, join, parse } from 'node:path'
3
3
  import { mkDir } from './utils/fs-utils.js'
4
4
  import { videoAttrs } from './utils/videoAttrs.js'
5
5
  import { parseOptions } from './utils/parseOptions.js'
6
- import { ffmpeg, assertUserHasFFmpeg } from './utils/subprocess.js'
6
+ import { ffmpeg } from './utils/subprocess.js'
7
7
 
8
8
 
9
+ const WIDTH = 640
10
+
9
11
  const HELP = `
10
12
  SYNOPSIS
11
- mediasnacks edgespic [--width=<num>] <files>
13
+ mediasnacks edgespic [-w | --width=<num>] <files>
12
14
 
13
15
  DESCRIPTION
14
16
  Extracts the first and last frames from each video and saves them to the 'edgepics/' subfolder.
15
17
 
16
18
  OPTIONS
17
- -w, --width Default:640 The aspect ratio is preserved.
19
+ -w, --width Default: ${WIDTH} The aspect ratio is preserved.
18
20
 
19
21
  EXAMPLES
20
22
  mediasnacks edgespic -w 800 *.mov
21
23
  mediasnacks edgespic -w 600 'videos/**/*.mp4'
22
- `.trim()
23
-
24
+ `
24
25
 
25
26
  export default async function main() {
26
- await assertUserHasFFmpeg()
27
-
28
- const { values, files } = await parseOptions({
29
- 'width': { short: 'w', type: 'string', default: '640' },
30
- help: { short: 'h', type: 'boolean' },
27
+ const { values, files } = await parseOptions(HELP, {
28
+ width: { short: 'w', type: 'string', default: String(WIDTH) }
31
29
  })
32
30
 
33
- if (values.help) {
34
- console.log(HELP)
35
- return
36
- }
37
-
38
- const width = Number(values['width'])
39
- if (width <= 0 || !Number.isInteger(width)) throw new Error('--width must be a positive number')
40
- if (!files.length) throw new Error('No video files specified')
31
+ const width = Number(values.width)
32
+ if (width <= 0 || !Number.isInteger(width)) throw '--width must be a positive number'
33
+ if (!files.length) throw 'No video files specified'
41
34
 
42
35
  const outDir = join(parse(files[0]).dir, 'edgespic')
43
36
  await mkDir(outDir)
44
37
 
45
- console.log('Extracting edge frames…')
46
38
  for (const file of files)
47
39
  await edgespic(file, width, outDir)
48
40
  }
package/src/frameseq.js CHANGED
@@ -2,7 +2,7 @@ import { basename, extname, join, parse } from 'node:path'
2
2
 
3
3
  import { mkDir } from './utils/fs-utils.js'
4
4
  import { parseOptions } from './utils/parseOptions.js'
5
- import { ffmpeg, assertUserHasFFmpeg } from './utils/subprocess.js'
5
+ import { ffmpeg } from './utils/subprocess.js'
6
6
  import { countframes } from './countframes.js'
7
7
 
8
8
 
@@ -24,31 +24,23 @@ EXAMPLES
24
24
 
25
25
  Custom framerate, all video duration
26
26
  mediasnacks frameseq --fps=12 video.mov
27
- `.trim()
27
+ `
28
28
 
29
29
 
30
30
  export default async function main() {
31
- await assertUserHasFFmpeg()
32
-
33
- const { values, files } = await parseOptions({
34
- fps: { short: 'f', type: 'string', default: '' },
35
- start: { short: 's', type: 'string', default: '' },
36
- end: { short: 'e', type: 'string', default: '' },
31
+ const { values, files } = await parseOptions(HELP, {
32
+ fps: { short: 'f', type: 'string' },
33
+ start: { short: 's', type: 'string' },
34
+ end: { short: 'e', type: 'string' },
37
35
  outdir: { type: 'string', default: '' },
38
- help: { short: 'h', type: 'boolean' }
39
36
  })
40
37
 
41
- if (values.help) {
42
- console.log(HELP)
43
- return
44
- }
45
-
46
38
  const { fps, start, end, outdir } = values
47
39
  const video = files[0]
48
- if (!video) throw new Error('No video files specified')
49
- if (fps && isNaN(parseFloat(fps))) throw new Error('Invalid --fps')
50
- if (start && isNaN(parseFloat(start))) throw new Error('Invalid --start')
51
- if (end && isNaN(parseFloat(end))) throw new Error('Invalid --end')
40
+ if (!video) throw 'No video files specified'
41
+ if (fps && isNaN(parseFloat(fps))) throw 'Invalid --fps'
42
+ if (start && isNaN(parseFloat(start))) throw 'Invalid --start'
43
+ if (end && isNaN(parseFloat(end))) throw 'Invalid --end'
52
44
 
53
45
  const nFrames = await countframes({ video, fps, start, end })
54
46
  const pad = String(nFrames).length
@@ -57,13 +49,13 @@ export default async function main() {
57
49
 
58
50
  export async function frameseq({ video, fps, start, end, pad, outdir }) {
59
51
  const name = basename(video, extname(video))
60
- const outDir = outdir || join(parse(video).dir, name)
61
- await mkDir(outDir)
52
+ const dir = outdir || join(parse(video).dir, name)
53
+ await mkDir(dir)
62
54
  await ffmpeg([
63
55
  start ? ['-ss', start] : [],
64
56
  end ? ['-to', end] : [],
65
57
  '-i', video,
66
58
  fps ? ['-vf', `fps=${fps}`] : [],
67
- join(outDir, `${name}_%0${pad}d.png`)
59
+ join(dir, `${name}_%0${pad}d.png`)
68
60
  ].flat())
69
61
  }
package/src/gif.js ADDED
@@ -0,0 +1,43 @@
1
+ import { parse, format } from 'node:path'
2
+ import { parseOptions } from './utils/parseOptions.js'
3
+ import { run } from './utils/subprocess.js'
4
+
5
+
6
+ const FPS = 12
7
+ const WIDTH = 600
8
+
9
+ const HELP = `
10
+ SYNOPSIS
11
+ mediasnacks gif [-f | --fps <number>] [-w | --width <pixels>] <file>
12
+
13
+ DESCRIPTION
14
+ Converts video to GIF
15
+
16
+ OPTIONS
17
+ -f, --fps Default: ${FPS}
18
+ -w, --width Default: ${WIDTH}
19
+ `
20
+
21
+ export default async function main() {
22
+ const { values, files } = await parseOptions(HELP, {
23
+ fps: { short: 'f', type: 'string', default: String(FPS) },
24
+ width: { short: 'w', type: 'string', default: String(WIDTH) },
25
+ })
26
+
27
+ if (!files.length)
28
+ throw 'Missing input file'
29
+
30
+ await gif(files[0], values.fps, values.width)
31
+ }
32
+
33
+ export async function gif(file, fps, width) {
34
+ const { dir, name } = parse(file)
35
+ const outName = format({ dir, name, ext: '.gif' })
36
+
37
+ await run('ffmpeg', [
38
+ '-v', 'error',
39
+ '-i', file,
40
+ '-vf', `fps=${fps},scale=${width}:-1`,
41
+ outName,
42
+ ])
43
+ }
package/src/hev1tohvc1.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { parseOptions } from './utils/parseOptions.js'
2
2
  import { uniqueFilenameFor, overwrite } from './utils/fs-utils.js'
3
- import { ffmpeg, assertUserHasFFmpeg } from './utils/subprocess.js'
3
+ import { ffmpeg } from './utils/subprocess.js'
4
4
  import { videoAttrs } from './utils/videoAttrs.js'
5
5
 
6
6
 
@@ -12,36 +12,25 @@ DESCRIPTION
12
12
  This program fixes video thumbnails not rendering in macOS
13
13
  Finder, and fixes video not importable in Final Cut Pro. That’s done
14
14
  by changing the container’s sample entry code from HEV1 to HVC1.
15
- `.trim()
15
+ `
16
16
 
17
17
 
18
18
  export default async function main() {
19
- await assertUserHasFFmpeg()
19
+ const { values, files } = await parseOptions(HELP)
20
20
 
21
- const { values, files } = await parseOptions({
22
- help: { short: 'h', type: 'boolean' }
23
- })
21
+ if (!files.length)
22
+ throw 'Missing input file(s)'
24
23
 
25
- if (values.help) {
26
- console.log(HELP)
27
- return
24
+ for (const file of files) {
25
+ await hev1tohvc1(file)
26
+ console.log(file)
28
27
  }
29
-
30
- if (!files.length) throw new Error('Missing input file(s)')
31
-
32
- for (const file of files)
33
- try {
34
- await hev1tohvc1(file)
35
- console.log(file)
36
- }
37
- catch (err) {
38
- console.error(err?.message || err)
39
- }
40
28
  }
41
29
 
42
30
  export async function hev1tohvc1(file) {
43
31
  const v = await videoAttrs(file)
44
- if (v.codec_tag_string !== 'hev1') throw new Error(`non hev1 ${file}`)
32
+ if (v.codec_tag_string !== 'hev1')
33
+ throw `non hev1 ${file}`
45
34
 
46
35
  const tmp = uniqueFilenameFor(file)
47
36
  await ffmpeg([