diff --git a/crates/metadata/src/lib.rs b/crates/metadata/src/lib.rs index 833aa1b..4845bea 100644 --- a/crates/metadata/src/lib.rs +++ b/crates/metadata/src/lib.rs @@ -565,6 +565,15 @@ const HERO_SCORE_CONCURRENCY: usize = 4; /// Minimum width:height for a hero candidate. Album sleeves are exactly 1.0; /// every press photo across the probe fixtures measures 1.33 or wider. const HERO_MIN_ASPECT: f64 = 1.15; +/// Portrait sources are admitted below this ratio, well clear of the square +/// band where release artwork lives. The app crops tall sources to 16:9 around +/// the detected faces (`hero_crop::maximal_hero_rect` keeps full width and +/// slides the window vertically), so a portrait press photo is usable hero +/// material while a 1:1 sleeve is not. +const HERO_PORTRAIT_MAX_ASPECT: f64 = 0.92; +/// A portrait source is cropped to full-width x (width*9/16), so its width IS +/// the resulting hero width. Keep enough pixels that the crop is still a hero. +const HERO_PORTRAIT_MIN_WIDTH: u32 = 1000; 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 @@ -8902,10 +8911,22 @@ fn score_bing_hero_image( // collage detector only runs at `text_penalty <= 0.70`). Camping in // Alaska's square "lost and found" sleeve reached a live hero slot that // way. Real press photos measured across the fixtures run 1.33-1.9, so - // the floor sits below every one of them; portrait crops fail it too, - // which is correct for a full-bleed banner. Trusted ticketing profiles + // the floor sits below every one of them. Trusted ticketing profiles // keep their existing exemption — that tier is identity-verified art. - if ratio < HERO_MIN_ASPECT && !trusted_ticketing_profile { + // + // PORTRAIT is admitted, though, because the guard is really an ALBUM-ART + // guard and sleeves are square: a 0.75-0.85 press portrait is not a sleeve. + // The app already crops tall sources to 16:9 around the detected faces + // (`hero_crop.rs`, recipe `vertical-yoloxnano-...-faces-first`), so these + // were being discarded one crate before the machinery built to use them. + // This is the only route to a hero for artists whose entire press corpus is + // portrait: Hana Vu (2000x2500, 1705x2129) and Nikki Nair (1080x1350, + // 1024x1365) both pass identity and died here. Text-heavy posters and + // flyers cannot sneak in this way — the text, flat-graphic and collage + // gates all run BEFORE this one. + let usable_portrait = + ratio <= HERO_PORTRAIT_MAX_ASPECT && w >= HERO_PORTRAIT_MIN_WIDTH; + if ratio < HERO_MIN_ASPECT && !usable_portrait && !trusted_ticketing_profile { if diagnostics { eprintln!( "HERO_REJECT\taspect\t{}x{}\tratio={:.2}\t{}",