summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/readability
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-22 20:37:41 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-22 20:37:41 +0000
commit34b663c6e75a2c360de154418dafb8f616b546a1 (patch)
tree300f7b5a2344925832d13f634abe16470ce668ae /app/src/main/java/org/terst/feedreader/readability
parentee019c91f66b6ba35b7295ea01220c6c13819b1c (diff)
Fix extraction regression on kottke; add Older view, mark-read-from-here
- ReaderExtractor: the un-hide-before-extracting fix (for soft paywalls like West Hawaii Today) was unconditional -- it also un-hid normal, legitimately-collapsed chrome (kottke's footer/membership notice), which Readability then folded into the result alongside the real article. Reported live with a screenshot showing the extracted content wrapped in site footer boilerplate. Now tries the normal (hidden-respecting) pass first and only falls back to the un-hide pass if that came back thin (<500 chars), keeping whichever pass actually found more text. Verified live: the same kottke article now stays clean and stable (no delayed switch to the footer version). - New Unread/Older split: the default Unread view is now clamped to the last 72 hours (RECENT_WINDOW_MS); the rest of the unread backlog moved to a new "Older" view (new FeedListScreen row, ArticleDao.observeUnread now takes a cutoff, observeOlderUnread/observeUnreadCount/ observeOlderUnreadCount added). Undated articles are kept in the main Unread view rather than silently dropped from both. Verified live: 150 recent / 1172 older, summing to the prior combined total. - "Mark read from here": long-press an article row to mark it and everything older in that same filtered list as read in one batch (re-added ArticleDao.markReadBatch, this time as an explicit, confirmed action -- not the implicit on-scroll version that was reverted earlier). Verified live: confirmed a 147-article batch: the tapped article and older were marked read, newer ones above it were not. - BACKLOG.md: added a YouTube-embeds note (JS is off in the reader WebView, so embeds currently show broken -- rough shape is a tap-to-play thumbnail rather than turning JS on for third-party content).
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/readability')
-rw-r--r--app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt39
1 files changed, 26 insertions, 13 deletions
diff --git a/app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt b/app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt
index 28c762c..7e8e10c 100644
--- a/app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt
+++ b/app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt
@@ -69,16 +69,29 @@ object ReaderExtractor {
val script = """
(function() {
try {
- // Some "soft" paywalls deliver the full article in the initial HTML
- // response to every visitor, then hide it with CSS (inline or via a
- // stylesheet rule) pending a client-side subscription check -- the
- // content itself isn't withheld by the server, only its visibility is.
- // Readability has a built-in heuristic that skips anything hidden this
- // way (treating it as boilerplate to discard), which is exactly what
- // was throwing away real, already-delivered article text and leaving
- // only the visible teaser/subscribe prompt. Un-hide before extracting --
- // this must run on the live document (getComputedStyle needs a node
- // attached to the render tree), so it happens before the clone below.
+ $readabilityJs
+ function plainLength(article) {
+ return (article && article.textContent) ? article.textContent.trim().length : 0;
+ }
+
+ // Readability's default hidden-element skip (treating display:none /
+ // visibility:hidden nodes as boilerplate) is correct for the vast
+ // majority of sites -- it's what keeps collapsed nav/footer/sidebar
+ // chrome out of the result. Try it first, unmodified.
+ var normalArticle = new Readability(document.cloneNode(true)).parse();
+ if (plainLength(normalArticle) >= 500) {
+ return JSON.stringify(normalArticle);
+ }
+
+ // Only reached when the normal pass came back thin. Some "soft"
+ // paywalls deliver the full article in the initial HTML response to
+ // every visitor, then hide it with CSS (inline or via a stylesheet
+ // rule) pending a client-side subscription check -- the content isn't
+ // withheld by the server, only its visibility is, and Readability's
+ // hidden-element skip discards it the same as any other hidden chrome.
+ // Un-hide and retry; keep whichever pass actually found more text,
+ // since un-hiding everything indiscriminately can just as easily pull
+ // in an unrelated hidden footer/notice on a normal (non-paywalled) page.
var all = document.querySelectorAll('*');
for (var i = 0; i < all.length; i++) {
var el = all[i];
@@ -88,9 +101,9 @@ object ReaderExtractor {
el.style.setProperty('visibility', 'visible', 'important');
}
}
- $readabilityJs
- var article = new Readability(document.cloneNode(true)).parse();
- return JSON.stringify(article);
+ var unhiddenArticle = new Readability(document.cloneNode(true)).parse();
+ var best = plainLength(unhiddenArticle) > plainLength(normalArticle) ? unhiddenArticle : normalArticle;
+ return JSON.stringify(best);
} catch (e) {
return JSON.stringify({error: e.toString()});
}