From 3f53cbd8f680c5659c53bf709fdd044c5dcb59c7 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight 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 (硫酸鶏) * update test * webpはwebpublicにできる --------- Co-authored-by: Acid Chicken (硫酸鶏) Co-authored-by: tamaina --- 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 { // 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 { 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 => { + // 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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 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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 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;