Paywall detection: swap to Wayback snapshot when article body is gated
Fetch each new article once and measure visible <p> text. If body is below 500 chars (or fetch fails), resolve a Wayback snapshot via the archive.org/wayback/available API and use that URL for both the og:image fallback and the posted link. Dedup keys stay derived from the original URL so paywalled/non-paywalled hits collide as before. - New: internal/ingestion/article.go (FetchArticleMeta via goquery) - New: internal/ingestion/wayback.go (ResolveWayback) - Removed: internal/ingestion/og.go (folded into article.go) - poller.go: dedup first, then one article fetch, then snapshot fallback
This commit is contained in:
@@ -106,17 +106,10 @@ func (p *Poller) pollOnceWithErr(src config.SourceConfig) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// Last-resort image fallback: feed gave us nothing, scrape og:image
|
||||
// from the article page. Only runs for genuinely new items.
|
||||
if items[i].ImageURL == "" {
|
||||
if og := FetchOGImage(items[i].ArticleURL); og != "" {
|
||||
items[i].ImageURL = og
|
||||
slog.Debug("og:image fallback used",
|
||||
"guid", items[i].GUID, "url", items[i].ArticleURL, "image", og)
|
||||
}
|
||||
}
|
||||
|
||||
canonical := dedup.CanonicalURL(items[i].ArticleURL)
|
||||
// Dedup checks first — canonical and headline use the ORIGINAL URL so
|
||||
// they stay stable whether we end up swapping to a Wayback snapshot.
|
||||
originalURL := items[i].ArticleURL
|
||||
canonical := dedup.CanonicalURL(originalURL)
|
||||
headlineNorm := dedup.NormalizeHeadline(items[i].Headline)
|
||||
|
||||
if storage.IsCanonicalSeen(canonical) {
|
||||
@@ -130,6 +123,38 @@ func (p *Poller) pollOnceWithErr(src config.SourceConfig) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// Genuinely new story — fetch the article page once. We use it for:
|
||||
// 1. og:image fallback when the feed didn't give us one.
|
||||
// 2. Paywall detection: visible body text below the threshold means
|
||||
// the article is gated, so swap in a Wayback snapshot for both
|
||||
// Pete (image) and the reader (posted link).
|
||||
meta := FetchArticleMeta(originalURL)
|
||||
paywalled := !meta.Fetched || meta.BodyChars < PaywallBodyThreshold
|
||||
if paywalled {
|
||||
if snap := ResolveWayback(originalURL); snap != "" {
|
||||
snapMeta := FetchArticleMeta(snap)
|
||||
if snapMeta.Fetched {
|
||||
items[i].ArticleURL = snap
|
||||
if items[i].ImageURL == "" && snapMeta.ImageURL != "" {
|
||||
items[i].ImageURL = snapMeta.ImageURL
|
||||
}
|
||||
slog.Info("paywall detected, using wayback snapshot",
|
||||
"guid", items[i].GUID, "original", originalURL,
|
||||
"snapshot", snap, "body_chars", meta.BodyChars)
|
||||
} else {
|
||||
slog.Debug("paywall detected but snapshot fetch failed",
|
||||
"guid", items[i].GUID, "original", originalURL, "snapshot", snap)
|
||||
}
|
||||
} else {
|
||||
slog.Debug("paywall detected but no wayback snapshot available",
|
||||
"guid", items[i].GUID, "url", originalURL, "body_chars", meta.BodyChars)
|
||||
}
|
||||
} else if items[i].ImageURL == "" && meta.ImageURL != "" {
|
||||
items[i].ImageURL = meta.ImageURL
|
||||
slog.Debug("og:image fallback used",
|
||||
"guid", items[i].GUID, "url", originalURL, "image", meta.ImageURL)
|
||||
}
|
||||
|
||||
// Stamp source metadata onto the item
|
||||
items[i].Source = src.Name
|
||||
items[i].FeedHint = src.FeedHint
|
||||
|
||||
Reference in New Issue
Block a user