added favicon, improve layout
This commit is contained in:
+75
-3
@@ -38,6 +38,28 @@ fn image_src_is_resolvable(element: &scraper::ElementRef) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn escape_html_attr(value: &str) -> String {
|
||||
value
|
||||
.replace('&', "&")
|
||||
.replace('"', """)
|
||||
.replace('<', "<")
|
||||
.replace('>', ">")
|
||||
}
|
||||
|
||||
// Some feeds (e.g. Deutsche Welle) don't embed an <img> in the item content at
|
||||
// all — they carry the article image as an RSS <enclosure> instead. Build an
|
||||
// <img> tag from it so those feeds get a preview image too.
|
||||
fn enclosure_image_html(item: &Item) -> Option<String> {
|
||||
let enclosure = item.enclosure()?;
|
||||
if !enclosure.mime_type().to_lowercase().starts_with("image/") {
|
||||
return None;
|
||||
}
|
||||
Some(format!(
|
||||
r#"<img src="{}">"#,
|
||||
escape_html_attr(enclosure.url())
|
||||
))
|
||||
}
|
||||
|
||||
fn create_feed_item(item: Item, feed: &Feed, connection: &mut PgConnection) {
|
||||
let item_title = item.title.clone().unwrap();
|
||||
log::info!("Create feed item: {}", item_title);
|
||||
@@ -48,9 +70,17 @@ fn create_feed_item(item: Item, feed: &Feed, connection: &mut PgConnection) {
|
||||
let mut content = "".to_string();
|
||||
|
||||
let selector_img = Selector::parse("img").unwrap();
|
||||
if let Some(image) = frag.select(&selector_img).find(image_src_is_resolvable) {
|
||||
content.push_str(&image.html());
|
||||
content.push_str("<br>");
|
||||
match frag.select(&selector_img).find(image_src_is_resolvable) {
|
||||
Some(image) => {
|
||||
content.push_str(&image.html());
|
||||
content.push_str("<br>");
|
||||
}
|
||||
None => {
|
||||
if let Some(image_html) = enclosure_image_html(&item) {
|
||||
content.push_str(&image_html);
|
||||
content.push_str("<br>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for node in frag.tree.nodes() {
|
||||
@@ -176,6 +206,48 @@ mod tests {
|
||||
assert!(html.select(&selector).find(image_src_is_resolvable).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enclosure_image_html_builds_img_for_image_enclosures() {
|
||||
let mut item = Item::default();
|
||||
item.set_enclosure(rss::Enclosure {
|
||||
url: "https://static.dw.com/image/73880499_302.jpg".to_string(),
|
||||
length: "2000".to_string(),
|
||||
mime_type: "image/jpeg".to_string(),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
Some(r#"<img src="https://static.dw.com/image/73880499_302.jpg">"#.to_string()),
|
||||
enclosure_image_html(&item)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enclosure_image_html_ignores_non_image_enclosures() {
|
||||
let mut item = Item::default();
|
||||
item.set_enclosure(rss::Enclosure {
|
||||
url: "https://example.test/episode.mp3".to_string(),
|
||||
length: "2000".to_string(),
|
||||
mime_type: "audio/mpeg".to_string(),
|
||||
});
|
||||
|
||||
assert_eq!(None, enclosure_image_html(&item));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enclosure_image_html_escapes_url_attribute() {
|
||||
let mut item = Item::default();
|
||||
item.set_enclosure(rss::Enclosure {
|
||||
url: "https://example.test/img.jpg?a=1&b=\"x\"".to_string(),
|
||||
length: "2000".to_string(),
|
||||
mime_type: "image/jpeg".to_string(),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
Some(r#"<img src="https://example.test/img.jpg?a=1&b="x"">"#.to_string()),
|
||||
enclosure_image_html(&item)
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn create_feed_item_does_not_duplicate_existing_items() {
|
||||
let mut connection = establish_connection();
|
||||
|
||||
Reference in New Issue
Block a user