From 2d5e836e96994abd1604f60320e66568c79f4d1e Mon Sep 17 00:00:00 2001 From: Skystryder Date: Sat, 28 Jan 2023 17:46:03 -0800 Subject: [PATCH 1/2] Cleaning up image conversions to use webp, and increasing the thumbnail size. --- .../src/server/file/send-drive-file.ts | 10 +-- .../backend/src/services/drive/add-file.ts | 20 +++--- .../drive/generate-video-thumbnail.ts | 5 +- .../src/services/drive/image-processor.ts | 71 +------------------ 4 files changed, 14 insertions(+), 92 deletions(-) diff --git a/packages/backend/src/server/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts index 26a755ad2..087736902 100644 --- a/packages/backend/src/server/file/send-drive-file.ts +++ b/packages/backend/src/server/file/send-drive-file.ts @@ -11,11 +11,7 @@ import { InternalStorage } from "@/services/drive/internal-storage.js"; import { createTemp } from "@/misc/create-temp.js"; import { downloadUrl } from "@/misc/download-url.js"; import { detectType } from "@/misc/get-file-info.js"; -import { - convertToWebp, - convertToJpeg, - convertToPng, -} from "@/services/drive/image-processor.js"; +import { convertToWebp } from "@/services/drive/image-processor.js"; import { GenerateVideoThumbnail } from "@/services/drive/generate-video-thumbnail.js"; import { StatusError } from "@/misc/fetch.js"; import { FILE_TYPE_BROWSERSAFE } from "@/const.js"; @@ -77,7 +73,7 @@ export default async function (ctx: Koa.Context) { "image/avif", ].includes(mime) ) { - return await convertToWebp(path, 498, 280); + return await convertToWebp(path, 996, 560); } else if (mime.startsWith("video/")) { return await GenerateVideoThumbnail(path); } @@ -85,7 +81,7 @@ export default async function (ctx: Koa.Context) { if (isWebpublic) { if (["image/svg+xml"].includes(mime)) { - return await convertToPng(path, 2048, 2048); + return await convertToWebp(path, 2048, 2048, 100); } } diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts index da6994024..b25375b94 100644 --- a/packages/backend/src/services/drive/add-file.ts +++ b/packages/backend/src/services/drive/add-file.ts @@ -30,11 +30,7 @@ import { IdentifiableError } from "@/misc/identifiable-error.js"; import { getS3 } from "./s3.js"; import { InternalStorage } from "./internal-storage.js"; import type { IImage } from "./image-processor.js"; -import { - convertSharpToJpeg, - convertSharpToWebp, - convertSharpToPng, -} from "./image-processor.js"; +import { convertSharpToWebp } from "./image-processor.js"; import { driveLogger } from "./logger.js"; import { GenerateVideoThumbnail } from "./generate-video-thumbnail.js"; import { deleteFile } from "./delete-file.js"; @@ -75,8 +71,8 @@ async function save( if (type === "image/vnd.mozilla.apng") ext = ".apng"; } - // 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、 - // 許可されているファイル形式でしか拡張子をつけない + // Some cloud providers (notably upcloud) will infer the content-type based + // on extension, so we remove extensions from non-browser-safe types. if (!FILE_TYPE_BROWSERSAFE.includes(type)) { ext = ""; } @@ -282,13 +278,13 @@ export async function generateAlts( try { if (["image/jpeg"].includes(type)) { - webpublic = await convertSharpToJpeg(img, 2048, 2048); + webpublic = await convertSharpToWebp(img, 2048, 2048); } else if (["image/webp"].includes(type)) { - webpublic = await convertSharpToPng(img, 2048, 2048); + webpublic = await convertSharpToWebp(img, 2048, 2048); } else if (["image/png"].includes(type)) { - webpublic = await convertSharpToPng(img, 2048, 2048); + webpublic = await convertSharpToWebp(img, 2048, 2048, 100); } else if (["image/svg+xml"].includes(type)) { - webpublic = await convertSharpToPng(img, 2048, 2048); + webpublic = await convertSharpToWebp(img, 2048, 2048); } else { logger.debug("web image not created (not an required image)"); } @@ -315,7 +311,7 @@ export async function generateAlts( "image/avif", ].includes(type) ) { - thumbnail = await convertSharpToWebp(img, 498, 280); + thumbnail = await convertSharpToWebp(img, 996, 560); } else { logger.debug("thumbnail not created (not an required file)"); } diff --git a/packages/backend/src/services/drive/generate-video-thumbnail.ts b/packages/backend/src/services/drive/generate-video-thumbnail.ts index e12d00936..356623e79 100644 --- a/packages/backend/src/services/drive/generate-video-thumbnail.ts +++ b/packages/backend/src/services/drive/generate-video-thumbnail.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; import { createTempDir } from "@/misc/create-temp.js"; import type { IImage } from "./image-processor.js"; -import { convertToJpeg } from "./image-processor.js"; +import { convertToWebp } from "./image-processor.js"; import FFmpeg from "fluent-ffmpeg"; export async function GenerateVideoThumbnail(source: string): Promise { @@ -22,8 +22,7 @@ export async function GenerateVideoThumbnail(source: string): Promise { }); }); - // JPEGに変換 (Webpでもいいが、MastodonはWebpをサポートせず表示できなくなる) - return await convertToJpeg(`${dir}/out.png`, 498, 280); + return await convertToWebp(`${dir}/out.png`, 996, 560); } finally { cleanup(); } diff --git a/packages/backend/src/services/drive/image-processor.ts b/packages/backend/src/services/drive/image-processor.ts index 23404139b..0934aa806 100644 --- a/packages/backend/src/services/drive/image-processor.ts +++ b/packages/backend/src/services/drive/image-processor.ts @@ -6,42 +6,6 @@ export type IImage = { type: string; }; -/** - * Convert to JPEG - * with resize, remove metadata, resolve orientation, stop animation - */ -export async function convertToJpeg( - path: string, - width: number, - height: number, -): Promise { - return convertSharpToJpeg(await sharp(path), width, height); -} - -export async function convertSharpToJpeg( - sharp: sharp.Sharp, - width: number, - height: number, -): Promise { - const data = await sharp - .resize(width, height, { - fit: "inside", - withoutEnlargement: true, - }) - .rotate() - .jpeg({ - quality: 85, - progressive: true, - }) - .toBuffer(); - - return { - data, - ext: "jpg", - type: "image/jpeg", - }; -} - /** * Convert to WebP * with resize, remove metadata, resolve orientation, stop animation @@ -61,7 +25,7 @@ export async function convertSharpToWebp( height: number, quality: number = 85, ): Promise { - const data = await sharp + const data = await sharp .resize(width, height, { fit: "inside", withoutEnlargement: true, @@ -78,36 +42,3 @@ export async function convertSharpToWebp( type: "image/webp", }; } - -/** - * Convert to PNG - * with resize, remove metadata, resolve orientation, stop animation - */ -export async function convertToPng( - path: string, - width: number, - height: number, -): Promise { - return convertSharpToPng(await sharp(path), width, height); -} - -export async function convertSharpToPng( - sharp: sharp.Sharp, - width: number, - height: number, -): Promise { - const data = await sharp - .resize(width, height, { - fit: "inside", - withoutEnlargement: true, - }) - .rotate() - .png() - .toBuffer(); - - return { - data, - ext: "png", - type: "image/png", - }; -} From 6d65843f7871a6da2faa4fe7f2d4fba28d7dba13 Mon Sep 17 00:00:00 2001 From: Skystryder Date: Sat, 28 Jan 2023 18:00:08 -0800 Subject: [PATCH 2/2] Also increase the size for proxy. --- packages/backend/src/server/proxy/proxy-media.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index 47ede2159..a9c257bfe 100644 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ b/packages/backend/src/server/proxy/proxy-media.ts @@ -31,9 +31,9 @@ export async function proxyMedia(ctx: Koa.Context) { let image: IImage; if ("static" in ctx.query && isConvertibleImage) { - image = await convertToWebp(path, 498, 280); + image = await convertToWebp(path, 996, 560); } else if ("preview" in ctx.query && isConvertibleImage) { - image = await convertToWebp(path, 200, 200); + image = await convertToWebp(path, 400, 400); } else if ("badge" in ctx.query) { if (!isConvertibleImage) { // 画像でないなら404でお茶を濁す