diff --git a/src/note.ts b/src/note.ts index 7dc39f5a..71dc0174 100644 --- a/src/note.ts +++ b/src/note.ts @@ -1,10 +1,10 @@ -import { typeByExtension } from '@std/media-types'; import 'linkify-plugin-hashtag'; import linkifyStr from 'linkify-string'; import linkify from 'linkifyjs'; 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); @@ -60,16 +60,14 @@ function parseNoteContent(content: string): ParsedNoteContent { function getMediaLinks(links: Link[]): DittoAttachment[] { return links.reduce((acc, link) => { - const mimeType = getUrlMimeType(link.href); - if (!mimeType) return acc; + const mediaType = getUrlMediaType(link.href); + if (!mediaType) return acc; - const [baseType, _subType] = mimeType.split('/'); - - if (['audio', 'image', 'video'].includes(baseType)) { + if (isPermittedMediaType(mediaType, ['audio', 'image', 'video'])) { acc.push({ url: link.href, data: { - mime: mimeType, + mime: mediaType, }, }); } @@ -79,7 +77,7 @@ function getMediaLinks(links: Link[]): DittoAttachment[] { } function isNonMediaLink({ href }: Link): boolean { - return /^https?:\/\//.test(href) && !getUrlMimeType(href); + return /^https?:\/\//.test(href) && !getUrlMediaType(href); } /** Ensures the Link is a URL so it can be parsed. */ @@ -87,17 +85,6 @@ function isLinkURL(link: Link): boolean { return link.type === 'url'; } -/** Get the extension from the URL, then get its type. */ -function getUrlMimeType(url: string): string | undefined { - try { - const { pathname } = new URL(url); - const ext = pathname.split('.').pop() ?? ''; - return typeByExtension(ext); - } catch { - return undefined; - } -} - /** Get pubkey from decoded bech32 entity, or undefined if not applicable. */ function getDecodedPubkey(decoded: nip19.DecodeResult): string | undefined { switch (decoded.type) { diff --git a/src/utils/media.test.ts b/src/utils/media.test.ts new file mode 100644 index 00000000..e88e97da --- /dev/null +++ b/src/utils/media.test.ts @@ -0,0 +1,17 @@ +import { assertEquals } from '@std/assert'; + +import { getUrlMediaType, isPermittedMediaType } from '@/utils/media.ts'; + +Deno.test('getUrlMediaType', () => { + assertEquals(getUrlMediaType('https://example.com/image.png'), 'image/png'); + assertEquals(getUrlMediaType('https://example.com/index.html'), 'text/html'); + assertEquals(getUrlMediaType('https://example.com/yolo'), undefined); + assertEquals(getUrlMediaType('https://example.com/'), undefined); +}); + +Deno.test('isPermittedMediaType', () => { + assertEquals(isPermittedMediaType('image/png', ['image', 'video']), true); + assertEquals(isPermittedMediaType('video/webm', ['image', 'video']), true); + assertEquals(isPermittedMediaType('audio/ogg', ['image', 'video']), false); + assertEquals(isPermittedMediaType('application/json', ['image', 'video']), false); +}); diff --git a/src/utils/media.ts b/src/utils/media.ts new file mode 100644 index 00000000..9c0ea9e3 --- /dev/null +++ b/src/utils/media.ts @@ -0,0 +1,24 @@ +import { typeByExtension } from '@std/media-types'; + +/** Get media type of the filename in the URL by its extension, if any. */ +export function getUrlMediaType(url: string): string | undefined { + try { + const { pathname } = new URL(url); + const ext = pathname.split('.').pop() ?? ''; + return typeByExtension(ext); + } catch { + return undefined; + } +} + +/** + * Check if the base type matches any of the permitted types. + * + * ```ts + * isPermittedMediaType('image/png', ['image', 'video']); // true + * ``` + */ +export function isPermittedMediaType(mediaType: string, permitted: string[]): boolean { + const [baseType, _subType] = mediaType.split('/'); + return permitted.includes(baseType); +}