summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/readability
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-24 03:28:31 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-24 03:28:31 +0000
commit72f76cc41ae406e00a4dd9d448e2b552f9a6baaa (patch)
tree5f23e410b2ac4f8dddf89422ab57e738b3b04b2f /app/src/main/java/org/terst/feedreader/readability
parentab392e2ab983a4c87e241b457c98244251b2206a (diff)
Collapsible folders, long-press link menu, YouTube thumbnails, strip more Ars sequel chrome
- Feed list: folders now collapse (start collapsed, with a per-folder unread count so collapsing doesn't hide "is there anything new") - Long-pressing a link inside an article shows an action menu (open in browser, share, save to Stash, save for later) instead of WebView's default text-selection popup -- confirmed live end to end, including the Stash/save-for-later paths creating a real standalone article - New shared ensureStandaloneArticle() helper (data/ArticleSaving.kt), factored out of ShareReceiverActivity, now also used by the long-press menu - YouTube iframe embeds become tap-to-open thumbnails (JS is off in the reader WebView, so the real player never worked anyway); tapping one goes straight to the YouTube app/browser instead of our own article-extraction pipeline - Fixed back navigation through a chain of links-within-links: each hop was stacking a new backstack entry, so back stepped through the chain one link at a time instead of returning straight to the source article. Confirmed live: A -> link -> B -> link -> C, one back-press now goes straight to A - Extended the <header> chrome-stripping from last round: also strip <footer>, an author bio card, and a "Most Read" trending-stories widget -- confirmed live one of the latter showed up as a giant unrelated photo plus an empty numbered list (its lazy-loaded items never populate in this hidden, off-screen extraction WebView) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/readability')
-rw-r--r--app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt73
1 files changed, 60 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 f9adbb4..3cb4741 100644
--- a/app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt
+++ b/app/src/main/java/org/terst/feedreader/readability/ReaderExtractor.kt
@@ -180,23 +180,70 @@ object ReaderExtractor {
}
}
+ // A YouTube iframe embed is dead weight here -- JS is off in the WebView
+ // that actually displays this content, so the real embedded player could
+ // never initialize anyway (just a blank/broken box). Swap it for a
+ // tap-to-open thumbnail (a real <img>, which Readability treats like any
+ // other content image) linking to the video -- newReaderWebView sends a
+ // YouTube watch link straight to the YouTube app/browser instead of our
+ // own article-extraction pipeline, since there's no article to extract
+ // from a video page.
+ var iframes = document.querySelectorAll(
+ 'iframe[src*="youtube.com/embed/"], iframe[src*="youtube-nocookie.com/embed/"]'
+ );
+ for (var i = 0; i < iframes.length; i++) {
+ var frame = iframes[i];
+ var match = frame.getAttribute('src').match(/embed\/([a-zA-Z0-9_-]+)/);
+ if (!match) continue;
+ var videoId = match[1];
+ var link = document.createElement('a');
+ link.className = 'yt-embed';
+ link.href = 'https://www.youtube.com/watch?v=' + videoId;
+ var thumb = document.createElement('img');
+ thumb.src = 'https://i.ytimg.com/vi/' + videoId + '/hqdefault.jpg';
+ thumb.alt = 'YouTube video';
+ var play = document.createElement('span');
+ play.className = 'yt-play';
+ link.appendChild(thumb);
+ link.appendChild(play);
+ frame.parentNode.replaceChild(link, frame);
+ }
+
// A news/blog article page's <header> conventionally holds everything
// BEFORE the real body: a section/category kicker, the dek/subhead, the
- // byline, and the hero image + its caption. Readability doesn't exclude
- // <header> by tag outright (only deprioritizes it in scoring), so on
- // some articles enough of it scores highly enough to get pulled in
+ // byline, and the hero image + its caption. <footer> (site nav/link
+ // menus) is the same story at the other end. Readability doesn't exclude
+ // either by tag outright (only deprioritizes them in scoring), so on
+ // some articles enough of one scores highly enough to get pulled in
// alongside the real body -- confirmed live: a kicker icon, the dek
// paragraph, AND an orphaned duplicate hero-image caption (a mobile-only
- // copy with no adjacent <img> at all) all leaking in above the real
- // lede on an Ars Technica article. This is a pre-processing decision on
- // our side, not a change to Readability's own algorithm: treat <header>
- // as page chrome, never article content, and remove it before Readability
- // ever sees the page. (The article's actual title is shown separately --
- // see ReaderWebView.wrapReaderHtml -- so losing the in-body <h1> here is
- // fine, it was never meant to double as the body text anyway.)
- var headers = document.querySelectorAll('header');
- for (var i = 0; i < headers.length; i++) {
- headers[i].parentNode.removeChild(headers[i]);
+ // copy with no adjacent <img> at all) all leaking in above the real lede
+ // on an Ars Technica article. This is a pre-processing decision on our
+ // side, not a change to Readability's own algorithm: treat <header>/
+ // <footer> as page chrome, never article content, and remove them before
+ // Readability ever sees the page. (The article's actual title is shown
+ // separately -- see ReaderWebView.wrapReaderHtml -- so losing the in-body
+ // <h1> here is fine, it was never meant to double as the body text.)
+ var chrome = document.querySelectorAll('header, footer');
+ for (var i = 0; i < chrome.length; i++) {
+ chrome[i].parentNode.removeChild(chrome[i]);
+ }
+
+ // Two more sibling widgets confirmed live, same "not article content"
+ // story: an author bio card (photo + name + one-paragraph bio, after the
+ // real body -- its photo has the same Tailwind-dependent absolute-fill
+ // styling as the section-badge icon and hero image, so it'd render
+ // oversized for the same underlying reason even if kept) and a "Most
+ // Read"/trending-stories widget (a list of OTHER articles' thumbnails --
+ // confirmed live: one showed up as a giant unrelated photo followed by an
+ // empty numbered list, since the widget's later items only populate their
+ // thumbnail on scroll-into-view, which never happens in this hidden,
+ // off-screen extraction WebView).
+ var sequels = document.querySelectorAll(
+ '[class*="author-mini-bio" i], [class*="author-bio" i], [class*="most-read" i]'
+ );
+ for (var i = 0; i < sequels.length; i++) {
+ if (sequels[i].parentNode) sequels[i].parentNode.removeChild(sequels[i]);
}
// Readability's default hidden-element skip (treating display:none /