artist art: keep a known-good hero instead of rediscovering it daily
An artist's hero was re-discovered every 24 hours through a tier that fails per-name, and the stale manifest was deleted on the way in -- before the replacement lookup had run. So one bad discovery day did not merely fail to refresh a hero, it destroyed the good one first and left the artist blank. On this machine that took the persisted store from 26 artists down to 3; Crazy P, MSPAINT, Sword II, Hermeth, Billy Cobb, Pond, Weezer, Daft Punk and King Krule all had correct banners on disk that nothing would ever read again. Three changes: - The manifest TTL goes from 24 hours to 30 days, matching the cover resolution horizon. A press photo is still the artist's press photo a month later; it does not go stale the way metadata does. - Reading a manifest no longer destroys it. Stale and older-recipe manifests are retained as a floor and only replaced once a lookup returns something better, which also migrates them forward to the current recipe. Only files that are unusable -- oversized, unparseable, or belonging to another artist -- are removed. - A failed hero discovery is detected by an EMPTY BANNER LIST, not a None: catalog_artist_art returns Some as soon as the Deezer profile resolves, so the previous None check could never fire, and an empty result would also overwrite good banners whenever logos were present. Also widens discovery so fewer artists need the floor at all. The query ladder gains the bare artist name: decorated queries can collapse a rare artist onto a popular entity sharing a word -- every "Hana Vu ... photo" phrasing returns the Japanese group HANA (525 of 525 cards, quoting included), while the bare name returns her real press photos with 16 of 17 passing the identity gate as Strong. The gate, not the query, is what filters. And a near-empty Bing page now counts as degraded. The retry test was is_empty(), which let the commonest degraded shape through: a one-card page of something unrelated, cached as that artist's verdict for a quarter hour. Measured 15 of 30 waves for MSPAINT and Billy Cobb. The richer of the two reads is kept so a thin retry cannot lose a good first answer. Verified live: Crazy P, MSPAINT, Sword II and Hermeth all render heroes again.
This commit is contained in:
1 parent
ee2509538a
commit
7ce9fd0efc
2 files changed
+148
-37
No files matched your search
@@ -566,6 +566,10 @@ const HERO_SCORE_CONCURRENCY: usize = 4;
|
||||
/// every press photo across the probe fixtures measures 1.33 or wider.
|
||||
const HERO_MIN_ASPECT: f64 = 1.15;
|
||||
const HERO_SEARCH_WAVE_CONCURRENCY: usize = 3;
|
||||
/// A Bing image page with at most this many cards is a degraded answer, not a
|
||||
/// small result set. Bing's genuine pages run 12-100 cards; the degraded shape
|
||||
/// is one card of something unrelated.
|
||||
const HERO_DEGRADED_PAGE_CARDS: usize = 2;
|
||||
const HERO_SCORE_CACHE_MAX_ENTRIES: usize = 128;
|
||||
const HERO_SCORE_RECIPE: &str = "bing-identity-visual-v1";
|
||||
|
||||
@@ -1819,6 +1823,16 @@ impl MetadataClient {
|
||||
// crowding out better solo/DJ portraits for non-band artists.
|
||||
let queries = [
|
||||
format!("{artist} band photo"),
|
||||
// The BARE name, no decoration at all. Our decorated queries can
|
||||
// collapse a rare artist onto a popular entity that shares a word:
|
||||
// every "Hana Vu ... photo" phrasing returns the Japanese girl group
|
||||
// HANA (525 of 525 cards, and quoting does not help), while the bare
|
||||
// "Hana Vu" returns her real press photos -- 16 of 17 pass the
|
||||
// identity gate as Strong (NME, Pitchfork, LA Times, Nylon, Dork).
|
||||
// Safe to widen the funnel here because the identity gate is the
|
||||
// thing that filters: it rejected ~3,000 cards in the audit that
|
||||
// found this, with no observed false rejects.
|
||||
artist.to_string(),
|
||||
format!(
|
||||
"\"{artist}\" musician portrait photo -album -cover -vinyl -poster -flyer -tickets"
|
||||
),
|
||||
@@ -1883,18 +1897,30 @@ impl MetadataClient {
|
||||
.map(|url| async move {
|
||||
// Bing answers the SAME url with wildly different pages:
|
||||
// measured back to back, one card on one read and 25-80 on
|
||||
// the next. A card-less page is a degraded answer, not an
|
||||
// the next. A near-empty page is a degraded answer, not an
|
||||
// empty result set, so drop it and ask once more rather than
|
||||
// caching it as this artist's verdict for the next quarter
|
||||
// hour.
|
||||
//
|
||||
// The emptiness test used to be `is_empty()`, which let the
|
||||
// commonest degraded shape straight through: a ONE-card page
|
||||
// carrying something unrelated. Measured across one audit,
|
||||
// 15 of 30 waves for MSPAINT and Billy Cobb, 6 of 15 for
|
||||
// DJ Sabrina and 9 of 15 for TAGABOW came back with exactly
|
||||
// one junk card -- each one enough to blank that artist for
|
||||
// a full CATALOG_TTL. Keep whichever read is richer so a
|
||||
// thin retry can never lose a good first answer.
|
||||
let mut parsed = match self.browser_html(&url).await {
|
||||
Some(page) => parse_bing_image_results(&page),
|
||||
None => Vec::new(),
|
||||
};
|
||||
if parsed.is_empty() {
|
||||
if parsed.len() <= HERO_DEGRADED_PAGE_CARDS {
|
||||
self.forget_html(&url).await;
|
||||
if let Some(page) = self.browser_html(&url).await {
|
||||
parsed = parse_bing_image_results(&page);
|
||||
let retried = parse_bing_image_results(&page);
|
||||
if retried.len() > parsed.len() {
|
||||
parsed = retried;
|
||||
}
|
||||
}
|
||||
}
|
||||
parsed
|
||||
|
||||
Reference in new issue
Block a user