Files
proton-stream/crates/pstr-meta/src/provider.rs
T
nvrl 4c80076762 fix(core,meta,app,android,cli): number episodes the way releases do
AniList's per-season lookup searched for "<title> 2nd Season" and the
scorer picked the base entry again, so Attack on Titan seasons 2-4 and
Jigokuraku season 2 were captioned with season one's names. Episode
names came from streamingEpisodes, which is partial and numbered however
a streaming site counted. Bleach S17 and Mushishi S02/S03 had none.

AniList now walks SEQUEL relations from the match and takes each
entry's episodes from ani.zip, which carries TVDB's season, number and
absolute number. An episode row keeps all three plus its place in the
AniList entry. EpisodeGuide::get matches whichever numbering a file
uses and never answers from another season.

Episodes and fanart are stored as one enrichment. The episode list is
replaced even when empty, and the enrichment is recorded as asked, so a
film is not re-asked every run. Storing a different match drops the old
entry's episode rows. AniList's 4.75:1 bannerImage is no longer stored
as the 16:9 backdrop; ani.zip's 1920x1080 fanart is used instead.

Schema v9 adds the columns and deletes the AniList episode rows and
banners. Matches are kept. Desktop, Android and the CLI now share
service::plan and MetadataService::work. The settings text names
ani.zip. Audit and roadmap in docs/METADATA.md; bug entry B61.
2026-10-04 00:47:41 +02:00

45 lines
1.8 KiB
Rust

//! What every metadata source has to be able to do.
use pstr_core::library::Title;
use pstr_core::metadata::{Enrichment, ProviderId, TitleMetadata};
use crate::error::Result;
use crate::matching::{Candidate, Query};
/// A source of titles.
///
/// Deliberately narrow: search, and enriching something search already
/// matched. Both are requests to a third party, and neither happens until the
/// viewer has turned enrichment on — but note what the second one does *not*
/// need: it takes the id search returned, so it sends nothing about the library
/// it did not already learn from the match.
///
/// Not `#[async_trait]`: this is only ever used through a concrete type or an
/// enum, never as `dyn Provider`, so the native `async fn` costs nothing here.
pub trait Provider: Send + Sync {
fn id(&self) -> ProviderId;
/// Everything the provider thinks `query` might be, in its own order.
///
/// Scoring is not the provider's job — it returns candidates and
/// [`crate::matching::best`] decides. That is what keeps the "wrong poster"
/// threshold in one place rather than one per provider.
fn search(
&self,
query: &Query,
) -> impl std::future::Future<Output = Result<Vec<Candidate>>> + Send;
/// The episodes, and any art the search answer lacked, of `found` — the
/// entry `title` matched.
///
/// `title` is there for its files: a provider that files each sequel as a
/// separate entry (AniList) has to know which seasons the library holds to
/// know how far along the franchise to look. An empty episode list is a
/// real answer — a film has none — and the caller records it.
fn enrich(
&self,
title: &Title,
found: &TitleMetadata,
) -> impl std::future::Future<Output = Result<Enrichment>> + Send;
}