enhance(server): 画像圧縮周り(主にサムネイルの仕様)の変更 (#10287)
* DriveService, is-mime-image * static, previewをavifに, アニメーション画像でもthumbnailを生成 * fallback * animated: true * fix * avatarはwebp * revert ?? file.url --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
parent
cc7fd2f68a
commit
88e3d3e8cb
10 changed files with 101 additions and 84 deletions
|
@ -2,6 +2,7 @@ import * as fs from 'node:fs';
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import sharp from 'sharp';
|
||||
import { sharpBmp } from 'sharp-read-bmp';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js';
|
||||
|
@ -34,6 +35,7 @@ import { FileInfoService } from '@/core/FileInfoService.js';
|
|||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||
import type S3 from 'aws-sdk/clients/s3.js';
|
||||
|
||||
type AddFileArgs = {
|
||||
|
@ -274,8 +276,8 @@ export class DriveService {
|
|||
}
|
||||
}
|
||||
|
||||
if (!['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/svg+xml'].includes(type)) {
|
||||
this.registerLogger.debug('web image and thumbnail not created (not an required file)');
|
||||
if (!isMimeImage(type, 'sharp-convertible-image-with-bmp')) {
|
||||
this.registerLogger.debug('web image and thumbnail not created (cannot convert by sharp)');
|
||||
return {
|
||||
webpublic: null,
|
||||
thumbnail: null,
|
||||
|
@ -284,22 +286,16 @@ export class DriveService {
|
|||
|
||||
let img: sharp.Sharp | null = null;
|
||||
let satisfyWebpublic: boolean;
|
||||
let isAnimated: boolean;
|
||||
|
||||
try {
|
||||
img = sharp(path);
|
||||
img = await sharpBmp(path, type);
|
||||
const metadata = await img.metadata();
|
||||
const isAnimated = metadata.pages && metadata.pages > 1;
|
||||
|
||||
// skip animated
|
||||
if (isAnimated) {
|
||||
return {
|
||||
webpublic: null,
|
||||
thumbnail: null,
|
||||
};
|
||||
}
|
||||
isAnimated = !!(metadata.pages && metadata.pages > 1);
|
||||
|
||||
satisfyWebpublic = !!(
|
||||
type !== 'image/svg+xml' && type !== 'image/avif' &&
|
||||
type !== 'image/svg+xml' && // security reason
|
||||
type !== 'image/avif' && // not supported by Mastodon
|
||||
!(metadata.exif ?? metadata.iptc ?? metadata.xmp ?? metadata.tifftagPhotoshop) &&
|
||||
metadata.width && metadata.width <= 2048 &&
|
||||
metadata.height && metadata.height <= 2048
|
||||
|
@ -315,15 +311,13 @@ export class DriveService {
|
|||
// #region webpublic
|
||||
let webpublic: IImage | null = null;
|
||||
|
||||
if (generateWeb && !satisfyWebpublic) {
|
||||
if (generateWeb && !satisfyWebpublic && !isAnimated) {
|
||||
this.registerLogger.info('creating web image');
|
||||
|
||||
try {
|
||||
if (type === 'image/jpeg') {
|
||||
webpublic = await this.imageProcessingService.convertSharpToJpeg(img, 2048, 2048);
|
||||
} else if (['image/webp', 'image/avif'].includes(type)) {
|
||||
if (['image/jpeg', 'image/webp', 'image/avif'].includes(type)) {
|
||||
webpublic = await this.imageProcessingService.convertSharpToWebp(img, 2048, 2048);
|
||||
} else if (['image/png', 'image/svg+xml'].includes(type)) {
|
||||
} else if (['image/png', 'image/bmp', 'image/svg+xml'].includes(type)) {
|
||||
webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048);
|
||||
} else {
|
||||
this.registerLogger.debug('web image not created (not an required image)');
|
||||
|
@ -333,6 +327,7 @@ export class DriveService {
|
|||
}
|
||||
} else {
|
||||
if (satisfyWebpublic) this.registerLogger.info('web image not created (original satisfies webpublic)');
|
||||
else if (isAnimated) this.registerLogger.info('web image not created (animated image)');
|
||||
else this.registerLogger.info('web image not created (from remote)');
|
||||
}
|
||||
// #endregion webpublic
|
||||
|
@ -341,10 +336,10 @@ export class DriveService {
|
|||
let thumbnail: IImage | null = null;
|
||||
|
||||
try {
|
||||
if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(type)) {
|
||||
thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 280);
|
||||
if (isAnimated) {
|
||||
thumbnail = await this.imageProcessingService.convertSharpToWebp(sharp(path, { animated: true }), 374, 317, { alphaQuality: 70 });
|
||||
} else {
|
||||
this.registerLogger.debug('thumbnail not created (not an required file)');
|
||||
thumbnail = await this.imageProcessingService.convertSharpToAvif(img, 498, 422);
|
||||
}
|
||||
} catch (err) {
|
||||
this.registerLogger.warn('thumbnail not created (an error occured)', err as Error);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue