1use std::collections::HashMap;
2use std::str::FromStr;
3
4use serde::{Deserialize, Deserializer};
5use time::macros::format_description;
6use time::Date;
7
8use crate::append::appendable;
9use crate::endpoints::credit::Credits;
10use crate::endpoints::keyword::{MovieKeywords, TvKeywords};
11use crate::endpoints::movie::ReleaseDates;
12use crate::{Backdrop, CountryCode, Language, Logo, Poster};
13
14#[derive(Debug, Clone, Deserialize)]
15pub struct Video {
16 pub id: String,
17 pub key: String,
18 pub name: String,
19 pub site: String,
20 #[serde(rename = "type")]
21 pub kind: String,
22 pub size: u32,
23 pub official: bool,
24}
25
26#[derive(Debug, Clone, Deserialize)]
27pub struct Videos {
28 pub results: Vec<Video>,
29}
30
31#[derive(Debug, Clone, Deserialize)]
32pub struct Image<K> {
33 #[serde(rename = "file_path")]
34 pub file: K,
35 pub width: u32,
36 pub height: u32,
37 pub aspect_ratio: f64,
38 pub vote_average: f64,
39 pub vote_count: u32,
40 #[serde(rename = "iso_639_1")]
42 pub language: Option<Language>,
43}
44
45#[derive(Debug, Clone, Default, Deserialize)]
46#[serde(default)]
47pub struct Images {
48 pub backdrops: Vec<Image<Backdrop>>,
49 pub posters: Vec<Image<Poster>>,
50 pub logos: Vec<Image<Logo>>,
51}
52
53#[derive(Debug, Clone, Deserialize)]
54pub struct ExternalIds {
55 pub imdb_id: Option<String>,
56 pub wikidata_id: Option<String>,
57 pub tvdb_id: Option<u64>,
58 pub facebook_id: Option<String>,
59 pub instagram_id: Option<String>,
60 pub twitter_id: Option<String>,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
65#[serde(rename_all = "snake_case")]
66pub enum MediaType {
67 Movie,
68 Tv,
69}
70
71#[derive(Debug, Clone, Deserialize)]
73pub struct StatusResponse {
74 pub success: bool,
75 pub status_code: i32,
76 pub status_message: String,
77}
78
79#[derive(Debug, Clone, Deserialize)]
80pub struct Translation {
81 #[serde(rename = "iso_3166_1")]
82 pub country: CountryCode,
83 #[serde(rename = "iso_639_1")]
84 pub language: Language,
85 pub name: String,
86 pub english_name: String,
87 pub data: TranslationData,
88}
89
90#[derive(Debug, Clone, Deserialize)]
92pub struct TranslationData {
93 pub title: Option<String>,
94 pub name: Option<String>,
95 pub overview: Option<String>,
96 #[serde(rename = "homepage")]
97 pub website: Option<String>,
98 pub tagline: Option<String>,
99}
100
101impl TranslationData {
102 pub fn display_title(&self) -> Option<&str> {
104 self.title.as_deref().or(self.name.as_deref())
105 }
106}
107
108#[derive(Debug, Clone, Deserialize)]
109pub struct Translations {
110 pub translations: Vec<Translation>,
111}
112
113#[derive(Debug, Clone, Deserialize)]
114pub struct AlternativeTitle {
115 #[serde(rename = "iso_3166_1")]
116 pub country: CountryCode,
117 #[serde(rename = "title")]
118 pub name: String,
119 #[serde(rename = "type")]
120 pub kind: String,
121}
122
123#[derive(Debug, Clone, Deserialize)]
125#[serde(from = "RawAlternativeTitles")]
126pub struct AlternativeTitles {
127 pub titles: Vec<AlternativeTitle>,
128}
129
130#[derive(Deserialize)]
131struct RawAlternativeTitles {
132 titles: Option<Vec<AlternativeTitle>>,
133 results: Option<Vec<AlternativeTitle>>,
134}
135
136impl From<RawAlternativeTitles> for AlternativeTitles {
137 fn from(raw: RawAlternativeTitles) -> Self {
138 Self {
139 titles: raw.titles.or(raw.results).unwrap_or_default(),
140 }
141 }
142}
143
144#[derive(Debug, Clone, Deserialize)]
145pub struct AlternativeName {
146 pub name: String,
147 #[serde(rename = "type")]
148 pub kind: String,
149}
150
151#[derive(Debug, Clone, Deserialize)]
152pub struct ChangeItem {
153 pub id: String,
154 pub action: String,
155 pub time: String,
156 #[serde(rename = "iso_639_1")]
157 pub language: Option<Language>,
158 #[serde(rename = "iso_3166_1")]
159 pub country: Option<CountryCode>,
160 pub value: serde_json::Value,
162 pub original_value: Option<serde_json::Value>,
163}
164
165#[derive(Debug, Clone, Deserialize)]
166pub struct Change {
167 pub key: String,
168 pub items: Vec<ChangeItem>,
169}
170
171#[derive(Debug, Clone, Deserialize)]
172pub struct Changes {
173 pub changes: Vec<Change>,
174}
175
176#[derive(Debug, Clone, Copy, Deserialize)]
178#[serde(untagged)]
179pub enum Rated {
180 Unrated(bool),
181 Rated { value: f64 },
182}
183
184impl Rated {
185 pub fn value(&self) -> Option<f64> {
186 match self {
187 Self::Unrated(_) => None,
188 Self::Rated { value } => Some(*value),
189 }
190 }
191}
192
193#[derive(Debug, Clone, Deserialize)]
194pub struct AccountStates {
195 pub favorite: bool,
196 pub watchlist: bool,
197 pub rated: Rated,
198}
199
200#[derive(Debug, Clone, Deserialize)]
201pub struct WatchProvider {
202 pub provider_id: u64,
203 pub provider_name: String,
204 #[serde(rename = "logo_path")]
205 pub logo: Option<Logo>,
206 pub display_priority: Option<u32>,
207}
208
209#[derive(Debug, Clone, Default, Deserialize)]
211#[serde(default)]
212pub struct CountryProviders {
213 pub link: Option<String>,
214 pub flatrate: Vec<WatchProvider>,
215 pub rent: Vec<WatchProvider>,
216 pub buy: Vec<WatchProvider>,
217 pub ads: Vec<WatchProvider>,
218 pub free: Vec<WatchProvider>,
219}
220
221#[derive(Debug, Clone, Deserialize)]
223pub struct WatchProviders {
224 pub results: HashMap<String, CountryProviders>,
225}
226
227appendable! {
228 Credits,
229 ReleaseDates,
230 MovieKeywords,
231 TvKeywords,
232 ExternalIds,
233 Videos,
234 Images,
235 Translations,
236 AlternativeTitles,
237 Changes,
238 AccountStates,
239 WatchProviders,
240}
241
242#[derive(Debug, Clone, Deserialize)]
244pub struct Page<T> {
245 pub page: u32,
246 pub results: Vec<T>,
247 pub total_pages: u32,
248 pub total_results: u32,
249}
250
251impl<T> Page<T> {
252 pub fn has_next(&self) -> bool {
253 self.page < self.total_pages
254 }
255}
256
257pub(crate) fn opt_language<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Language>, D::Error> {
259 Ok(Option::<String>::deserialize(d)?.and_then(|s| Language::from_str(&s).ok()))
260}
261
262pub(crate) fn language_list<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<Language>, D::Error> {
264 Ok(Vec::<String>::deserialize(d)?
265 .into_iter()
266 .filter_map(|s| Language::from_str(&s).ok())
267 .collect())
268}
269
270pub(crate) fn opt_date<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Date>, D::Error> {
272 match Option::<String>::deserialize(d)?.filter(|s| !s.is_empty()) {
273 Some(s) => Date::parse(&s, format_description!("[year]-[month]-[day]"))
274 .map(Some)
275 .map_err(serde::de::Error::custom),
276 None => Ok(None),
277 }
278}