From 6ff90d63bbdaa139e0cfba99971653169909ad9b Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Sun, 25 Aug 2024 16:23:25 +0530 Subject: [PATCH] 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];