From e64c34adea6ad918511f26c8c22ff76500d51ba3 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Sun, 3 Sep 2023 14:17:11 +1000 Subject: Rewrite thumbnail/image generation for embeds --- src/api/util/utility/EmbedHandlers.ts | 148 +++++++++++++++------------------- 1 file changed, 65 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts index af01a32f..b1e6b866 100644 --- a/src/api/util/utility/EmbedHandlers.ts +++ b/src/api/util/utility/EmbedHandlers.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { Config, Embed, EmbedType } from "@spacebar/util"; +import { Config, Embed, EmbedImage, EmbedType } from "@spacebar/util"; import * as cheerio from "cheerio"; import crypto from "crypto"; import fetch, { RequestInit } from "node-fetch"; @@ -35,6 +35,20 @@ export const DEFAULT_FETCH_OPTIONS: RequestInit = { method: "GET", }; +const makeEmbedImage = ( + url: string | undefined, + width: number | undefined, + height: number | undefined, +): Required | undefined => { + if (!url || !width || !height) return undefined; + return { + url, + width, + height, + proxy_url: getProxyUrl(new URL(url), width, height), + }; +}; + let hasWarnedAboutImagor = false; export const getProxyUrl = ( @@ -82,6 +96,15 @@ const getMeta = ($: cheerio.CheerioAPI, name: string): string | undefined => { return ret.trim().length == 0 ? undefined : ret; }; +const tryParseInt = (str: string | undefined) => { + if (!str) return undefined; + try { + return parseInt(str); + } catch (e) { + return undefined; + } +}; + export const getMetaDescriptions = (text: string) => { const $ = cheerio.load(text); @@ -94,8 +117,8 @@ export const getMetaDescriptions = (text: string) => { image: getMeta($, "og:image") || getMeta($, "twitter:image"), image_fallback: $(`image`).attr("src"), video_fallback: $(`video`).attr("src"), - width: parseInt(getMeta($, "og:image:width") || "0"), - height: parseInt(getMeta($, "og:image:height") || "0"), + width: tryParseInt(getMeta($, "og:image:width")), + height: tryParseInt(getMeta($, "og:image:height")), url: getMeta($, "og:url"), youtube_embed: getMeta($, "og:video:secure_url"), @@ -120,13 +143,11 @@ const genericImageHandler = async (url: URL): Promise => { method: "HEAD", }); - let width: number, height: number, image: string | undefined; + let image; if (type.headers.get("content-type")?.indexOf("image") !== -1) { const result = await probe(url.href); - width = result.width; - height = result.height; - image = url.href; + image = makeEmbedImage(url.href, result.width, result.height); } else if (type.headers.get("content-type")?.indexOf("video") !== -1) { // TODO return null; @@ -135,22 +156,19 @@ const genericImageHandler = async (url: URL): Promise => { const response = await doFetch(url); if (!response) return null; const metas = getMetaDescriptions(await response.text()); - width = metas.width; - height = metas.height; - image = metas.image || metas.image_fallback; + image = makeEmbedImage( + metas.image || metas.image_fallback, + metas.width, + metas.height, + ); } - if (!width || !height || !image) return null; + if (!image) return null; return { url: url.href, type: EmbedType.image, - thumbnail: { - width: width, - height: height, - url: url.href, - proxy_url: getProxyUrl(new URL(image), width, height), - }, + thumbnail: image, }; }; @@ -176,13 +194,15 @@ export const EmbedHandlers: { if (!metas.image) metas.image = metas.image_fallback; + let image: Required | undefined; + if (metas.image && (!metas.width || !metas.height)) { const result = await probe(metas.image); - metas.width = result.width; - metas.height = result.height; + image = makeEmbedImage(metas.image, result.width, result.height); } - if (!metas.image && (!metas.title || !metas.description)) { + if (!image && (!metas.title || !metas.description)) { + // we don't have any content to display return null; } @@ -191,24 +211,11 @@ export const EmbedHandlers: { if (metas.type == "object") embedType = EmbedType.article; // github if (metas.type == "rich") embedType = EmbedType.rich; - if (metas.width && metas.width < 400) embedType = EmbedType.link; - return { url: url.href, type: embedType, title: metas.title, - thumbnail: { - width: metas.width, - height: metas.height, - url: metas.image, - proxy_url: metas.image - ? getProxyUrl( - new URL(metas.image), - metas.width, - metas.height, - ) - : undefined, - }, + thumbnail: image, description: metas.description, }; }, @@ -340,14 +347,7 @@ export const EmbedHandlers: { type: EmbedType.link, title: metas.title, description: metas.description, - thumbnail: { - width: 640, - height: 640, - proxy_url: metas.image - ? getProxyUrl(new URL(metas.image), 640, 640) - : undefined, - url: metas.image, - }, + thumbnail: makeEmbedImage(metas.image, 640, 640), provider: { url: "https://spotify.com", name: "Spotify", @@ -369,18 +369,11 @@ export const EmbedHandlers: { type: EmbedType.image, title: metas.title, description: metas.description, - image: { - width: metas.width, - height: metas.height, - url: url.href, - proxy_url: metas.image - ? getProxyUrl( - new URL(metas.image), - metas.width, - metas.height, - ) - : undefined, - }, + image: makeEmbedImage( + metas.image || metas.image_fallback, + metas.width, + metas.height, + ), provider: { url: "https://pixiv.net", name: "Pixiv", @@ -437,37 +430,31 @@ export const EmbedHandlers: { const metas = getMetaDescriptions(await response.text()); return { - video: { - // TODO: does this adjust with aspect ratio? - width: metas.width, - height: metas.height, - url: metas.youtube_embed, - }, + video: makeEmbedImage( + metas.youtube_embed, + metas.width, + metas.height, + ), url: url.href, - type: EmbedType.video, + type: metas.youtube_embed ? EmbedType.video : EmbedType.link, title: metas.title, - thumbnail: { - width: metas.width, - height: metas.height, - url: metas.image, - proxy_url: metas.image - ? getProxyUrl( - new URL(metas.image), - metas.width, - metas.height, - ) - : undefined, - }, + thumbnail: makeEmbedImage( + metas.image || metas.image_fallback, + metas.width, + metas.height, + ), provider: { url: "https://www.youtube.com", name: "YouTube", }, description: metas.description, color: 16711680, - author: { - name: metas.author, - // TODO: author channel url - }, + author: metas.author + ? { + name: metas.author, + // TODO: author channel url + } + : undefined, }; }, @@ -487,12 +474,7 @@ export const EmbedHandlers: { url: url.href, type: EmbedType.rich, title: `xkcd: ${metas.title}`, - image: { - width, - height, - url: metas.image, - proxy_url: getProxyUrl(new URL(metas.image), width, height), - }, + image: makeEmbedImage(metas.image, width, height), footer: hoverText ? { text: hoverText, -- cgit 1.4.1