Rewritten function to generate video thumbnails with ffmpeg.
This should be much faster than ever.
This should also solve an issue where potrait videos could have their
thumbnails be taller than 200px, since it was only forcing width to be
no larger than 200px.

Updated dashboard's styling to make sure potrait thumbnails (only matter
for video thumbnails) not going out of its container.

Updated thumbs.js (yarn thumbs) script to display elapsed time for each
operation in seconds.

Bumped v1 version string (for dashboard.css).
This commit is contained in:
Bobby Wibowo 2019-01-30 02:50:45 +07:00
parent e38037e660
commit 8140ff4d71
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
4 changed files with 74 additions and 63 deletions

View File

@ -149,74 +149,77 @@ utilsController.generateThumbs = (name, force) => {
// Only make thumbnail if it does not exist (ENOENT)
if (!error && !stats.isSymbolicLink() && !force) return resolve(true)
// If image extension
if (utilsController.imageExtensions.includes(extname)) {
const resizeOptions = {
width: 200,
height: 200,
fit: 'contain',
background: {
r: 0,
g: 0,
b: 0,
alpha: 0
}
}
const image = sharp(path.join(__dirname, '..', config.uploads.folder, name))
return image
.metadata()
.then(metadata => {
if (metadata.width > resizeOptions.width || metadata.height > resizeOptions.height) {
return image
.resize(resizeOptions)
.toFile(thumbname)
} else if (metadata.width === resizeOptions.width && metadata.height === resizeOptions.height) {
return image
.toFile(thumbname)
} else {
const x = resizeOptions.width - metadata.width
const y = resizeOptions.height - metadata.height
return image
.extend({
top: Math.floor(y / 2),
bottom: Math.ceil(y / 2),
left: Math.floor(x / 2),
right: Math.ceil(x / 2),
background: resizeOptions.background
})
.toFile(thumbname)
}
})
.then(() => {
resolve(true)
})
.catch(error => {
console.error(`${name}: ${error.toString()}`)
fs.symlink(thumbUnavailable, thumbname, error => {
if (error) console.error(error)
resolve(!error)
})
})
}
// Full path to input file
const input = path.join(__dirname, '..', config.uploads.folder, name)
// Otherwise video extension
ffmpeg(path.join(__dirname, '..', config.uploads.folder, name))
.thumbnail({
timestamps: ['20%'],
filename: '%b.png',
folder: path.join(__dirname, '..', config.uploads.folder, 'thumbs'),
size: '200x?'
new Promise((resolve, reject) => {
// If image extension
if (utilsController.imageExtensions.includes(extname)) {
const resizeOptions = {
width: 200,
height: 200,
fit: 'contain',
background: {
r: 0,
g: 0,
b: 0,
alpha: 0
}
}
const image = sharp(input)
return image
.metadata()
.then(metadata => {
if (metadata.width > resizeOptions.width || metadata.height > resizeOptions.height) {
return image
.resize(resizeOptions)
.toFile(thumbname)
} else if (metadata.width === resizeOptions.width && metadata.height === resizeOptions.height) {
return image
.toFile(thumbname)
} else {
const x = resizeOptions.width - metadata.width
const y = resizeOptions.height - metadata.height
return image
.extend({
top: Math.floor(y / 2),
bottom: Math.ceil(y / 2),
left: Math.floor(x / 2),
right: Math.ceil(x / 2),
background: resizeOptions.background
})
.toFile(thumbname)
}
})
.then(() => resolve(true))
.catch(reject)
}
// Otherwise video extension
ffmpeg.ffprobe(input, (error, metadata) => {
if (error) return reject(error)
ffmpeg(input)
.inputOptions([
`-ss ${parseInt(metadata.format.duration) * 20 / 100}`
])
.output(thumbname)
.outputOptions([
'-vframes 1',
'-vf scale=200:200:force_original_aspect_ratio=decrease'
])
.on('error', reject)
.on('end', () => resolve(true))
.run()
})
.on('error', error => {
console.log(`${name}: ${error.message}`)
})
.then(resolve)
.catch(error => {
console.error(`${name}: ${error.toString()}`)
fs.symlink(thumbUnavailable, thumbname, error => {
if (error) console.error(error)
resolve(!error)
})
})
.on('end', () => {
resolve(true)
})
})
})
}

View File

@ -117,6 +117,13 @@ ul#albumsContainer li {
justify-content: center;
}
.image-container .image img {
max-height: 100%;
max-width: 100%;
height: auto;
width: auto;
}
.image-container .checkbox {
position: absolute;
top: .75rem;

View File

@ -86,8 +86,9 @@ thumbs.do = async () => {
if (thumbs.verbose) console.log(`${_upload}: extension skipped.`)
skipped++
} else {
const start = Date.now()
const generated = await utils.generateThumbs(_upload, thumbs.force)
console.log(`${_upload}: ${generated ? 'OK' : 'ERROR'}`)
console.log(`${_upload}: ${(Date.now() - start) / 1000}s: ${generated ? 'OK' : 'ERROR'}`)
generated ? succeeded.push(_upload) : error++
}
return generate(i + 1)

View File

@ -16,7 +16,7 @@
v3: CSS and JS files (libs such as bulma, lazyload, etc).
v4: Renders in /public/render/* directories (to be used by render.js).
#}
{% set v1 = "whRjt0jclB" %}
{% set v1 = "BjoRSWgoqY" %}
{% set v2 = "hiboQUzAzp" %}
{% set v3 = "hiboQUzAzp" %}
{% set v4 = "dLp5zKKIkN" %}