The same feed over http and https is one feed (#362)
rays was subscribed to oldvcr's blog twice, once at https://oldvcr.blogspot.com/feeds/posts/default and once at the same address over http, and saw every post twice. same_feed compared addresses exactly but for Patreon's, so add_to_catalogue found nothing there already; check_url compared exactly too and now asks same_feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,13 +22,17 @@ pub fn is_patreon_creator(url: &str) -> bool {
|
|||||||
matches!(patreon_parts(url), Some((_, None)))
|
matches!(patreon_parts(url), Some((_, None)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether two URLs are the same feed. One Patreon show has several spellings -- by the
|
/// Whether two URLs are the same feed. The same address over http and https is one feed:
|
||||||
/// creator's name, by number, or with no creator at all -- and the token and show are what
|
/// oldvcr's blog was subscribed under both, every post twice (#362). One Patreon show has several
|
||||||
/// identify it.
|
/// spellings -- by the creator's name, by number, or with no creator at all -- and the token and
|
||||||
|
/// show are what identify it.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn same_feed(a: &str, b: &str) -> bool {
|
pub fn same_feed(a: &str, b: &str) -> bool {
|
||||||
// Parsing both is the slow part, and only Patreon's addresses can differ and still match.
|
fn bare(u: &str) -> &str {
|
||||||
a == b
|
u.strip_prefix("https://").or_else(|| u.strip_prefix("http://")).unwrap_or(u)
|
||||||
|
}
|
||||||
|
// Parsing both is the slow part, and only Patreon's addresses can differ further and still match.
|
||||||
|
bare(a) == bare(b)
|
||||||
|| (a.contains("patreon.com")
|
|| (a.contains("patreon.com")
|
||||||
&& b.contains("patreon.com")
|
&& b.contains("patreon.com")
|
||||||
&& patreon_parts(a).is_some_and(|p| Some(p) == patreon_parts(b)))
|
&& patreon_parts(a).is_some_and(|p| Some(p) == patreon_parts(b)))
|
||||||
@@ -117,6 +121,13 @@ mod tests {
|
|||||||
assert!(!is_patreon_creator(&format!("https://www.patreon.com/rss/x?auth={tok}&show=1")), "one show is a feed");
|
assert!(!is_patreon_creator(&format!("https://www.patreon.com/rss/x?auth={tok}&show=1")), "one show is a feed");
|
||||||
assert!(!is_patreon_creator(&format!("https://example.com/rss?auth={tok}")));
|
assert!(!is_patreon_creator(&format!("https://example.com/rss?auth={tok}")));
|
||||||
|
|
||||||
|
assert!(same_feed(
|
||||||
|
"http://oldvcr.blogspot.com/feeds/posts/default",
|
||||||
|
"https://oldvcr.blogspot.com/feeds/posts/default"
|
||||||
|
));
|
||||||
|
assert!(!same_feed("https://a.example/feed", "https://b.example/feed"));
|
||||||
|
assert!(!same_feed("https://a.example/feed", "https://a.example/feed/comments"));
|
||||||
|
|
||||||
// The show you already have by name is the one a bare token would add by number.
|
// The show you already have by name is the one a bare token would add by number.
|
||||||
assert!(same_feed(
|
assert!(same_feed(
|
||||||
&format!("https://www.patreon.com/rss/glasscannon?auth={tok}&show=2073588"),
|
&format!("https://www.patreon.com/rss/glasscannon?auth={tok}&show=2073588"),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- A feed you already have, added again by the same address over http rather than https or the other way round, is the one you have, not a second subscription with every item twice. Changing a feed's address to another feed's over the other scheme is refused the same way (#362).
|
||||||
- A podcast that was once in an OPML and is now a feed of its own is no longer hidden from All Subscriptions when that OPML is, so its unread items are listed there as the feed list counts them (#358).
|
- A podcast that was once in an OPML and is now a feed of its own is no longer hidden from All Subscriptions when that OPML is, so its unread items are listed there as the feed list counts them (#358).
|
||||||
|
|
||||||
## [1.5.1] - 2026-10-24
|
## [1.5.1] - 2026-10-24
|
||||||
|
|||||||
@@ -611,6 +611,19 @@ mod tests {
|
|||||||
assert!(ctx.db.feed_summary("has-file").await.unwrap().orphaned, "and flagged as orphaned");
|
assert!(ctx.db.feed_summary("has-file").await.unwrap().orphaned, "and flagged as orphaned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn a_feed_added_again_over_the_other_scheme_is_the_one_already_there() {
|
||||||
|
// oldvcr's blog, subscribed over https and again over http: every post twice (#362).
|
||||||
|
let mut cfg = config::Config::default();
|
||||||
|
let mut f = feed();
|
||||||
|
f.url = "https://oldvcr.blogspot.com/feeds/posts/default".into();
|
||||||
|
cfg.feeds.insert("oldvcr".into(), f);
|
||||||
|
let ctx = test_ctx(cfg).await;
|
||||||
|
let got = add_to_catalogue(&ctx, "http://oldvcr.blogspot.com/feeds/posts/default", None, vec![]).await.unwrap();
|
||||||
|
assert_eq!(got, ("oldvcr".to_owned(), true));
|
||||||
|
assert_eq!(ctx.cfg().feeds.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn a_show_made_a_feed_of_its_own_is_not_hidden_with_its_list() {
|
async fn a_show_made_a_feed_of_its_own_is_not_hidden_with_its_list() {
|
||||||
// Unsung: promoted out of a Hacker News OPML hidden from All Subscriptions, its unread
|
// Unsung: promoted out of a Hacker News OPML hidden from All Subscriptions, its unread
|
||||||
|
|||||||
@@ -1238,7 +1238,7 @@ fn check_url(url: &str, id: &str, feeds: &BTreeMap<String, crate::config::Feed>)
|
|||||||
Ok(u) => return Err(format!("{:?} is not an http(s) URL", u.scheme())),
|
Ok(u) => return Err(format!("{:?} is not an http(s) URL", u.scheme())),
|
||||||
Err(e) => return Err(format!("that is not a valid URL: {e}")),
|
Err(e) => return Err(format!("that is not a valid URL: {e}")),
|
||||||
}
|
}
|
||||||
if let Some((other, _)) = feeds.iter().find(|(k, f)| k.as_str() != id && f.url == url) {
|
if let Some((other, _)) = feeds.iter().find(|(k, f)| k.as_str() != id && crate::feed::same_feed(&f.url, url)) {
|
||||||
return Err(format!("{other:?} is already subscribed to that URL"));
|
return Err(format!("{other:?} is already subscribed to that URL"));
|
||||||
}
|
}
|
||||||
Ok(url.to_owned())
|
Ok(url.to_owned())
|
||||||
|
|||||||
Reference in New Issue
Block a user