mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2024-12-12 15:36:22 +00:00
feat: support generating animated thumbnails
GIF files only for now existing installations will have to use "yarn thumbs" script to re-generate thumbnails doing "yarn thumbs 4 1" should be enough to only re-generate thumbnails of GIF files
This commit is contained in:
parent
25ca7a6bb7
commit
b6b645df93
@ -602,6 +602,9 @@ module.exports = {
|
||||
generateThumbs: {
|
||||
image: true,
|
||||
video: true,
|
||||
// Generate animated thumbnails wherever applicable.
|
||||
// For now will only work with GIF images, also depends on "image" option being enabled.
|
||||
animated: true,
|
||||
// Placeholder defaults to 'public/images/unavailable.png'.
|
||||
placeholder: null,
|
||||
size: 200,
|
||||
|
@ -1809,7 +1809,11 @@ self.list = async (req, res) => {
|
||||
for (const file of result.files) {
|
||||
file.extname = utils.extname(file.name)
|
||||
if (utils.mayGenerateThumb(file.extname)) {
|
||||
file.thumb = `thumbs/${file.name.slice(0, -file.extname.length)}.png`
|
||||
let thumbext = '.png'
|
||||
if (file.extname === '.gif' && config.uploads.generateThumbs.animated) {
|
||||
thumbext = '.gif'
|
||||
}
|
||||
file.thumb = `thumbs/${file.name.slice(0, -file.extname.length)}${thumbext}`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,15 +399,30 @@ self.assertJSON = async (req, res) => {
|
||||
|
||||
self.generateThumbs = async (name, extname, force) => {
|
||||
extname = extname.toLowerCase()
|
||||
const thumbname = path.join(paths.thumbs, name.slice(0, -extname.length) + '.png')
|
||||
const thumbname = name.slice(0, -extname.length)
|
||||
let thumbext = '.png'
|
||||
if (extname === '.gif' && config.uploads.generateThumbs.animated) {
|
||||
thumbext = '.gif'
|
||||
}
|
||||
|
||||
const thumbfile = path.join(paths.thumbs, thumbname + thumbext)
|
||||
|
||||
try {
|
||||
// Check if old static thumbnail exists, then unlink it
|
||||
if (thumbext === '.gif') {
|
||||
const staticthumb = path.join(paths.thumbs, thumbname + '.png')
|
||||
const stat = await jetpack.inspectAsync(staticthumb)
|
||||
if (stat) {
|
||||
await jetpack.removeAsync(staticthumb)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if thumbnail already exists
|
||||
const stat = await jetpack.inspectAsync(thumbname)
|
||||
const stat = await jetpack.inspectAsync(thumbfile)
|
||||
if (stat) {
|
||||
if (stat.type === 'symlink') {
|
||||
// Unlink if symlink (should be symlink to the placeholder)
|
||||
await jetpack.removeAsync(thumbname)
|
||||
await jetpack.removeAsync(thumbfile)
|
||||
} else if (!force) {
|
||||
// Continue only if it does not exist, unless forced to
|
||||
return true
|
||||
@ -419,6 +434,11 @@ self.generateThumbs = async (name, extname, force) => {
|
||||
|
||||
// If image extension
|
||||
if (Constants.IMAGE_EXTS.includes(extname)) {
|
||||
const sharpOptions = {}
|
||||
if (thumbext === '.gif') {
|
||||
sharpOptions.animated = true
|
||||
}
|
||||
|
||||
const resizeOptions = {
|
||||
width: self.thumbsSize,
|
||||
height: self.thumbsSize,
|
||||
@ -430,15 +450,17 @@ self.generateThumbs = async (name, extname, force) => {
|
||||
alpha: 0
|
||||
}
|
||||
}
|
||||
const image = sharp(input)
|
||||
|
||||
const image = sharp(input, sharpOptions)
|
||||
|
||||
const metadata = await image.metadata()
|
||||
if (metadata.width > resizeOptions.width || metadata.height > resizeOptions.height) {
|
||||
await image
|
||||
.resize(resizeOptions)
|
||||
.toFile(thumbname)
|
||||
.toFile(thumbfile)
|
||||
} else if (metadata.width === resizeOptions.width && metadata.height === resizeOptions.height) {
|
||||
await image
|
||||
.toFile(thumbname)
|
||||
.toFile(thumbfile)
|
||||
} else {
|
||||
const x = resizeOptions.width - metadata.width
|
||||
const y = resizeOptions.height - metadata.height
|
||||
@ -450,7 +472,7 @@ self.generateThumbs = async (name, extname, force) => {
|
||||
right: Math.ceil(x / 2),
|
||||
background: resizeOptions.background
|
||||
})
|
||||
.toFile(thumbname)
|
||||
.toFile(thumbfile)
|
||||
}
|
||||
} else if (Constants.VIDEO_EXTS.includes(extname)) {
|
||||
const metadata = await self.ffprobe(input)
|
||||
@ -486,7 +508,7 @@ self.generateThumbs = async (name, extname, force) => {
|
||||
// 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
|
||||
if (await jetpack.existsAsync(thumbname)) {
|
||||
if (await jetpack.existsAsync(thumbfile)) {
|
||||
return true
|
||||
} else {
|
||||
throw error || new Error('FFMPEG exited with empty output file')
|
||||
@ -497,9 +519,9 @@ self.generateThumbs = async (name, extname, force) => {
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`[${name}]: generateThumbs(): ${error.toString().trim()}`)
|
||||
await jetpack.removeAsync(thumbname) // try to unlink incomplete thumbs first
|
||||
await jetpack.removeAsync(thumbfile) // try to unlink incomplete thumbs first
|
||||
try {
|
||||
await jetpack.symlinkAsync(paths.thumbPlaceholder, thumbname)
|
||||
await jetpack.symlinkAsync(paths.thumbPlaceholder, thumbfile)
|
||||
return true
|
||||
} catch (err) {
|
||||
logger.error(`[${name}]: generateThumbs(): ${err.toString().trim()}`)
|
||||
|
@ -8,7 +8,8 @@ const self = {
|
||||
mode: null,
|
||||
mayGenerateThumb: extname => {
|
||||
return ([1, 3].includes(self.mode) && Constants.IMAGE_EXTS.includes(extname)) ||
|
||||
([2, 3].includes(self.mode) && Constants.VIDEO_EXTS.includes(extname))
|
||||
([2, 3].includes(self.mode) && Constants.VIDEO_EXTS.includes(extname)) ||
|
||||
(self.mode === 4 && extname === '.gif')
|
||||
},
|
||||
getFiles: async directory => {
|
||||
const names = await jetpack.listAsync(directory)
|
||||
@ -34,7 +35,7 @@ const self = {
|
||||
const verbose = parseInt(args[2]) || 0
|
||||
const cfcache = parseInt(args[3]) || 0
|
||||
|
||||
if (![1, 2, 3].includes(self.mode) ||
|
||||
if (![1, 2, 3, 4].includes(self.mode) ||
|
||||
![0, 1].includes(force) ||
|
||||
![0, 1, 2].includes(verbose) ||
|
||||
![0, 1].includes(cfcache) ||
|
||||
@ -44,9 +45,9 @@ const self = {
|
||||
Generate thumbnails.
|
||||
|
||||
Usage:
|
||||
node ${location} <mode=1|2|3> [force=0|1] [verbose=0|1] [cfcache=0|1]
|
||||
node ${location} <mode=1|2|3|4> [force=0|1] [verbose=0|1] [cfcache=0|1]
|
||||
|
||||
mode : 1 = images only, 2 = videos only, 3 = both images and videos
|
||||
mode : 1 = images only, 2 = videos only, 3 = both images and videos, 4 = animated only
|
||||
force : 0 = no force (default), 1 = overwrite existing thumbnails
|
||||
verbose : 0 = only print missing thumbs (default), 1 = print all, 2 = print nothing
|
||||
cfcache : 0 = do not clear cloudflare cache (default), 1 = clear cloudflare cache
|
||||
|
Loading…
Reference in New Issue
Block a user