1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-22 02:12:40 +01:00

Rewrite thumbnail/image generation for embeds

This commit is contained in:
Madeline 2023-09-03 14:17:11 +10:00
parent fb7409947c
commit e64c34adea
No known key found for this signature in database
GPG Key ID: 1958E017C36F2E47

View File

@ -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,