From 3f53cbd8f680c5659c53bf709fdd044c5dcb59c7 Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Fri, 10 Mar 2023 01:37:22 +0100
Subject: [PATCH] fix(backend/DriveService): convert WebP/AVIF to WebP (#10239)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend/DriveService): convert transparent WebP/AVIF to PNG

* webpにする

その希望が複数ありましたので

* Update packages/backend/src/core/DriveService.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* update test

* webpはwebpublicにできる

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
---
 packages/backend/src/core/DriveService.ts     |  12 ++--
 .../src/core/activitypub/ApRendererService.ts |  68 +++++++++---------
 packages/backend/test/e2e/endpoints.ts        |  41 ++++++++++-
 .../backend/test/resources/with-alpha.avif    | Bin 0 -> 10032 bytes
 .../backend/test/resources/with-alpha.webp    | Bin 0 -> 4984 bytes
 .../backend/test/resources/without-alpha.avif | Bin 0 -> 3982 bytes
 .../backend/test/resources/without-alpha.webp | Bin 0 -> 4474 bytes
 packages/backend/test/utils.ts                |   7 +-
 8 files changed, 86 insertions(+), 42 deletions(-)
 create mode 100644 packages/backend/test/resources/with-alpha.avif
 create mode 100644 packages/backend/test/resources/with-alpha.webp
 create mode 100644 packages/backend/test/resources/without-alpha.avif
 create mode 100644 packages/backend/test/resources/without-alpha.webp

diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index f4a06faebb..c3835faa33 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -299,7 +299,7 @@ export class DriveService {
 			}
 
 			satisfyWebpublic = !!(
-				type !== 'image/svg+xml' && type !== 'image/webp' && type !== 'image/avif' &&
+				type !== 'image/svg+xml' && type !== 'image/avif' &&
 			!(metadata.exif ?? metadata.iptc ?? metadata.xmp ?? metadata.tifftagPhotoshop) &&
 			metadata.width && metadata.width <= 2048 &&
 			metadata.height && metadata.height <= 2048
@@ -319,11 +319,11 @@ export class DriveService {
 			this.registerLogger.info('creating web image');
 
 			try {
-				if (['image/jpeg', 'image/webp', 'image/avif'].includes(type)) {
+				if (type === 'image/jpeg') {
 					webpublic = await this.imageProcessingService.convertSharpToJpeg(img, 2048, 2048);
-				} else if (['image/png'].includes(type)) {
-					webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048);
-				} else if (['image/svg+xml'].includes(type)) {
+				} else if (['image/webp', 'image/avif'].includes(type)) {
+					webpublic = await this.imageProcessingService.convertSharpToWebp(img, 2048, 2048);
+				} else if (['image/png', '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)');
@@ -749,7 +749,7 @@ export class DriveService {
 	}: UploadFromUrlArgs): Promise<DriveFile> {
 		// Create temp file
 		const [path, cleanup] = await createTemp();
-	
+
 		try {
 			// write content at URL to temp file
 			const { filename: name } = await this.downloadService.downloadUrl(url, path);
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 6a1f233bd8..4601eca2f0 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -116,7 +116,7 @@ export class ApRendererService {
 		if (block.blockee?.uri == null) {
 			throw new Error('renderBlock: missing blockee uri');
 		}
-	
+
 		return {
 			type: 'Block',
 			id: `${this.config.url}/blocks/${block.id}`,
@@ -134,10 +134,10 @@ export class ApRendererService {
 			published: note.createdAt.toISOString(),
 			object,
 		} as ICreate;
-	
+
 		if (object.to) activity.to = object.to;
 		if (object.cc) activity.cc = object.cc;
-	
+
 		return activity;
 	}
 
@@ -155,7 +155,7 @@ export class ApRendererService {
 	public renderDocument(file: DriveFile): IApDocument {
 		return {
 			type: 'Document',
-			mediaType: file.type,
+			mediaType: file.webpublicType ?? file.type,
 			url: this.driveFileEntityService.getPublicUrl(file),
 			name: file.comment,
 		};
@@ -297,16 +297,16 @@ export class ApRendererService {
 			const items = await this.driveFilesRepository.findBy({ id: In(ids) });
 			return ids.map(id => items.find(item => item.id === id)).filter(item => item != null) as DriveFile[];
 		};
-	
+
 		let inReplyTo;
 		let inReplyToNote: Note | null;
-	
+
 		if (note.replyId) {
 			inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
-	
+
 			if (inReplyToNote != null) {
 				const inReplyToUser = await this.usersRepository.findOneBy({ id: inReplyToNote.userId });
-	
+
 				if (inReplyToUser != null) {
 					if (inReplyToNote.uri) {
 						inReplyTo = inReplyToNote.uri;
@@ -322,24 +322,24 @@ export class ApRendererService {
 		} else {
 			inReplyTo = null;
 		}
-	
+
 		let quote;
-	
+
 		if (note.renoteId) {
 			const renote = await this.notesRepository.findOneBy({ id: note.renoteId });
-	
+
 			if (renote) {
 				quote = renote.uri ? renote.uri : `${this.config.url}/notes/${renote.id}`;
 			}
 		}
-	
+
 		const attributedTo = `${this.config.url}/users/${note.userId}`;
-	
+
 		const mentions = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri);
-	
+
 		let to: string[] = [];
 		let cc: string[] = [];
-	
+
 		if (note.visibility === 'public') {
 			to = ['https://www.w3.org/ns/activitystreams#Public'];
 			cc = [`${attributedTo}/followers`].concat(mentions);
@@ -352,44 +352,44 @@ export class ApRendererService {
 		} else {
 			to = mentions;
 		}
-	
+
 		const mentionedUsers = note.mentions.length > 0 ? await this.usersRepository.findBy({
 			id: In(note.mentions),
 		}) : [];
-	
+
 		const hashtagTags = (note.tags ?? []).map(tag => this.renderHashtag(tag));
 		const mentionTags = mentionedUsers.map(u => this.renderMention(u));
-	
+
 		const files = await getPromisedFiles(note.fileIds);
-	
+
 		const text = note.text ?? '';
 		let poll: Poll | null = null;
-	
+
 		if (note.hasPoll) {
 			poll = await this.pollsRepository.findOneBy({ noteId: note.id });
 		}
-	
+
 		let apText = text;
-	
+
 		if (quote) {
 			apText += `\n\nRE: ${quote}`;
 		}
-	
+
 		const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
-	
+
 		const content = this.apMfmService.getNoteHtml(Object.assign({}, note, {
 			text: apText,
 		}));
-	
+
 		const emojis = await this.getEmojis(note.emojis);
 		const apemojis = emojis.map(emoji => this.renderEmoji(emoji));
-	
+
 		const tag = [
 			...hashtagTags,
 			...mentionTags,
 			...apemojis,
 		];
-	
+
 		const asPoll = poll ? {
 			type: 'Question',
 			content: this.apMfmService.getNoteHtml(Object.assign({}, note, {
@@ -601,7 +601,7 @@ export class ApRendererService {
 		if (typeof x === 'object' && x.id == null) {
 			x.id = `${this.config.url}/${uuid()}`;
 		}
-	
+
 		return Object.assign({
 			'@context': [
 				'https://www.w3.org/ns/activitystreams',
@@ -634,18 +634,18 @@ export class ApRendererService {
 			],
 		}, x as T & { id: string; });
 	}
-	
+
 	@bindThis
 	public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> {
 		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id);
-	
+
 		const ldSignature = this.ldSignatureService.use();
 		ldSignature.debug = false;
 		activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`);
-	
+
 		return activity;
 	}
-	
+
 	/**
 	 * Render OrderedCollectionPage
 	 * @param id URL of self
@@ -686,11 +686,11 @@ export class ApRendererService {
 			type: 'OrderedCollection',
 			totalItems,
 		};
-	
+
 		if (first) page.first = first;
 		if (last) page.last = last;
 		if (orderedItems) page.orderedItems = orderedItems;
-	
+
 		return page;
 	}
 
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index c130093732..653520cf5f 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -4,7 +4,7 @@ import * as assert from 'assert';
 // node-fetch only supports it's own Blob yet
 // https://github.com/node-fetch/node-fetch/pull/1664
 import { Blob } from 'node-fetch';
-import { startServer, signup, post, api, uploadFile } from '../utils.js';
+import { startServer, signup, post, api, uploadFile, simpleGet } from '../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
 
 describe('Endpoints', () => {
@@ -439,6 +439,45 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body.name, 'image.svg');
 			assert.strictEqual(res.body.type, 'image/svg+xml');
 		});
+
+		for (const type of ['webp', 'avif']) {
+			const mediaType = `image/${type}`;
+
+			const getWebpublicType = async (user: any, fileId: string): Promise<string> => {
+				// drive/files/create does not expose webpublicType directly, so get it by posting it
+				const res = await post(user, {
+					text: mediaType,
+					fileIds: [fileId],
+				});
+				const apRes = await simpleGet(`notes/${res.id}`, 'application/activity+json');
+				assert.strictEqual(apRes.status, 200);
+				assert.ok(Array.isArray(apRes.body.attachment));
+				return apRes.body.attachment[0].mediaType;
+			};
+
+			test(`透明な${type}ファイルを作成できる`, async () => {
+				const path = `with-alpha.${type}`;
+				const res = await uploadFile(alice, { path });
+
+				assert.strictEqual(res.status, 200);
+				assert.strictEqual(res.body.name, path);
+				assert.strictEqual(res.body.type, mediaType);
+
+				const webpublicType = await getWebpublicType(alice, res.body.id);
+				assert.strictEqual(webpublicType, 'image/webp');
+			});
+
+			test(`透明じゃない${type}ファイルを作成できる`, async () => {
+				const path = `without-alpha.${type}`;
+				const res = await uploadFile(alice, { path });
+				assert.strictEqual(res.status, 200);
+				assert.strictEqual(res.body.name, path);
+				assert.strictEqual(res.body.type, mediaType);
+
+				const webpublicType = await getWebpublicType(alice, res.body.id);
+				assert.strictEqual(webpublicType, 'image/webp');
+			});
+		}
 	});
 
 	describe('drive/files/update', () => {
diff --git a/packages/backend/test/resources/with-alpha.avif b/packages/backend/test/resources/with-alpha.avif
new file mode 100644
index 0000000000000000000000000000000000000000..05f494212eff008216ce34d939b545aeaf288e88
GIT binary patch
literal 10032
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

literal 0
HcmV?d00001

diff --git a/packages/backend/test/resources/with-alpha.webp b/packages/backend/test/resources/with-alpha.webp
new file mode 100644
index 0000000000000000000000000000000000000000..d7b0d70b7fdbbd9ef4be7e2d680aeee7b7821d5a
GIT binary patch
literal 4984
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
EKN@#aRsaA1

literal 0
HcmV?d00001

diff --git a/packages/backend/test/resources/without-alpha.avif b/packages/backend/test/resources/without-alpha.avif
new file mode 100644
index 0000000000000000000000000000000000000000..9ea23608b85155a9bbd933961dba3b6604ef8147
GIT binary patch
literal 3982
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

literal 0
HcmV?d00001

diff --git a/packages/backend/test/resources/without-alpha.webp b/packages/backend/test/resources/without-alpha.webp
new file mode 100644
index 0000000000000000000000000000000000000000..a51091efe24081ecea3d6f38d35bd8625d48b608
GIT binary patch
literal 4474
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
WO9AMwgQ0@_CHvP+QbOXN+y4NnZExrR

literal 0
HcmV?d00001

diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 37e5ae10d6..d1a5d6d949 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -204,7 +204,12 @@ export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status:
 		redirect: 'manual',
 	});
 
-	const body = res.headers.get('content-type') === 'application/json; charset=utf-8'
+	const jsonTypes = [
+		'application/json; charset=utf-8',
+		'application/activity+json; charset=utf-8',
+	];
+
+	const body = jsonTypes.includes(res.headers.get('content-type') ?? '')
 		? await res.json()
 		: null;