From 6ff90d63bbdaa139e0cfba99971653169909ad9b Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 16:23:25 +0530 Subject: [PATCH 1/7] implement PUT /api/v1/media/:id Adds support for setting image descriptions for accessibility reasons --- src/app.ts | 6 +++++- src/controllers/api/media.ts | 28 +++++++++++++++++++++++++++- src/db/unattached-media.ts | 13 +++++++++++++ src/views/mastodon/attachments.ts | 2 +- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/app.ts b/src/app.ts index 9828724d..a0e752ba 100644 --- a/src/app.ts +++ b/src/app.ts @@ -53,7 +53,7 @@ import { instanceV2Controller, } from '@/controllers/api/instance.ts'; import { markersController, updateMarkersController } from '@/controllers/api/markers.ts'; -import { mediaController } from '@/controllers/api/media.ts'; +import { mediaController, updateMediaDescriptionController } from '@/controllers/api/media.ts'; import { mutesController } from '@/controllers/api/mutes.ts'; import { notificationsController } from '@/controllers/api/notifications.ts'; import { createTokenController, oauthAuthorizeController, oauthController } from '@/controllers/api/oauth.ts'; @@ -226,6 +226,10 @@ app.delete('/api/v1/statuses/:id{[0-9a-f]{64}}', requireSigner, deleteStatusCont app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/quotes', quotesController); app.post('/api/v1/media', mediaController); +app.put( + '/api/v1/media/:id{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}', + updateMediaDescriptionController, +); app.post('/api/v2/media', mediaController); app.get('/api/v1/timelines/home', requireSigner, homeTimelineController); diff --git a/src/controllers/api/media.ts b/src/controllers/api/media.ts index 71b3e782..f0ed3069 100644 --- a/src/controllers/api/media.ts +++ b/src/controllers/api/media.ts @@ -5,6 +5,7 @@ import { fileSchema } from '@/schema.ts'; import { parseBody } from '@/utils/api.ts'; import { renderAttachment } from '@/views/mastodon/attachments.ts'; import { uploadFile } from '@/utils/upload.ts'; +import { setMediaDescription } from '@/db/unattached-media.ts'; const mediaBodySchema = z.object({ file: fileSchema, @@ -13,6 +14,10 @@ const mediaBodySchema = z.object({ focus: z.string().optional(), }); +const mediaDescriptionUpdateSchema = z.object({ + description: z.string(), +}); + const mediaController: AppController = async (c) => { const pubkey = await c.get('signer')?.getPublicKey()!; const result = mediaBodySchema.safeParse(await parseBody(c.req.raw)); @@ -32,4 +37,25 @@ const mediaController: AppController = async (c) => { } }; -export { mediaController }; +const updateMediaDescriptionController: AppController = async (c) => { + console.log('in media description update controller'); + const result = mediaDescriptionUpdateSchema.safeParse(await parseBody(c.req.raw)); + console.log(result); + if (!result.success) { + return c.json({ error: 'Bad request.', schema: result.error }, 422); + } + try { + const { description } = result.data; + console.log(description); + if (!await setMediaDescription(c.req.param('id'), description)) { + return c.json({ error: 'File with specified ID not found.' }, 404); + } + } catch (e) { + console.error(e); + return c.json({ error: 'Failed to set media description.' }, 500); + } + + return c.json({ message: 'ok' }, 200); +}; + +export { mediaController, updateMediaDescriptionController }; diff --git a/src/db/unattached-media.ts b/src/db/unattached-media.ts index fac7a1d9..260a9d5a 100644 --- a/src/db/unattached-media.ts +++ b/src/db/unattached-media.ts @@ -56,6 +56,18 @@ async function getUnattachedMediaByIds(kysely: Kysely, ids: string[ })); } +async function setMediaDescription(id: string, desc = '') { + const { kysely } = await DittoDB.getInstance(); + const existing = await selectUnattachedMediaQuery(kysely).where('id', '=', id).executeTakeFirst(); + if (!existing) return false; + const parsed = (await JSON.parse(existing.data) as string[][]).filter((itm) => itm[0] !== 'alt'); + parsed.push(['alt', desc]); + await kysely.updateTable('unattached_media') + .set({ data: JSON.stringify(parsed) }) + .execute(); + return true; +} + /** Delete rows as an event with media is being created. */ async function deleteAttachedMedia(pubkey: string, urls: string[]): Promise { if (!urls.length) return; @@ -71,5 +83,6 @@ export { deleteUnattachedMediaByUrl, getUnattachedMediaByIds, insertUnattachedMedia, + setMediaDescription, type UnattachedMedia, }; diff --git a/src/views/mastodon/attachments.ts b/src/views/mastodon/attachments.ts index 9320f604..293b1499 100644 --- a/src/views/mastodon/attachments.ts +++ b/src/views/mastodon/attachments.ts @@ -10,7 +10,7 @@ function renderAttachment( const url = tags.find(([name]) => name === 'url')?.[1]; const m = tags.find(([name]) => name === 'm')?.[1] ?? getUrlMediaType(url!); - const alt = tags.find(([name]) => name === 'alt')?.[1]; + const alt = tags.find(([name]) => name === 'alt')?.slice(1).join(' ') || 'picture'; const cid = tags.find(([name]) => name === 'cid')?.[1]; const dim = tags.find(([name]) => name === 'dim')?.[1]; const blurhash = tags.find(([name]) => name === 'blurhash')?.[1]; From cf4dc8627b8fb50c724f680bd47174fd76fe8065 Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 16:25:36 +0530 Subject: [PATCH 2/7] remove unnecessary coalescing --- src/views/mastodon/attachments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/mastodon/attachments.ts b/src/views/mastodon/attachments.ts index 293b1499..9307ea82 100644 --- a/src/views/mastodon/attachments.ts +++ b/src/views/mastodon/attachments.ts @@ -10,7 +10,7 @@ function renderAttachment( const url = tags.find(([name]) => name === 'url')?.[1]; const m = tags.find(([name]) => name === 'm')?.[1] ?? getUrlMediaType(url!); - const alt = tags.find(([name]) => name === 'alt')?.slice(1).join(' ') || 'picture'; + const alt = tags.find(([name]) => name === 'alt')?.slice(1).join(' '); const cid = tags.find(([name]) => name === 'cid')?.[1]; const dim = tags.find(([name]) => name === 'dim')?.[1]; const blurhash = tags.find(([name]) => name === 'blurhash')?.[1]; From 1f04a7fcdfb914c8320344a6653375177a58763f Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 17:20:25 +0530 Subject: [PATCH 3/7] remove debug console.logs --- src/controllers/api/media.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/controllers/api/media.ts b/src/controllers/api/media.ts index f0ed3069..5b0cd218 100644 --- a/src/controllers/api/media.ts +++ b/src/controllers/api/media.ts @@ -38,15 +38,12 @@ const mediaController: AppController = async (c) => { }; const updateMediaDescriptionController: AppController = async (c) => { - console.log('in media description update controller'); const result = mediaDescriptionUpdateSchema.safeParse(await parseBody(c.req.raw)); - console.log(result); if (!result.success) { return c.json({ error: 'Bad request.', schema: result.error }, 422); } try { const { description } = result.data; - console.log(description); if (!await setMediaDescription(c.req.param('id'), description)) { return c.json({ error: 'File with specified ID not found.' }, 404); } From f8fae52d5e083a4676989ef1604f57d38c88adb3 Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 19:13:31 +0530 Subject: [PATCH 4/7] fix bug in alt text tagging --- src/views/mastodon/attachments.ts | 2 +- src/views/mastodon/statuses.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/views/mastodon/attachments.ts b/src/views/mastodon/attachments.ts index 9307ea82..9320f604 100644 --- a/src/views/mastodon/attachments.ts +++ b/src/views/mastodon/attachments.ts @@ -10,7 +10,7 @@ function renderAttachment( const url = tags.find(([name]) => name === 'url')?.[1]; const m = tags.find(([name]) => name === 'm')?.[1] ?? getUrlMediaType(url!); - const alt = tags.find(([name]) => name === 'alt')?.slice(1).join(' '); + const alt = tags.find(([name]) => name === 'alt')?.[1]; const cid = tags.find(([name]) => name === 'cid')?.[1]; const dim = tags.find(([name]) => name === 'dim')?.[1]; const blurhash = tags.find(([name]) => name === 'blurhash')?.[1]; diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index d1c02f1e..bc42765b 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -84,7 +84,12 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< const imeta: string[][][] = event.tags .filter(([name]) => name === 'imeta') - .map(([_, ...entries]) => entries.map((entry) => entry.split(' '))); + .map(([_, ...entries]) => + entries.map((entry) => { + const split = entry.split(' '); + return [split[0], split.splice(1).join(' ')]; + }) + ); const media = imeta.length ? imeta : getMediaLinks(links); From 7e711aa8a8858995247f4e01dcd65ba49db0aa5d Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 19:43:28 +0530 Subject: [PATCH 5/7] updateMediaDescriptionController --> updateMediaController --- src/app.ts | 4 ++-- src/controllers/api/media.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app.ts b/src/app.ts index a0e752ba..43ad4b00 100644 --- a/src/app.ts +++ b/src/app.ts @@ -53,7 +53,7 @@ import { instanceV2Controller, } from '@/controllers/api/instance.ts'; import { markersController, updateMarkersController } from '@/controllers/api/markers.ts'; -import { mediaController, updateMediaDescriptionController } from '@/controllers/api/media.ts'; +import { mediaController, updateMediaController } from '@/controllers/api/media.ts'; import { mutesController } from '@/controllers/api/mutes.ts'; import { notificationsController } from '@/controllers/api/notifications.ts'; import { createTokenController, oauthAuthorizeController, oauthController } from '@/controllers/api/oauth.ts'; @@ -228,7 +228,7 @@ app.get('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/quotes', quotesController); app.post('/api/v1/media', mediaController); app.put( '/api/v1/media/:id{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}', - updateMediaDescriptionController, + updateMediaController, ); app.post('/api/v2/media', mediaController); diff --git a/src/controllers/api/media.ts b/src/controllers/api/media.ts index 5b0cd218..e13e9d21 100644 --- a/src/controllers/api/media.ts +++ b/src/controllers/api/media.ts @@ -37,7 +37,7 @@ const mediaController: AppController = async (c) => { } }; -const updateMediaDescriptionController: AppController = async (c) => { +const updateMediaController: AppController = async (c) => { const result = mediaDescriptionUpdateSchema.safeParse(await parseBody(c.req.raw)); if (!result.success) { return c.json({ error: 'Bad request.', schema: result.error }, 422); @@ -55,4 +55,4 @@ const updateMediaDescriptionController: AppController = async (c) => { return c.json({ message: 'ok' }, 200); }; -export { mediaController, updateMediaDescriptionController }; +export { mediaController, updateMediaController }; From 1a38061b34a070bcd6b8edaa37ffe2d2eea04f69 Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 19:48:04 +0530 Subject: [PATCH 6/7] mediaDescriptionUpdateSchema --> mediaUpdateSchema --- src/controllers/api/media.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/api/media.ts b/src/controllers/api/media.ts index e13e9d21..3fa3ee68 100644 --- a/src/controllers/api/media.ts +++ b/src/controllers/api/media.ts @@ -14,7 +14,7 @@ const mediaBodySchema = z.object({ focus: z.string().optional(), }); -const mediaDescriptionUpdateSchema = z.object({ +const mediaUpdateSchema = z.object({ description: z.string(), }); @@ -38,7 +38,7 @@ const mediaController: AppController = async (c) => { }; const updateMediaController: AppController = async (c) => { - const result = mediaDescriptionUpdateSchema.safeParse(await parseBody(c.req.raw)); + const result = mediaUpdateSchema.safeParse(await parseBody(c.req.raw)); if (!result.success) { return c.json({ error: 'Bad request.', schema: result.error }, 422); } From e7f5535bd2eda62d41d80f1ce57bbd3b11907d42 Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 1 Sep 2024 19:15:22 +0530 Subject: [PATCH 7/7] handle event insertion errors with warning --- scripts/nostr-pull.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/nostr-pull.ts b/scripts/nostr-pull.ts index 68766c8f..0556b64a 100644 --- a/scripts/nostr-pull.ts +++ b/scripts/nostr-pull.ts @@ -29,6 +29,18 @@ const importUsers = async ( const notes = new Set(); const { profilesOnly = false } = opts || {}; + const put = async (event: NostrEvent) => { + try { + await doEvent(event); + } catch (error) { + if (error.message.includes('violates unique constraint')) { + console.warn(`Skipping existing event ${event.id}...`); + } else { + console.error(error); + } + } + }; + await Promise.all(relays.map(async (relay) => { if (!relay.startsWith('wss://')) console.error(`Invalid relay url ${relay}`); const conn = new NRelay1(relay); @@ -49,7 +61,7 @@ const importUsers = async ( if (kind === 1 && !notes.has(event.id)) { // add the event to eventsDB only if it has not been found already. notes.add(event.id); - await doEvent(event); + await put(event); return; } @@ -64,7 +76,7 @@ const importUsers = async ( for (const user in profiles) { const profile = profiles[user]; for (const kind in profile) { - await doEvent(profile[kind]); + await put(profile[kind]); } let name = user;