1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::collections::HashMap;

use futures::stream::StreamExt;

use crate::EpisodeLink;
use crate::Error;
use crate::Title;
use crate::TitleType;
use crate::get_episodes_filtered;
use crate::stream_titles_filtered;

pub async fn get_shows_filtered(ids: &[u64]) -> Result<Vec<Show>, Error> {
	let mut episode_links = get_episodes_filtered(ids).await?;
	let all_ids = {
		let mut v = Vec::from(ids);
		v.extend(episode_links.iter().map(|(id, _)| id));
		v
	};
	let mut episodes_by_show = HashMap::new();
	let mut shows = Vec::with_capacity(ids.len());
	let mut stream = stream_titles_filtered(&all_ids).await?;
	while let Some(result) = stream.next().await {
		let title = result?;
		if let Some(link) = episode_links.remove(&title.imdb_id) {
			let series_id = link.series_imdb_id;
			if let Some(result) = Episode::from_title_and_link(title, link) {
				let episode = result?;
				let episodes = episodes_by_show.entry(series_id).or_insert_with(|| Vec::new());
				episodes.push(episode);
			}
			continue;
		}
		shows.push(match Show::from_title(&title) {
			Some(Ok(v)) => v,
			Some(Err(e)) => return Err(e),
			None => continue
		});
	}
	for show in shows.iter_mut() {
		show.episodes = match episodes_by_show.remove(&show.imdb_id) {
			Some(v) => v,
			None => Vec::new()
		};
	}
	Ok(shows)
}

#[derive(Debug)]
pub struct Show {
	pub imdb_id: u64,
	pub title: String,
	pub is_adult: bool,
	pub start_year: u16,
	pub end_year: Option<u16>,
	pub runtime_minutes: Option<u16>,
	pub genres: Vec<String>,
	pub episodes: Vec<Episode>
}

impl Show {
	pub(crate) fn from_title(input: &Title) -> Option<Result<Self, Error>> {
		match input.title_type {
			TitleType::TVSeries => Some(Ok(Self{
				imdb_id: input.imdb_id,
				title: input.primary_title.clone(),
				is_adult: input.is_adult != 0,
				start_year: match input.start_year {
					Some(v) => v,
					None => return Some(Err(Error::YearMissing.into()))
				},
				end_year: input.end_year,
				runtime_minutes: input.runtime_minutes,
				genres: input.genres.clone(),
				episodes: Vec::new()
			})),
			_ => None
		}
	}

	fn from_wrapped_title(input: Result<Title, Error>) -> Option<Result<Self, Error>> {
		match input {
			Ok(t) => Self::from_title(&t),
			Err(e) => Some(Err(e))
		}
	}
}

#[derive(Debug)]
pub struct Episode {
	pub season: u16,
	pub episode: u16,
	pub imdb_id: u64,
	pub title: String,
	pub runtime_minutes: Option<u16>
}

impl Episode {
	pub(crate) fn from_title_and_link(title: Title, link: EpisodeLink) -> Option<Result<Self, Error>> {
		match title.title_type {
			TitleType::Episode => Some(Ok(Self{
				season: match link.season {
					Some(v) => v,
					None => return Some(Err(Error::SeasonMissing.into()))
				},
				episode: match link.episode {
					Some(v) => v,
					None => return Some(Err(Error::EpisodeMissing.into()))
				},
				imdb_id: title.imdb_id,
				title: title.primary_title,
				runtime_minutes: title.runtime_minutes
			})),
			_ => None
		}
	}
}