news: headlines about the artists you follow, anchored to the artist

No per-artist music news API exists — every product that does this
aggregates and filters, so this does too. Google News RSS is the source:
free, keyless, no pagination, and no contract at all, which is why it
gets the most conservative pacer in the file (4 requests per 10 seconds)
and a 6-hour cache on disk as well as in memory. Nothing user-facing
waits on it; the home page asks after it is already drawn.

The query is the QUOTED artist name plus the word "music", and the
quoting is the whole design. Measured 2026-08-04 against the real feed: a
bare ambiguous name returned 0 of 10 relevant items — searching "Girls"
gives Indigo Girls, The Deli Girls and a school production of Mean Girls
— while the same name with a corroborating token returned 8 of 8, and an
unambiguous one 10 of 10. That is the identity-anchor rule the rest of
the app already lives by, applied to a provider that only speaks in
names: never trust the name alone.

The parser is deliberately not an XML library. One provider, four flat
tags inside <item> blocks, exactly the judgment the Bing scrape made.
What it does handle is what Google actually emits: entity-encoded titles
including numeric forms, a " - Publication" suffix on every headline
(stripped when the tail matches the <source> tag, so the title reads as a
headline rather than a citation), and RFC 2822 pubDates converted to unix
seconds by a hand-rolled civil-date conversion — nothing else in the
workspace needs a date crate for one field.

Freshness and relevance stay OUT of here. The 45-day window and the
review filter are display policy and belong to the surface that shows
them; this returns what the feed said, newest first, capped at a dozen.
This commit is contained in:
KeelrLeek committed 2026-08-04 19:08:35 -05:00
1 parent 50afa6a6a9
commit 8ef730848a
3 files changed
+245

No files matched your search

+19
View File
@@ -4204,6 +4204,25 @@ pub async fn unfollow_artist(
Ok(())
}
// ---- home ----
/// Headlines for the home page's news panel, one paced Google News query per
/// followed artist. Serial on purpose: the pacer inside `artist_news` is the
/// budget, and nothing user-blocking waits on this call — the frontend
/// requests it after the page is already up.
#[tauri::command]
pub async fn home_artist_news(
state: State<'_, LibraryState>,
artists: Vec<String>,
) -> Result<std::collections::HashMap<String, Vec<metadata::ArtistNewsItem>>, String> {
let mut out = std::collections::HashMap::new();
for name in artists {
let items = state.metadata.artist_news(&name).await;
out.insert(name, items);
}
Ok(out)
}
// ---- playlists ----
fn now_secs() -> i64 {
+1
View File
@@ -89,6 +89,7 @@ pub fn run() {
commands::followed_artists,
commands::follow_artist,
commands::unfollow_artist,
commands::home_artist_news,
commands::list_playlists,
commands::create_playlist,
commands::delete_playlist,