diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 2a28405121..300cdbd0a0 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -5,6 +5,7 @@
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
+import { load as cheerio } from 'cheerio/slim';
import { UnrecoverableError } from 'bullmq';
import { DI } from '@/di-symbols.js';
import type { UsersRepository, PollsRepository, EmojisRepository, NotesRepository, MiMeta } from '@/models/_.js';
@@ -41,6 +42,7 @@ import { ApQuestionService } from './ApQuestionService.js';
import { ApImageService } from './ApImageService.js';
import type { Resolver } from '../ApResolverService.js';
import type { IObject, IPost } from '../type.js';
+import type { CheerioAPI } from 'cheerio/slim';
@Injectable()
export class ApNoteService {
@@ -265,6 +267,16 @@ export class ApNoteService {
if (file) files.push(file);
}
+ // Extract inline media from note content.
+ // Don't use source.content, _misskey_content, or anything else because those aren't HTML.
+ if (note.content) {
+ for (const attach of extractInlineMedia(note.content)) {
+ attach.sensitive ??= note.sensitive;
+ const file = await this.apImageService.resolveImage(actor, attach);
+ if (file) files.push(file);
+ }
+ }
+
// リプライ
const reply: MiNote | null = note.inReplyTo
? await this.resolveNote(note.inReplyTo, { resolver })
@@ -463,6 +475,16 @@ export class ApNoteService {
if (file) files.push(file);
}
+ // Extract inline media from note content.
+ // Don't use source.content, _misskey_content, or anything else because those aren't HTML.
+ if (note.content) {
+ for (const attach of extractInlineMedia(note.content)) {
+ attach.sensitive ??= note.sensitive;
+ const file = await this.apImageService.resolveImage(actor, attach);
+ if (file) files.push(file);
+ }
+ }
+
// リプライ
const reply: MiNote | null = note.inReplyTo
? await this.resolveNote(note.inReplyTo, { resolver })
@@ -741,3 +763,73 @@ function getBestIcon(note: IObject): IObject | null {
return best;
}, null as IApDocument | null) ?? null;
}
+
+function extractInlineMedia(html: string): IApDocument[] {
+ const $ = parseHtml(html);
+ if (!$) return [];
+
+ const attachments: IApDocument[] = [];
+
+ //
tags, including and