Every ingested article used to show up on the homepage regardless of its
source, which meant a handful of high-volume feeds could flood "Top Stories."
Sources now default to not appearing there; a source has to explicitly opt
in via a new checkbox (also toggleable inline with a star icon) for its
articles to show up on the homepage feed. An article shows there if any of
its contributing sources opted in — merged/clustered stories aren't held to
requiring all sources to agree. Category pages, Local, tags, and events are
unaffected; this only gates the bare, no-filter homepage query.
Schema: sources.push_to_top_stories and merged_articles.top_stories, both
backfilled for existing databases via ALTER TABLE.
YouTube's public Atom feed only accepts a channel_id (or the legacy user
param) — it has no equivalent for the newer @handle format, so pasting a
handle URL straight into the source's url field wouldn't have worked. The
adapter now accepts a bare channel ID, a /channel/UC... URL, an @handle URL,
or a bare handle/username, resolving whichever was given to the actual
channel ID by reading it off the channel page when needed.
- Fix "Body cannot be empty" error on DELETE by making the JSON content-type
parser tolerate empty bodies, and by only sending Content-Type from the
frontend when a request actually has one.
- Deleting a source now cascades: raw content items and any article composed
entirely from that source are removed too, plus their media.
- Add admin endpoints/UI to clear all articles, all media, or a single
source's content without deleting the source, so things can be repopulated
fresh.
- Sources can now be assigned multiple categories via checkboxes (instead of
free text) and edited in place, not just added/deleted.
- Add a "News" default category (seeded fresh, backfilled on existing DBs) so
general news sources have a real home instead of the pseudo-category "Top
stories", which is just the homepage's all-categories chronological view.
- Widen the site's content column 15% (1080px -> 1242px).
- Add YouTube as its own source type/ingestion module: pulls a channel's
public Atom feed, and each video always publishes directly as its own
article (title, embedded video, publish date, description) rather than
going through the cross-source clustering/synthesis pipeline.