use super::tvmaze_types::{TvMazeEpisode, TvMazeShow};
use super::{Episode, MetadataProvider, MetadataRetrievalError, Season, TVSeries};
use std::collections::HashMap;
pub(crate) struct TvMazeProvider {
client: reqwest::blocking::Client,
base_url: String,
}
impl TvMazeProvider {
pub fn new() -> Self {
Self {
client: reqwest::blocking::Client::new(),
base_url: "https://api.tvmaze.com".to_string(),
}
}
fn convert_episode(tvmaze_episode: TvMazeEpisode) -> Episode {
Episode {
season_number: tvmaze_episode.season,
episode_number: tvmaze_episode.number,
name: tvmaze_episode.name.unwrap_or_else(|| "Unknown".to_string()),
summary: tvmaze_episode
.summary
.map(|s| nanohtml2text::html2text(&s).trim().to_string())
.unwrap_or_default(),
}
}
fn convert_to_series(
tvmaze_show: TvMazeShow,
season_filter: Option<Vec<usize>>,
) -> Result<TVSeries, MetadataRetrievalError> {
let episodes = tvmaze_show
.embedded
.ok_or_else(|| {
MetadataRetrievalError::InvalidData("No episodes found in API response".to_string())
})?
.episodes;
let mut seasons_map: HashMap<usize, Vec<Episode>> = HashMap::new();
for tvmaze_episode in episodes {
if let Some(ref filter) = season_filter {
if !filter.contains(&tvmaze_episode.season) {
continue;
}
}
seasons_map
.entry(tvmaze_episode.season)
.or_insert_with(Vec::new)
.push(Self::convert_episode(tvmaze_episode));
}
let mut seasons: Vec<Season> = seasons_map
.into_iter()
.map(|(season_number, mut episodes)| {
episodes.sort_by_key(|e| e.episode_number);
Season {
season_number,
episodes,
}
})
.collect();
seasons.sort_by_key(|s| s.season_number);
Ok(TVSeries {
name: tvmaze_show.name,
seasons,
})
}
}
impl MetadataProvider for TvMazeProvider {
fn fetch_series(
&self,
series_name: &str,
season_numbers: Option<Vec<usize>>,
) -> Result<TVSeries, MetadataRetrievalError> {
let url = format!("{}/singlesearch/shows", self.base_url);
let response = self
.client
.get(&url)
.query(&[("q", series_name), ("embed", "episodes")])
.send()
.map_err(|e| MetadataRetrievalError::RequestError(e.to_string()))?;
if response.status() == 404 {
return Err(MetadataRetrievalError::SeriesNotFound(
series_name.to_string(),
));
}
if !response.status().is_success() {
return Err(MetadataRetrievalError::RequestError(format!(
"HTTP {} {}",
response.status().as_u16(),
response.status().canonical_reason().unwrap_or("Unknown")
)));
}
let tvmaze_show: TvMazeShow = response
.json()
.map_err(|e| MetadataRetrievalError::ParseError(e.to_string()))?;
Self::convert_to_series(tvmaze_show, season_numbers)
}
}