fix: Correct parsing of relative/absolute paths in embed images & fix ImageSize being overwritten (#930)

Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>
This commit is contained in:
Ray
2026-08-24 14:05:35 -04:00
committed by GitHub
parent 0bce9b332e
commit 365572ccd8
2 changed files with 49 additions and 48 deletions

View File

@@ -202,6 +202,7 @@ impl Request {
pub async fn fetch_image_metadata(
url: &str,
request: Option<Request>,
size: ImageSize,
) -> Result<Option<Image>> {
if let Some(hit) = EMBED_CACHE.get(url).await {
match hit {
@@ -228,7 +229,7 @@ impl Request {
url: url.to_owned(),
width,
height,
size: ImageSize::Large,
size,
}))
} else {
Ok(None)
@@ -332,10 +333,12 @@ impl Request {
.map(Embed::Website)
.unwrap_or_default()
}
(mime::IMAGE, _) => Request::fetch_image_metadata(&url, Some(request))
.await
.map(|res| res.map(Embed::Image).unwrap_or_default())
.unwrap_or_default(),
(mime::IMAGE, _) => {
Request::fetch_image_metadata(&url, Some(request), ImageSize::Large)
.await
.map(|res| res.map(Embed::Image).unwrap_or_default())
.unwrap_or_default()
}
(mime::VIDEO, _) => Request::fetch_video_metadata(&url, Some(request))
.await
.map(|res| res.map(Embed::Video).unwrap_or_default())

View File

@@ -6,6 +6,24 @@ use revolt_models::v0::{
BandcampType, Image, ImageSize, LightspeedType, Special, TwitchType, Video, WebsiteMetadata,
};
use scraper::{Html, Selector};
use url::Url;
/// Convert all URLs to absolute form
fn url_to_absolute(page_url: &str, url: String) -> String {
if url.starts_with("http") && url.contains("://") {
//External
return url;
} else if url.starts_with('/') {
//Absolute
let page = Url::parse(page_url);
if page.is_err() {
return "".to_string();
}
return format!("{}{}", &page.unwrap().origin().unicode_serialization(), url);
}
//Relative
format!("{}/{}", &page_url.trim_end_matches('/'), url)
}
/// Create website metadata from URL and document
pub async fn create_website_embed(original_url: &str, document: &str) -> Option<WebsiteMetadata> {
@@ -61,14 +79,7 @@ pub async fn create_website_embed(original_url: &str, document: &str) -> Option<
.or_else(|| meta.remove("twitter:image"))
.or_else(|| meta.remove("twitter:image:src"))
.map(|s| s.trim().to_owned())
.map(|mut url| {
// If relative URL, prepend root URL. Also if root URL ends with a slash, remove it.
if let Some(ch) = url.chars().next() {
if ch == '/' {
url = format!("{}{}", &original_url.trim_end_matches('/'), url);
}
}
.map(|url| {
let mut size = ImageSize::Preview;
if let Some(card) = meta.remove("twitter:card") {
if &card == "summary_large_image" {
@@ -77,7 +88,7 @@ pub async fn create_website_embed(original_url: &str, document: &str) -> Option<
}
Image {
url: url.to_owned(),
url: url_to_absolute(original_url, url),
width: meta
.remove("og:image:width")
.unwrap_or_default()
@@ -96,42 +107,24 @@ pub async fn create_website_embed(original_url: &str, document: &str) -> Option<
.or_else(|| meta.remove("og:video:url"))
.or_else(|| meta.remove("og:video:secure_url"))
.map(|s| s.trim().to_owned())
.map(|mut url| {
// If relative URL, prepend root URL. Also if root URL ends with a slash, remove it.
if let Some(ch) = url.chars().next() {
if ch == '/' {
url = format!("{}{}", &original_url.trim_end_matches('/'), url);
}
}
Video {
url: url.to_owned(),
width: meta
.remove("og:video:width")
.unwrap_or_default()
.parse()
.unwrap_or(0),
height: meta
.remove("og:video:height")
.unwrap_or_default()
.parse()
.unwrap_or(0),
}
.map(|url| Video {
url: url_to_absolute(original_url, url),
width: meta
.remove("og:video:width")
.unwrap_or_default()
.parse()
.unwrap_or(0),
height: meta
.remove("og:video:height")
.unwrap_or_default()
.parse()
.unwrap_or(0),
}),
icon_url: link
.remove("apple-touch-icon")
.or_else(|| link.remove("icon"))
.map(|s| s.trim().to_owned())
.map(|mut v| {
// If relative URL, prepend root URL.
if let Some(ch) = v.chars().next() {
if ch == '/' {
v = format!("{}{}", &original_url.trim_end_matches('/'), v);
}
}
v
}),
.map(|url| url_to_absolute(original_url, url)),
colour: meta.remove("theme-color").map(|s| s.trim().to_owned()),
site_name: meta.remove("og:site_name").map(|s| s.trim().to_owned()),
url: meta
@@ -147,7 +140,9 @@ pub async fn create_website_embed(original_url: &str, document: &str) -> Option<
// fetch video size if missing
if metadata.special.is_none() {
if let Some(Video { width, height, url }) = &metadata.video {
if width == &0 || height == &0 {
if url.is_empty() {
metadata.video.take();
} else if width == &0 || height == &0 {
metadata.video =
match crate::requests::Request::fetch_video_metadata(url, None).await {
Ok(Some(video)) => Some(video),
@@ -168,9 +163,12 @@ pub async fn create_website_embed(original_url: &str, document: &str) -> Option<
width, height, url, ..
}) = &metadata.image
{
if width == &0 || height == &0 {
if url.is_empty() {
metadata.image.take();
} else if width == &0 || height == &0 {
let size = metadata.image.as_ref().unwrap().size.clone();
metadata.image =
match crate::requests::Request::fetch_image_metadata(url, None).await {
match crate::requests::Request::fetch_image_metadata(url, None, size).await {
Ok(Some(image)) => Some(image),
_ => None,
}