More fixes to thumbnailers

Use fluent-ffmpeg's .screenshots() function instead,
with some countermeasures for weird situations.

No more selective error suppressions.
This commit is contained in:
Bobby Wibowo 2020-06-15 21:04:30 +07:00
parent 4225819b98
commit 0b8b1ed026
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF

View File

@ -254,59 +254,49 @@ self.generateThumbs = async (name, extname, force) => {
} }
} else if (self.videoExts.includes(extname)) { } else if (self.videoExts.includes(extname)) {
const metadata = await self.ffprobe(input) const metadata = await self.ffprobe(input)
const duration = parseInt(metadata.format.duration)
// Skip files that have neither video streams/channels nor valid duration metadata const duration = parseInt(metadata.format.duration)
if (!metadata.streams || !metadata.streams.some(s => s.codec_type === 'video') || isNaN(duration)) if (isNaN(duration))
throw 'File does not have valid required data' throw 'Warning: File does not have valid duration metadata'
const videoStream = metadata.streams && metadata.streams.find(s => s.codec_type === 'video')
if (!videoStream || !videoStream.width || !videoStream.height)
throw 'Warning: File does not have valid video stream metadata'
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
ffmpeg(input) ffmpeg(input)
.seekInput(duration * 20 / 100) .on('error', error => reject(error))
.frames(1) .on('end', () => resolve())
.videoFilters([ .screenshots({
{ folder: paths.thumbs,
filter: 'select', filename: name.slice(0, -extname.length) + '.png',
options: 'eq(pict_type\\,I)' timestamps: ['20%'],
}, size: videoStream.width >= videoStream.height
{ ? `${self.thumbsSize}x?`
filter: 'scale', : `?x${self.thumbsSize}`
options: `${self.thumbsSize}:${self.thumbsSize}:force_original_aspect_ratio=decrease`
}
])
.output(thumbname)
.on('error', async error => {
// Try to unlink thumbnail,
// since ffmpeg may have created an incomplete thumbnail
try {
await paths.unlink(thumbname)
} catch (err) {
if (err && err.code !== 'ENOENT')
logger.error(`[${name}]: ${err.toString()}`)
}
return reject(error)
}) })
.on('end', () => resolve(true)) })
.run() .catch(error => error) // Error passthrough
.then(async error => {
// FFMPEG would just warn instead of exiting with errors when dealing with incomplete files
// Sometimes FFMPEG would throw errors but actually somehow succeeded in making the thumbnails
// (this could be a fallback mechanism of fluent-ffmpeg library instead)
// So instead we check if the thumbnail exists to really make sure
try {
await paths.lstat(thumbname)
return true
} catch (err) {
if (err.code === 'ENOENT')
throw error || 'Warning: FFMPEG exited with empty output file'
else
throw error || err
}
}) })
} else { } else {
return false return false
} }
} catch (error) { } catch (error) {
// TODO: Parse ffmpeg/ffprobe errors into concise error messages (get rid of versions info) logger.error(`[${name}]: ${error.toString().trim()}`)
// Suppress error logging for errors matching these patterns
const errorString = error.toString()
const suppress = [
/Input file contains unsupported image format/,
/Invalid data found when processing input/,
/File does not have valid required data/,
/Could not find codec parameters/,
/Duplicate element/
]
if (!suppress.some(t => t.test(errorString)))
logger.error(`[${name}]: ${errorString}`)
try { try {
await paths.symlink(paths.thumbPlaceholder, thumbname) await paths.symlink(paths.thumbPlaceholder, thumbname)
return true return true