summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/api/util/utility/EmbedHandlers.ts124
1 files changed, 74 insertions, 50 deletions
diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts
index a4625b3b..a9cba383 100644
--- a/src/api/util/utility/EmbedHandlers.ts
+++ b/src/api/util/utility/EmbedHandlers.ts
@@ -23,27 +23,27 @@ export const getProxyUrl = (url: URL, width: number, height: number) => {
 	return `${endpointPublic}/external/resize/${encodeURIComponent(url.href)}?width=${width}&height=${height}`;
 };
 
-export const getMetaDescriptions = async (text: string) => {
+const getMeta = ($: cheerio.CheerioAPI, name: string): string | undefined => {
+	let elem = $(`meta[property="${name}"]`);
+	if (!elem.length) elem = $(`meta[name="${name}"]`);
+	return elem.attr("content") || elem.text();
+}
+
+export const getMetaDescriptions = (text: string) => {
 	const $ = cheerio.load(text);
 
 	return {
-		title: $('meta[property="og:title"]').attr("content"),
-		provider_name: $('meta[property="og:site_name"]').text(),
-		author: $('meta[property="article:author"]').attr("content"),
-		description:
-			$('meta[property="og:description"]').attr("content") ||
-			$('meta[property="description"]').attr("content"),
-		image: $('meta[property="og:image"]').attr("content") || $(`meta[property="twitter:image"]`).attr("content"),
-		width: parseInt(
-			$('meta[property="og:image:width"]').attr("content") ||
-			"",
-		) || undefined,
-		height: parseInt(
-			$('meta[property="og:image:height"]').attr("content") ||
-			"",
-		) || undefined,
-		url: $('meta[property="og:url"]').attr("content"),
-		youtube_embed: $(`meta[property="og:video:secure_url"]`).attr("content"),
+		title: getMeta($, "og:title") || $("title").first().text(),
+		provider_name: getMeta($, "og:site_name"),
+		author: getMeta($, "article:author"),
+		description: getMeta($, "og:description") || getMeta($, "description"),
+		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,
+		url: getMeta($, "og:url"),
+		youtube_embed: getMeta($, "og:video:secure_url"),
 	};
 };
 
@@ -60,24 +60,43 @@ const doFetch = async (url: URL) => {
 };
 
 const genericImageHandler = async (url: URL): Promise<Embed | null> => {
-	const response = await doFetch(url);
-	if (!response) return null;
-	const metas = await getMetaDescriptions(await response.text());
+	const type = await fetch(url, {
+		...DEFAULT_FETCH_OPTIONS,
+		method: "HEAD",
+	});
+
+	let width, height, image;
 
-	if (!metas.width || !metas.height) {
+	if (type.headers.get("content-type")?.indexOf("image") !== -1) {
 		const result = await probe(url.href);
-		metas.width = result.width;
-		metas.height = result.height;
+		width = result.width;
+		height = result.height;
+		image = url.href;
+	}
+	else if (type.headers.get("content-type")?.indexOf("video") !== -1) {
+		// TODO
+		return null;
 	}
+	else {
+		// have to download the page, unfortunately
+		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;
+	}
+
+	if (!width || !height || !image) return null;
 
 	return {
 		url: url.href,
 		type: EmbedType.image,
 		thumbnail: {
-			width: metas.width,
-			height: metas.height,
+			width: width,
+			height: height,
 			url: url.href,
-			proxy_url: getProxyUrl(new URL(metas.image || url.href), metas.width, metas.height),
+			proxy_url: getProxyUrl(new URL(image), width, height),
 		}
 	};
 };
@@ -85,27 +104,32 @@ const genericImageHandler = async (url: URL): Promise<Embed | null> => {
 export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>; } = {
 	// the url does not have a special handler
 	"default": async (url: URL) => {
+		const type = await fetch(url, {
+			...DEFAULT_FETCH_OPTIONS,
+			method: "HEAD",
+		});
+		if (type.headers.get("content-type")?.indexOf("image") !== -1)
+			return await genericImageHandler(url);
+
 		const response = await doFetch(url);
 		if (!response) return null;
 
-		if (response.headers.get("content-type")?.indexOf("image") !== -1) {
-			// this is an image
+		const metas = getMetaDescriptions(await response.text());
 
-			const size = imageSize(await response.buffer());
+		// TODO: handle video
 
-			return {
-				url: url.href,
-				type: EmbedType.image,
-				image: {
-					width: size.width,
-					height: size.height,
-					url: url.href,
-					proxy_url: getProxyUrl(url, size.width!, size.height!),
-				}
-			};
+		if (!metas.image) metas.image = metas.image_fallback;
+
+		if (metas.image && (!metas.width || !metas.height)) {
+			const result = await probe(metas.image);
+			metas.width = result.width;
+			metas.height = result.height;
+		}
+
+		if (!metas.image && (!metas.title || !metas.description)) {
+			return null;
 		}
 
-		const metas = await getMetaDescriptions(await response.text());
 		return {
 			url: url.href,
 			type: EmbedType.link,
@@ -114,7 +138,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 				width: metas.width,
 				height: metas.height,
 				url: metas.image,
-				proxy_url: getProxyUrl(new URL(metas.image!), metas.width!, metas.height!),
+				proxy_url: metas.image ? getProxyUrl(new URL(metas.image), metas.width!, metas.height!) : undefined,
 			},
 			description: metas.description,
 		};
@@ -132,7 +156,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 	"open.spotify.com": async (url: URL) => {
 		const response = await doFetch(url);
 		if (!response) return null;
-		const metas = await getMetaDescriptions(await response.text());
+		const metas = getMetaDescriptions(await response.text());
 
 		return {
 			url: url.href,
@@ -142,7 +166,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 			thumbnail: {
 				width: 640,
 				height: 640,
-				proxy_url: getProxyUrl(new URL(metas.image!), 640, 640),
+				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), 640, 640) : undefined,
 				url: metas.image,
 			},
 			provider: {
@@ -156,7 +180,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 	"www.pixiv.net": async (url: URL) => {
 		const response = await doFetch(url);
 		if (!response) return null;
-		const metas = await getMetaDescriptions(await response.text());
+		const metas = getMetaDescriptions(await response.text());
 
 		// TODO: doesn't show images. think it's a bug in the cdn
 		return {
@@ -168,7 +192,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 				width: metas.width,
 				height: metas.height,
 				url: url.href,
-				proxy_url: getProxyUrl(new URL(metas.image!), metas.width!, metas.height!),
+				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), metas.width!, metas.height!) : undefined,
 			},
 			provider: {
 				url: "https://pixiv.net",
@@ -180,7 +204,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 	"store.steampowered.com": async (url: URL) => {
 		const response = await doFetch(url);
 		if (!response) return null;
-		const metas = await getMetaDescriptions(await response.text());
+		const metas = getMetaDescriptions(await response.text());
 
 		return {
 			url: url.href,
@@ -192,7 +216,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 				width: 460,
 				height: 215,
 				url: metas.image,
-				proxy_url: getProxyUrl(new URL(metas.image!), 460, 215),
+				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), 460, 215) : undefined,
 			},
 			provider: {
 				url: "https://store.steampowered.com",
@@ -219,7 +243,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 	"www.youtube.com": async (url: URL): Promise<Embed | null> => {
 		const response = await doFetch(url);
 		if (!response) return null;
-		const metas = await getMetaDescriptions(await response.text());
+		const metas = getMetaDescriptions(await response.text());
 
 		return {
 			video: {
@@ -235,7 +259,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>
 				width: metas.width,
 				height: metas.height,
 				url: metas.image,
-				proxy_url: getProxyUrl(new URL(metas.image!), metas.width!, metas.height!),
+				proxy_url: metas.image ? getProxyUrl(new URL(metas.image!), metas.width!, metas.height!) : undefined,
 			},
 			provider: {
 				url: "https://www.youtube.com",