diff --git a/fluxer_unfurl/src/html_parser.rs b/fluxer_unfurl/src/html_parser.rs
index 2ace67ea1..b90cbd0dc 100644
--- a/fluxer_unfurl/src/html_parser.rs
+++ b/fluxer_unfurl/src/html_parser.rs
@@ -135,7 +135,10 @@ pub fn parse_opengraph(html: &str) -> OgMetadata {
};
if og.title.is_none() {
- og.title = meta.first("twitter:title");
+ og.title = meta
+ .first("twitter:title")
+ .or_else(|| document_title(&doc))
+ .or_else(|| meta.first("title"));
}
if og.description.is_none() {
@@ -145,6 +148,16 @@ pub fn parse_opengraph(html: &str) -> OgMetadata {
og
}
+fn document_title(doc: &Html) -> Option {
+ let selector = Selector::parse("title").ok()?;
+ let text = doc.select(&selector).next()?.text().collect::();
+ let trimmed = text.trim();
+ if trimmed.is_empty() {
+ return None;
+ }
+ Some(trimmed.to_owned())
+}
+
#[allow(dead_code)]
pub fn parse_twitter_card(html: &str) -> TwitterCardMetadata {
let doc = Html::parse_document(html);
@@ -335,7 +348,7 @@ mod tests {
#[test]
fn handles_missing_tags() {
let m = og("X");
- assert!(m.title.is_none());
+ assert_eq!(m.title.as_deref(), Some("X"));
assert!(m.description.is_none() && m.image.is_none() && m.url.is_none());
}
@@ -380,18 +393,34 @@ mod tests {
}
#[test]
- fn title_uses_open_graph_then_twitter_only() {
+ fn title_fallback_chain_matches_ts() {
let m = og(r#""#);
assert_eq!(m.title.as_deref(), Some("OG"));
let m = og(r#""#);
assert_eq!(m.title.as_deref(), Some("TW"));
+ let m = og(r#"HTML Title"#);
+ assert_eq!(m.title.as_deref(), Some("HTML Title"));
+ let m = og(r#""#);
+ assert_eq!(m.title.as_deref(), Some("Meta"));
}
#[test]
- fn title_never_falls_back_to_document_title() {
- let m = og(r#"HTML Title"#);
- assert!(m.title.is_none());
- let m = og(r#""#);
+ fn title_prefers_open_graph_over_the_document_title() {
+ let m = og(
+ r#"HTML Title"#,
+ );
+ assert_eq!(m.title.as_deref(), Some("OG"));
+ let m = og(
+ r#"HTML Title"#,
+ );
+ assert_eq!(m.title.as_deref(), Some("TW"));
+ }
+
+ #[test]
+ fn document_title_is_trimmed_and_blank_is_ignored() {
+ let m = og(r#" Hello World "#);
+ assert_eq!(m.title.as_deref(), Some("Hello World"));
+ let m = og(r#" "#);
assert!(m.title.is_none());
}
diff --git a/fluxer_unfurl/src/resolvers/default_resolver.rs b/fluxer_unfurl/src/resolvers/default_resolver.rs
index 23c6bb612..236aaff5c 100644
--- a/fluxer_unfurl/src/resolvers/default_resolver.rs
+++ b/fluxer_unfurl/src/resolvers/default_resolver.rs
@@ -197,7 +197,11 @@ async fn resolve_html(ctx: &ResolveContext<'_>) -> anyhow::Result