diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index 8904b2b..d8186f5 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -5,7 +5,7 @@ import { z } from 'zod'; import { type AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; import { DittoDB } from '@/db/DittoDB.ts'; -import { getUnattachedMediaByIds } from '@/db/unattached-media.ts'; +import { getUnattachedMediaByUrls } from '@/db/unattached-media.ts'; import { getAncestors, getAuthor, getDescendants, getEvent } from '@/queries.ts'; import { addTag, deleteTag } from '@/tags.ts'; import { createEvent, paginationSchema, parseBody, updateListEvent } from '@/utils/api.ts'; @@ -94,9 +94,15 @@ const createStatusController: AppController = async (c) => { const viewerPubkey = await c.get('signer')?.getPublicKey(); if (data.media_ids?.length) { - const media = await getUnattachedMediaByIds(kysely, data.media_ids) + const media = await getUnattachedMediaByUrls(kysely, data.media_ids) .then((media) => media.filter(({ pubkey }) => pubkey === viewerPubkey)) - .then((media) => media.map(({ data }) => ['imeta', ...data])); + .then((media) => + media.map(({ data }) => { + const tags: string[][] = JSON.parse(data); + const values: string[] = tags.map((tag) => tag.join(' ')); + return ['imeta', ...values]; + }) + ); tags.push(...media); } diff --git a/src/db/unattached-media.ts b/src/db/unattached-media.ts index 0628278..397a1f7 100644 --- a/src/db/unattached-media.ts +++ b/src/db/unattached-media.ts @@ -64,6 +64,14 @@ async function getUnattachedMediaByIds(kysely: Kysely, ids: string[ .execute(); } +/** Get unattached media by URLs. */ +async function getUnattachedMediaByUrls(kysely: Kysely, urls: string[]) { + if (!urls.length) return []; + return await selectUnattachedMediaQuery(kysely) + .where('url', 'in', urls) + .execute(); +} + /** Delete rows as an event with media is being created. */ async function deleteAttachedMedia(pubkey: string, urls: string[]): Promise { if (!urls.length) return; @@ -79,6 +87,7 @@ export { deleteUnattachedMediaByUrl, getUnattachedMedia, getUnattachedMediaByIds, + getUnattachedMediaByUrls, insertUnattachedMedia, type UnattachedMedia, }; diff --git a/src/utils/api.ts b/src/utils/api.ts index dceede7..c54f5aa 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -29,6 +29,8 @@ async function createEvent(t: EventStub, c: AppContext): Promise { }); } + console.log(t); + const event = await signer.signEvent({ content: '', created_at: nostrNow(), diff --git a/src/utils/note.ts b/src/utils/note.ts index 580c207..20cb83a 100644 --- a/src/utils/note.ts +++ b/src/utils/note.ts @@ -5,7 +5,6 @@ import { nip19, nip21 } from 'nostr-tools'; import { Conf } from '@/config.ts'; import { getUrlMediaType, isPermittedMediaType } from '@/utils/media.ts'; -import { type DittoAttachment } from '@/views/mastodon/attachments.ts'; linkify.registerCustomProtocol('nostr', true); linkify.registerCustomProtocol('wss'); @@ -58,18 +57,17 @@ function parseNoteContent(content: string): ParsedNoteContent { }; } -function getMediaLinks(links: Pick[]): DittoAttachment[] { - return links.reduce((acc, link) => { +/** Returns a matrix of tags. Each item is a list of NIP-94 tags representing a file. */ +function getMediaLinks(links: Pick[]): string[][][] { + return links.reduce((acc, link) => { const mediaType = getUrlMediaType(link.href); if (!mediaType) return acc; if (isPermittedMediaType(mediaType, ['audio', 'image', 'video'])) { - acc.push({ - url: link.href, - data: { - mime: mediaType, - }, - }); + acc.push([ + ['url', link.href], + ['m', mediaType], + ]); } return acc; diff --git a/src/views/mastodon/attachments.ts b/src/views/mastodon/attachments.ts index 18fe031..8922985 100644 --- a/src/views/mastodon/attachments.ts +++ b/src/views/mastodon/attachments.ts @@ -4,16 +4,16 @@ import { UnattachedMedia } from '@/db/unattached-media.ts'; type DittoAttachment = TypeFest.SetOptional; -function renderAttachment(media: DittoAttachment) { - const { id, data, url } = media; +function renderAttachment(tags: string[][]) { + const url = tags.find(([name]) => name === 'url')?.[1]; - const m = data.find(([name]) => name === 'm')?.[1]; - const alt = data.find(([name]) => name === 'alt')?.[1]; - const cid = data.find(([name]) => name === 'cid')?.[1]; - const blurhash = data.find(([name]) => name === 'blurhash')?.[1]; + const m = tags.find(([name]) => name === 'm')?.[1]; + const alt = tags.find(([name]) => name === 'alt')?.[1]; + const cid = tags.find(([name]) => name === 'cid')?.[1]; + const blurhash = tags.find(([name]) => name === 'blurhash')?.[1]; return { - id: id ?? url, + id: url, type: getAttachmentType(m ?? ''), url, preview_url: url, diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index 8428e9a..889c23d 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -10,7 +10,7 @@ import { nostrDate } from '@/utils.ts'; import { getMediaLinks, parseNoteContent } from '@/utils/note.ts'; import { unfurlCardCached } from '@/utils/unfurl.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; -import { DittoAttachment, renderAttachment } from '@/views/mastodon/attachments.ts'; +import { renderAttachment } from '@/views/mastodon/attachments.ts'; import { renderEmojis } from '@/views/mastodon/emojis.ts'; interface RenderStatusOpts { @@ -78,16 +78,11 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< const mediaLinks = getMediaLinks(links); - const mediaTags: DittoAttachment[] = event.tags + const imeta: string[][][] = event.tags .filter(([name]) => name === 'imeta') - .map(([_, ...entries]) => { - const data = entries.map((entry) => entry.split(' ')); - const url = data.find(([name]) => name === 'url')?.[1]; - return { url, data }; - }) - .filter((media): media is DittoAttachment => !!media.url); + .map(([_, ...entries]) => entries.map((entry) => entry.split(' '))); - const media = [...mediaLinks, ...mediaTags]; + const media = [...mediaLinks, ...imeta]; return { id: event.id,