Move unfurl code to a separate module
This commit is contained in:
parent
db17f84937
commit
1662f616ef
|
@ -0,0 +1,73 @@
|
||||||
|
import { TTLCache, unfurl } from '@/deps.ts';
|
||||||
|
import { Time } from '@/utils/time.ts';
|
||||||
|
|
||||||
|
interface PreviewCard {
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
type: 'link' | 'photo' | 'video' | 'rich';
|
||||||
|
author_name: string;
|
||||||
|
author_url: string;
|
||||||
|
provider_name: string;
|
||||||
|
provider_url: string;
|
||||||
|
html: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
image: string | null;
|
||||||
|
embed_url: string;
|
||||||
|
blurhash: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unfurlCard(url: string, signal: AbortSignal): Promise<PreviewCard | null> {
|
||||||
|
console.log(`Unfurling ${url}...`);
|
||||||
|
try {
|
||||||
|
const result = await unfurl(url, {
|
||||||
|
fetch: (url) => fetch(url, { signal }),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: result.oEmbed?.type || 'link',
|
||||||
|
url: result.canonical_url || url,
|
||||||
|
title: result.oEmbed?.title || result.title || '',
|
||||||
|
description: result.open_graph.description || result.description || '',
|
||||||
|
author_name: result.oEmbed?.author_name || '',
|
||||||
|
author_url: result.oEmbed?.author_url || '',
|
||||||
|
provider_name: result.oEmbed?.provider_name || '',
|
||||||
|
provider_url: result.oEmbed?.provider_url || '',
|
||||||
|
// @ts-expect-error `html` does in fact exist on oEmbed.
|
||||||
|
html: sanitizeHtml(result.oEmbed?.html || '', {
|
||||||
|
allowedTags: ['iframe'],
|
||||||
|
allowedAttributes: {
|
||||||
|
iframe: ['width', 'height', 'src', 'frameborder', 'allowfullscreen'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
width: result.oEmbed?.width || 0,
|
||||||
|
height: result.oEmbed?.height || 0,
|
||||||
|
image: result.oEmbed?.thumbnails?.[0].url || result.open_graph.images?.[0].url || null,
|
||||||
|
embed_url: '',
|
||||||
|
blurhash: null,
|
||||||
|
};
|
||||||
|
} catch (_e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TTL cache for preview cards. */
|
||||||
|
const previewCardCache = new TTLCache<string, Promise<PreviewCard | null>>({
|
||||||
|
ttl: Time.hours(12),
|
||||||
|
max: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Unfurl card from cache if available, otherwise fetch it. */
|
||||||
|
function unfurlCardCached(url: string, timeout = Time.seconds(1)): Promise<PreviewCard | null> {
|
||||||
|
const cached = previewCardCache.get(url);
|
||||||
|
if (cached !== undefined) {
|
||||||
|
return cached;
|
||||||
|
} else {
|
||||||
|
const card = unfurlCard(url, AbortSignal.timeout(timeout));
|
||||||
|
previewCardCache.set(url, card);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { type PreviewCard, unfurlCardCached };
|
|
@ -2,14 +2,15 @@ import { isCWTag } from 'https://gitlab.com/soapbox-pub/mostr/-/raw/c67064aee5ad
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import * as eventsDB from '@/db/events.ts';
|
import * as eventsDB from '@/db/events.ts';
|
||||||
import { type Event, findReplyTag, lodash, nip19, sanitizeHtml, TTLCache, unfurl, type UnsignedEvent } from '@/deps.ts';
|
import { findUser } from '@/db/users.ts';
|
||||||
|
import { type Event, findReplyTag, lodash, nip19, type UnsignedEvent } from '@/deps.ts';
|
||||||
import { getMediaLinks, parseNoteContent } from '@/note.ts';
|
import { getMediaLinks, parseNoteContent } from '@/note.ts';
|
||||||
import { getAuthor, getFollowedPubkeys, getFollows } from '@/queries.ts';
|
import { getAuthor, getFollowedPubkeys, getFollows } from '@/queries.ts';
|
||||||
import { filteredArray } from '@/schema.ts';
|
import { filteredArray } from '@/schema.ts';
|
||||||
import { emojiTagSchema, jsonMediaDataSchema, jsonMetaContentSchema } from '@/schemas/nostr.ts';
|
import { emojiTagSchema, jsonMediaDataSchema, jsonMetaContentSchema } from '@/schemas/nostr.ts';
|
||||||
import { isFollowing, type Nip05, nostrDate, nostrNow, parseNip05, Time } from '@/utils.ts';
|
import { isFollowing, type Nip05, nostrDate, nostrNow, parseNip05 } from '@/utils.ts';
|
||||||
import { verifyNip05Cached } from '@/utils/nip05.ts';
|
import { verifyNip05Cached } from '@/utils/nip05.ts';
|
||||||
import { findUser } from '@/db/users.ts';
|
import { unfurlCardCached } from '@/utils/unfurl.ts';
|
||||||
import { DittoAttachment, renderAttachment } from '@/views/attachment.ts';
|
import { DittoAttachment, renderAttachment } from '@/views/attachment.ts';
|
||||||
|
|
||||||
const defaultAvatar = () => Conf.local('/images/avi.png');
|
const defaultAvatar = () => Conf.local('/images/avi.png');
|
||||||
|
@ -211,70 +212,6 @@ function buildInlineRecipients(mentions: Mention[]): string {
|
||||||
return `<span class="recipients-inline">${elements.join(' ')} </span>`;
|
return `<span class="recipients-inline">${elements.join(' ')} </span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PreviewCard {
|
|
||||||
url: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
type: 'link' | 'photo' | 'video' | 'rich';
|
|
||||||
author_name: string;
|
|
||||||
author_url: string;
|
|
||||||
provider_name: string;
|
|
||||||
provider_url: string;
|
|
||||||
html: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
image: string | null;
|
|
||||||
embed_url: string;
|
|
||||||
blurhash: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unfurlCard(url: string): Promise<PreviewCard | null> {
|
|
||||||
console.log(`Unfurling ${url}...`);
|
|
||||||
try {
|
|
||||||
const result = await unfurl(url, {
|
|
||||||
fetch: (url) => fetch(url, { signal: AbortSignal.timeout(Time.seconds(1)) }),
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: result.oEmbed?.type || 'link',
|
|
||||||
url: result.canonical_url || url,
|
|
||||||
title: result.oEmbed?.title || result.title || '',
|
|
||||||
description: result.open_graph.description || result.description || '',
|
|
||||||
author_name: result.oEmbed?.author_name || '',
|
|
||||||
author_url: result.oEmbed?.author_url || '',
|
|
||||||
provider_name: result.oEmbed?.provider_name || '',
|
|
||||||
provider_url: result.oEmbed?.provider_url || '',
|
|
||||||
// @ts-expect-error `html` does in fact exist on oEmbed.
|
|
||||||
html: sanitizeHtml(result.oEmbed?.html || '', {
|
|
||||||
allowedTags: ['iframe'],
|
|
||||||
allowedAttributes: {
|
|
||||||
iframe: ['width', 'height', 'src', 'frameborder', 'allowfullscreen'],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
width: result.oEmbed?.width || 0,
|
|
||||||
height: result.oEmbed?.height || 0,
|
|
||||||
image: result.oEmbed?.thumbnails?.[0].url || result.open_graph.images?.[0].url || null,
|
|
||||||
embed_url: '',
|
|
||||||
blurhash: null,
|
|
||||||
};
|
|
||||||
} catch (_e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const previewCardCache = new TTLCache<string, Promise<PreviewCard | null>>({ ttl: Time.hours(12), max: 500 });
|
|
||||||
|
|
||||||
/** Unfurl card from cache if available, otherwise fetch it. */
|
|
||||||
function unfurlCardCached(url: string): Promise<PreviewCard | null> {
|
|
||||||
const cached = previewCardCache.get(url);
|
|
||||||
if (cached !== undefined) return cached;
|
|
||||||
|
|
||||||
const card = unfurlCard(url);
|
|
||||||
previewCardCache.set(url, card);
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toEmojis(event: UnsignedEvent) {
|
function toEmojis(event: UnsignedEvent) {
|
||||||
const emojiTags = event.tags.filter((tag) => tag[0] === 'emoji');
|
const emojiTags = event.tags.filter((tag) => tag[0] === 'emoji');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue