From 1e539df6152da6d62699602aa200dd03c0c12b94 Mon Sep 17 00:00:00 2001 From: Sven Sauleau Date: Fri, 20 Jan 2023 11:05:11 +0000 Subject: [PATCH] MOW-119: preserve attachement from remote statuses --- backend/src/activitypub/objects/note.ts | 7 +-- backend/src/mastodon/status.ts | 18 +----- backend/src/media/index.ts | 83 +++++++++++++------------ backend/test/mastodon/accounts.spec.ts | 24 +++++++ 4 files changed, 74 insertions(+), 58 deletions(-) diff --git a/backend/src/activitypub/objects/note.ts b/backend/src/activitypub/objects/note.ts index 38d94a5..4755aa1 100644 --- a/backend/src/activitypub/objects/note.ts +++ b/backend/src/activitypub/objects/note.ts @@ -1,7 +1,6 @@ // https://www.w3.org/TR/activitystreams-vocabulary/#object-types import type { Actor } from 'wildebeest/backend/src/activitypub/actors' -import type { Document } from 'wildebeest/backend/src/activitypub/objects' import { followersURL } from 'wildebeest/backend/src/activitypub/actors' import { PUBLIC_GROUP } from 'wildebeest/backend/src/activitypub/activities' import * as objects from '.' @@ -16,7 +15,7 @@ export interface Note extends objects.Object { inReplyTo?: string replies?: string to: Array - attachment: Array + attachment: Array cc?: Array tag?: Array } @@ -26,7 +25,7 @@ export async function createPublicNote( db: D1Database, content: string, actor: Actor, - attachment: Array = [], + attachment: Array = [], extraProperties: any = {} ): Promise { const actorId = new URL(actor.id) @@ -57,7 +56,7 @@ export async function createPrivateNote( content: string, actor: Actor, targetActor: Actor, - attachment: Array = [], + attachment: Array = [], extraProperties: any = {} ): Promise { const actorId = new URL(actor.id) diff --git a/backend/src/mastodon/status.ts b/backend/src/mastodon/status.ts index cc6174b..e362695 100644 --- a/backend/src/mastodon/status.ts +++ b/backend/src/mastodon/status.ts @@ -1,7 +1,7 @@ import type { Handle } from '../utils/parse' import type { MediaAttachment } from 'wildebeest/backend/src/types/media' import type { UUID } from 'wildebeest/backend/src/types' -import { getObjectByMastodonId, getObjectById } from 'wildebeest/backend/src/activitypub/objects' +import { getObjectByMastodonId } from 'wildebeest/backend/src/activitypub/objects' import type { Note } from 'wildebeest/backend/src/activitypub/objects/note' import { loadExternalMastodonAccount } from 'wildebeest/backend/src/mastodon/account' import * as actors from 'wildebeest/backend/src/activitypub/actors' @@ -51,22 +51,10 @@ export async function toMastodonStatusFromObject( // const favourites = await getLikes(db, obj) // const reblogs = await getReblogs(db, obj) - const mediaAttachments: Array = [] + let mediaAttachments: Array = [] if (Array.isArray(obj.attachment)) { - for (let i = 0, len = obj.attachment.length; i < len; i++) { - if (obj.attachment[i].id) { - const document = await getObjectById(db, obj.attachment[i].id) - if (document === null) { - console.warn('missing attachment object: ' + obj.attachment[i].id) - continue - } - - mediaAttachments.push(media.fromObject(document)) - } else { - console.warn('attachment has no id') - } - } + mediaAttachments = obj.attachment.map(media.fromObject) } return { diff --git a/backend/src/media/index.ts b/backend/src/media/index.ts index 18d0160..2bd0721 100644 --- a/backend/src/media/index.ts +++ b/backend/src/media/index.ts @@ -1,4 +1,5 @@ import type { MediaAttachment } from 'wildebeest/backend/src/types/media' +import type { Document } from 'wildebeest/backend/src/activitypub/objects' import { IMAGE } from 'wildebeest/backend/src/activitypub/objects/image' import type { Object } from 'wildebeest/backend/src/activitypub/objects' @@ -6,50 +7,54 @@ export function fromObject(obj: Object): MediaAttachment { if (obj.type === IMAGE) { return fromObjectImage(obj) } else if (obj.type === 'Document') { - if (obj.mediaType === 'image/jpeg' || obj.mediaType === 'image/png') { - return fromObjectImage(obj) - } else if (obj.mediaType === 'video/mp4') { - return { - url: new URL(obj.url), - preview_url: new URL(obj.url), - id: obj.url.toString(), - type: 'video', - meta: { - length: '0:01:28.65', - duration: 88.65, - fps: 24, - size: '1280x720', - width: 1280, - height: 720, - aspect: 1.7777777777777777, - audio_encode: 'aac (LC) (mp4a / 0x6134706D)', - audio_bitrate: '44100 Hz', - audio_channels: 'stereo', - original: { - width: 1280, - height: 720, - frame_rate: '6159375/249269', - duration: 88.654, - bitrate: 862056, - }, - small: { - width: 400, - height: 225, - size: '400x225', - aspect: 1.7777777777777777, - }, - }, - description: 'test media description', - blurhash: 'UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}', - } - } else { - throw new Error(`unsupported media type ${obj.type}: ${JSON.stringify(obj)}`) - } + return fromObjectDocument(obj as Document) } else { throw new Error(`unsupported media type ${obj.type}: ${JSON.stringify(obj)}`) } } +export function fromObjectDocument(obj: Document): MediaAttachment { + if (obj.mediaType === 'image/jpeg' || obj.mediaType === 'image/png') { + return fromObjectImage(obj) + } else if (obj.mediaType === 'video/mp4') { + return { + url: new URL(obj.url), + preview_url: new URL(obj.url), + id: obj.url.toString(), + type: 'video', + meta: { + length: '0:01:28.65', + duration: 88.65, + fps: 24, + size: '1280x720', + width: 1280, + height: 720, + aspect: 1.7777777777777777, + audio_encode: 'aac (LC) (mp4a / 0x6134706D)', + audio_bitrate: '44100 Hz', + audio_channels: 'stereo', + original: { + width: 1280, + height: 720, + frame_rate: '6159375/249269', + duration: 88.654, + bitrate: 862056, + }, + small: { + width: 400, + height: 225, + size: '400x225', + aspect: 1.7777777777777777, + }, + }, + description: 'test media description', + blurhash: 'UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}', + } + } else { + throw new Error(`unsupported media Document type: ${JSON.stringify(obj)}`) + } +} + function fromObjectImage(obj: Object): MediaAttachment { return { url: new URL(obj.url), diff --git a/backend/test/mastodon/accounts.spec.ts b/backend/test/mastodon/accounts.spec.ts index b8bfbff..7ca0782 100644 --- a/backend/test/mastodon/accounts.spec.ts +++ b/backend/test/mastodon/accounts.spec.ts @@ -533,6 +533,26 @@ describe('Mastodon APIs', () => { id: 'https://example.com/object1', type: 'Note', content: '

p

', + attachment: [ + { + type: 'Document', + mediaType: 'image/jpeg', + url: 'https://example.com/image', + name: null, + blurhash: 'U48;V;_24mx[_1~p.7%MW9?a-;xtxvWBt6ad', + width: 1080, + height: 894, + }, + { + type: 'Document', + mediaType: 'video/mp4', + url: 'https://example.com/video', + name: null, + blurhash: 'UB9jfvtT0gO^N5tSX4XV9uR%^Ni]D%Rj$*nf', + width: 1080, + height: 616, + }, + ], }, }, { @@ -559,6 +579,10 @@ describe('Mastodon APIs', () => { assert.equal(data[0].content, '

p

') assert.equal(data[0].account.username, 'someone') + assert.equal(data[0].media_attachments.length, 2) + assert.equal(data[0].media_attachments[0].type, 'image') + assert.equal(data[0].media_attachments[1].type, 'video') + // Statuses were imported locally and once was a reblog of an already // existing local object. const row = await db.prepare(`SELECT count(*) as count FROM objects`).first()