From 8623d1198d75f971ee8547271a7daeee24f7b92d Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 5 Jan 2025 00:38:50 +0200 Subject: [PATCH] Add thumbnail dimensions to config (#3262) * Add thumbnail dimensions to config * Fix default value for thumbnails.enabled * Update comment for thumbnail recreation instructions in config.yaml * Lint config values * Verify config size > 0 * More config lint --- default/config.yaml | 22 +++++++++++------ post-install.js | 47 +++++++++++++++++++++++++++++++++++-- src/endpoints/thumbnails.js | 19 ++++++++------- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/default/config.yaml b/default/config.yaml index a3de53021..b18d9a2a5 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -83,13 +83,21 @@ autorun: true # Avoids using 'localhost' for autorun in auto mode. # use if you don't have 'localhost' in your hosts file avoidLocalhost: false -# Disable thumbnail generation -disableThumbnails: false -# Thumbnail quality (0-100) -thumbnailsQuality: 95 -# Generate avatar thumbnails as PNG instead of JPG (preserves transparency but increases filesize by about 100%) -# Changing this only affects new thumbnails. To recreate the old ones, clear out your ST/thumbnails/ folder. -avatarThumbnailsPng: false + +# THUMBNAILING CONFIGURATION +thumbnails: + # Enable thumbnail generation + enabled: true + # Image format of avatar thumbnails: + # * "jpg": best compression with adjustable quality, no transparency + # * "png": preserves transparency but increases filesize by about 100% + # Changing this only affects new thumbnails. To recreate the old ones, clear out /thumbnails folder in your user data. + format: "jpg" + # JPG thumbnail quality (0-100) + quality: 95 + # Maximum thumbnail dimensions per type [width, height] + dimensions: { 'bg': [160, 90], 'avatar': [96, 144] } + # Allow secret keys exposure via API allowKeysExposure: false # Skip new default content checks diff --git a/post-install.js b/post-install.js index f477df0e0..d3e16a498 100644 --- a/post-install.js +++ b/post-install.js @@ -28,6 +28,24 @@ const color = { white: (mess) => color.byNum(mess, 37), }; +const keyMigrationMap = [ + { + oldKey: 'disableThumbnails', + newKey: 'thumbnails.enabled', + migrate: (value) => !value, + }, + { + oldKey: 'thumbnailsQuality', + newKey: 'thumbnails.quality', + migrate: (value) => value, + }, + { + oldKey: 'avatarThumbnailsPng', + newKey: 'thumbnails.format', + migrate: (value) => (value ? 'png' : 'jpg'), + }, +]; + /** * Gets all keys from an object recursively. * @param {object} obj Object to get all keys from @@ -83,6 +101,24 @@ function addMissingConfigValues() { const defaultConfig = yaml.parse(fs.readFileSync(path.join(process.cwd(), './default/config.yaml'), 'utf8')); let config = yaml.parse(fs.readFileSync(path.join(process.cwd(), './config.yaml'), 'utf8')); + // Migrate old keys to new keys + const migratedKeys = []; + for (const { oldKey, newKey, migrate } of keyMigrationMap) { + if (_.has(config, oldKey)) { + const oldValue = _.get(config, oldKey); + const newValue = migrate(oldValue); + _.set(config, newKey, newValue); + _.unset(config, oldKey); + + migratedKeys.push({ + oldKey, + newKey, + oldValue, + newValue, + }); + } + } + // Get all keys from the original config const originalKeys = getAllKeys(config); @@ -95,11 +131,18 @@ function addMissingConfigValues() { // Find the keys that were added const addedKeys = _.difference(updatedKeys, originalKeys); - if (addedKeys.length === 0) { + if (addedKeys.length === 0 && migratedKeys.length === 0) { return; } - console.log('Adding missing config values to config.yaml:', addedKeys); + if (addedKeys.length > 0) { + console.log('Adding missing config values to config.yaml:', addedKeys); + } + + if (migratedKeys.length > 0) { + console.log('Migrating config values in config.yaml:', migratedKeys); + } + fs.writeFileSync('./config.yaml', yaml.stringify(config)); } catch (error) { console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error); diff --git a/src/endpoints/thumbnails.js b/src/endpoints/thumbnails.js index 275c64f07..8811a5cee 100644 --- a/src/endpoints/thumbnails.js +++ b/src/endpoints/thumbnails.js @@ -12,9 +12,12 @@ import { getAllUserHandles, getUserDirectories } from '../users.js'; import { getConfigValue } from '../util.js'; import { jsonParser } from '../express-common.js'; -const thumbnailsDisabled = getConfigValue('disableThumbnails', false); -const quality = getConfigValue('thumbnailsQuality', 95); -const pngFormat = getConfigValue('avatarThumbnailsPng', false); +const thumbnailsEnabled = !!getConfigValue('thumbnails.enabled', true); +const quality = Math.min(100, Math.max(1, parseInt(getConfigValue('thumbnails.quality', 95)))); +const pngFormat = String(getConfigValue('thumbnails.format', 'jpg')).toLowerCase().trim() === 'png'; + +/** @type {Record} */ +const dimensions = getConfigValue('thumbnails.dimensions', { 'bg': [160, 90], 'avatar': [96, 144] }); /** * Gets a path to thumbnail folder based on the type. @@ -114,16 +117,16 @@ async function generateThumbnail(directories, type, file) { return null; } - const imageSizes = { 'bg': [160, 90], 'avatar': [96, 144] }; - const mySize = imageSizes[type]; - try { let buffer; try { + const size = dimensions[type]; const image = await jimp.read(pathToOriginalFile); const imgType = type == 'avatar' && pngFormat ? 'image/png' : 'image/jpeg'; - buffer = await image.cover(mySize[0], mySize[1]).quality(quality).getBufferAsync(imgType); + const width = !isNaN(size?.[0]) && size?.[0] > 0 ? size[0] : image.bitmap.width; + const height = !isNaN(size?.[1]) && size?.[1] > 0 ? size[1] : image.bitmap.height; + buffer = await image.cover(width, height).quality(quality).getBufferAsync(imgType); } catch (inner) { console.warn(`Thumbnailer can not process the image: ${pathToOriginalFile}. Using original size`); @@ -193,7 +196,7 @@ router.get('/', jsonParser, async function (request, response) { return response.sendStatus(403); } - if (thumbnailsDisabled) { + if (!thumbnailsEnabled) { const folder = getOriginalFolder(request.user.directories, type); if (folder === undefined) {