nako-metadata-scraper 0.1.0-alpha.2

Official Nako metadata scraper Addon Sidecar.
Documentation
use crate::engine::{
    MetadataQuery, ProviderMetadataCandidate,
    av::{AvNumberRoute, AvNumberSource, facts_from_query, facts_from_text},
};

use super::{
    Fc2MetadataProvider,
    parser::{article_id_from_av_number, parse_detail_page},
};

impl<T> Fc2MetadataProvider<T>
where
    T: crate::providers::http_runtime::ProviderHttpTransport,
{
    pub(super) async fn suggest_candidates(
        &self,
        query: &MetadataQuery,
    ) -> anyhow::Result<Vec<ProviderMetadataCandidate>> {
        if let Some(article_id) = explicit_fc2_id(query) {
            let detail_url = self.detail_url(&article_id);
            let detail = self.render(detail_url.clone()).await?;
            let av_facts =
                facts_from_text(&format!("FC2-{article_id}"), AvNumberSource::ExternalId)
                    .expect("FC2 article ID direct lookup builds a parseable AV number");
            let Some(detail) = parse_detail_page(&detail.html, &detail_url, av_facts) else {
                return Ok(Vec::new());
            };
            return Ok(vec![detail.into_candidate(query)]);
        }

        let Some(av_facts) = facts_from_query(query) else {
            return Ok(Vec::new());
        };
        if av_facts.route != AvNumberRoute::Fc2 {
            return Ok(Vec::new());
        }

        let Some(article_id) = article_id_from_av_number(&av_facts.number) else {
            return Ok(Vec::new());
        };
        let detail_url = self.detail_url(&article_id);
        let detail = self.render(detail_url.clone()).await?;
        let Some(detail) = parse_detail_page(&detail.html, &detail_url, av_facts) else {
            return Ok(Vec::new());
        };

        Ok(vec![detail.into_candidate(query)])
    }
}

fn explicit_fc2_id(query: &MetadataQuery) -> Option<String> {
    query
        .external_ids
        .iter()
        .find(|external_id| external_id.provider.eq_ignore_ascii_case("fc2"))
        .map(|external_id| external_id.value.trim())
        .filter(|value| !value.is_empty())
        .map(|value| value.trim_start_matches("FC2-"))
        .filter(|value| value.chars().all(|character| character.is_ascii_digit()))
        .map(str::to_owned)
}