Research user-definable parsing for non-RSS (website) feeds #143
Labels
No labels
bug
devops
duplicate
enhancement
good first issue
layout
next major release
next minor release
question
research
testing
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lvl0/fedi-feed-router#143
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adding a website-scraped source today requires a code change and a deploy. This ticket is research only: work out how a user could define, adapt, or override parsing rules for a non-RSS feed themselves, and produce a recommendation plus follow-up implementation tickets.
No implementation in this ticket.
Problem
RSS feeds are effectively declarative — point at a URL and the generic XML path in
ArticleFetcher::getArticlesFromRssFeed()handles it. Website feeds are the opposite: every source needs hand-written PHP.Adding one source currently means:
HomepageParserInterface/ArticleParserInterfaceconfig/feed.phpentry wiring the class names inSo the app can only ever scrape sources its maintainer has written code for. A self-hosted user who wants their local news site has no path at all.
Current architecture (as of this ticket)
Resolution is config-driven but the parsers themselves are hardcoded classes:
HomepageParserFactory::getParserForFeed()andArticleParserFactory::getParserForFeed()readconfig("feed.providers.{$feed->provider}.parsers.*")and instantiate the mapped class.ArticleParserFactory::getParser()(used byArticleFetcher::fetchArticleData()) instead loops a hardcoded$parsersarray and picks the first whosecanParse($url)matches — a second, separate resolution path that ignores the feed's provider entirely. Any design has to account for both.The three existing providers are not shaped alike, which constrains the format:
VrtHomepageParseris a singlepreg_match_allover raw HTML with the language interpolated into the pattern. Language-parameterised.BelgaHomepageParseris not HTML at all. It decodes a JSON API response, plucksdata[].id, and templates those into article URLs via asprintfpattern.VrtArticlePageParseris already a chain of prioritised fallbacks (og:title→h1→<title>), which is a strong hint that the natural user-facing format is an ordered list of extraction rules per field, first match wins.Relevant existing facts:
feeds.settingsis ajsoncolumn, in$fillable, cast toarray, and currently unused — likely storage for per-feed rules with no migration needed.feeds.providerisNOT NULL($table->string('provider')), andCreateFeedActionderives the URL fromconfig("feed.providers.{$provider}.languages.{$langCode}.url"). A user-defined feed has no provider config entry, so this path needs rethinking.composer.jsonhas nosymfony/dom-crawler,symfony/css-selector, or equivalent. All current parsing is regex over raw strings.Research questions
Rule format
id→ article URL) expressed declaratively?Dependency
symfony/dom-crawler+symfony/css-selector? Weigh against the project's low-scaffolding preference.Layering
settingsoverrides, or full replacement only?providervalue, given the NOT NULL column?Security — user-supplied extraction rules are executed server-side:
HttpFetcherat an arbitrary URL. Assess what the current fetcher already does or does not guard against.UX
Migration
Non-goals
Relationship to other tickets
config/feed.phpprovider entry. Worth deciding whether that shared groundwork lands in #34 or in a third ticket.Deliverable