1 | 'use strict'
|
2 |
|
3 | const { spawn } = require('child_process')
|
4 |
|
5 | function parseSize (sizeStr) {
|
6 | const percentRegex = /(\d+)%/g
|
7 | const sizeRegex = /(\d+|\?)x(\d+|\?)/g
|
8 | let payload
|
9 |
|
10 | const percentResult = percentRegex.exec(sizeStr)
|
11 | const sizeResult = sizeRegex.exec(sizeStr)
|
12 |
|
13 | if (percentResult) {
|
14 | payload = { percentage: Number.parseInt(percentResult[1]) }
|
15 | } else if (sizeResult) {
|
16 | const sizeValues = sizeResult.map(x => x === '?' ? null : Number.parseInt(x))
|
17 |
|
18 | payload = {
|
19 | width: sizeValues[1],
|
20 | height: sizeValues[2]
|
21 | }
|
22 | } else {
|
23 | throw new Error('Invalid size argument')
|
24 | }
|
25 |
|
26 | return payload
|
27 | }
|
28 |
|
29 | function buildArgs (input, output, { width, height, percentage }) {
|
30 | const scaleArg = (percentage)
|
31 | ? `-vf scale=iw*${percentage / 100}:ih*${percentage / 100}`
|
32 | : `-vf scale=${width || -1}:${height || -1}`
|
33 |
|
34 | return [
|
35 | '-y',
|
36 | `-i ${input}`,
|
37 | '-vframes 1',
|
38 | scaleArg,
|
39 | output
|
40 | ]
|
41 | }
|
42 |
|
43 | function ffmpegExecute (path, args, stream = null) {
|
44 | const ffmpeg = spawn(path, args, { shell: true })
|
45 | let stderr = ''
|
46 |
|
47 | return new Promise((resolve, reject) => {
|
48 | if (stream) {
|
49 | stream.pipe(ffmpeg.stdin)
|
50 | }
|
51 |
|
52 | ffmpeg.stderr.on('data', (data) => {
|
53 | stderr += data.toString()
|
54 | })
|
55 |
|
56 | ffmpeg.stderr.on('error', (err) => {
|
57 | reject(err)
|
58 | })
|
59 |
|
60 | ffmpeg.on('close', resolve)
|
61 | ffmpeg.on('exit', (code, signal) => {
|
62 | if (code !== 0) {
|
63 | const err = new Error(`ffmpeg exited ${code}\nffmpeg stderr:\n\n${stderr}`)
|
64 |
|
65 | reject(err)
|
66 | }
|
67 | })
|
68 | })
|
69 | }
|
70 |
|
71 | function genThumbnail (input, output, size, config = {}) {
|
72 | const ffmpegPath = config.path || 'ffmpeg'
|
73 |
|
74 | const parsedSize = parseSize(size)
|
75 | const args = buildArgs(
|
76 | typeof input === 'string' ? input : 'pipe:0',
|
77 | output,
|
78 | parsedSize
|
79 | )
|
80 |
|
81 | return typeof input === 'string'
|
82 | ? ffmpegExecute(ffmpegPath, args)
|
83 | : ffmpegExecute(ffmpegPath, args, input)
|
84 | }
|
85 |
|
86 | module.exports = genThumbnail
|