Add only what is a feed or links one; refuse the rest (#102)
Adding an address looked for the feed a web page links and, finding none, added the address as it was: every check then failed, and the sidebar called it a feed that had moved. cnn.com is one; its page links no feed. find_feed replaces feed_behind_page: the address is added if it is a feed or an OPML list, the feed its page links if it is a web page that links one (and that is a feed), and otherwise the add is refused with the reason, from the web page (400, the dialog stays open) and from `ipx add`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
42
src/feed.rs
42
src/feed.rs
@@ -394,20 +394,38 @@ fn alternate_feed_link(bytes: &[u8]) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// The feed a web page links as its own, for someone who pasted a site's address instead of
|
||||
/// its feed's. Anything else, or a page that cannot be read, comes back as given, and adding
|
||||
/// it goes on as before.
|
||||
pub async fn feed_behind_page(client: &reqwest::Client, url: &str) -> String {
|
||||
let found = async {
|
||||
let resp = client.get(url).timeout(std::time::Duration::from_secs(20)).send().await.ok()?;
|
||||
/// What someone asked to add, as a feed: the address itself when it is a feed or an OPML list,
|
||||
/// otherwise the feed its web page links as its own, otherwise an error and nothing is added.
|
||||
/// A page that linked no feed used to be added as it was and failed on every check, called a
|
||||
/// feed that moved (#102); cnn.com is one.
|
||||
pub async fn find_feed(client: &reqwest::Client, url: &str) -> Result<String> {
|
||||
let read = |u: String| async move {
|
||||
let resp = client
|
||||
.get(&u)
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.send()
|
||||
.await
|
||||
.context("connecting")?;
|
||||
anyhow::ensure!(resp.status().is_success(), "HTTP {}", resp.status());
|
||||
let base = resp.url().clone();
|
||||
let page = resp.bytes().await.ok()?;
|
||||
if !looks_like_html(&page) {
|
||||
return None;
|
||||
}
|
||||
base.join(&alternate_feed_link(&page)?).ok()
|
||||
anyhow::Ok((base, resp.bytes().await.context("reading")?))
|
||||
};
|
||||
found.await.map(String::from).unwrap_or_else(|| url.to_owned())
|
||||
let is_feed = |b: &[u8]| is_opml(b) || parse(b).is_ok();
|
||||
let (base, body) = read(url.to_owned()).await.with_context(|| format!("could not read {url}"))?;
|
||||
if is_feed(&body) {
|
||||
return Ok(url.to_owned());
|
||||
}
|
||||
if !looks_like_html(&body) {
|
||||
let why = parse(&body).err().map(|e| format!("{e:#}")).unwrap_or_default();
|
||||
anyhow::bail!("{url} is not a feed: {why}");
|
||||
}
|
||||
let Some(href) = alternate_feed_link(&body) else {
|
||||
anyhow::bail!("{url} is a web page that links no feed, so there is nothing to subscribe to");
|
||||
};
|
||||
let linked = base.join(&href).with_context(|| format!("{url} links {href} as its feed, which is not an address"))?;
|
||||
let (_, feed) = read(linked.to_string()).await.with_context(|| format!("{url} links {linked} as its feed, but"))?;
|
||||
anyhow::ensure!(is_feed(&feed), "{url} links {linked} as its feed, but that is not a feed either");
|
||||
Ok(linked.into())
|
||||
}
|
||||
|
||||
/// Artwork for a feed that has none: the icon its site's page names, or else the site's
|
||||
|
||||
Reference in New Issue
Block a user