summary refs log tree commit diff
path: root/src/api/util/utility
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-09-03 14:17:11 +1000
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-09-03 14:17:11 +1000
commite64c34adea6ad918511f26c8c22ff76500d51ba3 (patch)
tree4eaa1e52e8354063f890b296bfb42128fd986589 /src/api/util/utility
parentFix bug in embed handler where getMeta would not return undefined (diff)
downloadserver-e64c34adea6ad918511f26c8c22ff76500d51ba3.tar.xz
Rewrite thumbnail/image generation for embeds
Diffstat (limited to 'src/api/util/utility')
-rw-r--r--src/api/util/utility/EmbedHandlers.ts148
1 files changed, 65 insertions, 83 deletions
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 <https://www.gnu.org/licenses/>.
 */
 
-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<EmbedImage> | 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<Embed | null> => {
 		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<Embed | null> => {
 		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<EmbedImage> | 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,