rs_plugin_common_interfaces/domain/
episode.rs1use crate::domain::{
2 other_ids::OtherIds,
3 rs_ids::{ApplyRsIds, RsIds},
4 tools::rating_serializer,
5};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
10#[serde(rename_all = "camelCase")]
11pub struct Episode {
12 pub serie: String,
13 pub season: u32,
14 pub number: u32,
15
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub abs: Option<u32>,
18
19 pub name: Option<String>,
20 pub overview: Option<String>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub alt: Option<Vec<String>>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub airdate: Option<i64>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub duration: Option<u64>,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub params: Option<Value>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub imdb: Option<String>,
34 pub slug: Option<String>,
35 pub tmdb: Option<u64>,
36 pub trakt: Option<u64>,
37 pub tvdb: Option<u64>,
38 pub otherids: Option<OtherIds>,
39
40 #[serde(serialize_with = "rating_serializer")]
41 pub imdb_rating: Option<f32>,
42 pub imdb_votes: Option<u64>,
43 #[serde(serialize_with = "rating_serializer")]
44 pub trakt_rating: Option<f32>,
45 pub trakt_votes: Option<u64>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub watched: Option<i64>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub progress: Option<u64>,
51
52 #[serde(default)]
53 pub modified: u64,
54 #[serde(default)]
55 pub added: u64,
56
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub serie_name: Option<String>,
59}
60
61impl Episode {
62 pub fn id(&self) -> String {
63 format!("{}x{}x{}", self.serie, self.season, self.number)
64 }
65}
66
67impl From<Episode> for RsIds {
68 fn from(value: Episode) -> Self {
69 RsIds {
70 redseat: Some(value.id()),
71 trakt: value.trakt,
72 slug: value.slug,
73 tvdb: value.tvdb,
74 imdb: value.imdb,
75 tmdb: value.tmdb,
76 tvrage: None,
77 other_ids: value.otherids,
78 ..Default::default()
79 }
80 }
81}
82
83impl ApplyRsIds for Episode {
84 fn apply_rs_ids(&mut self, ids: &RsIds) {
85 if let Some(trakt) = ids.trakt {
86 self.trakt = Some(trakt);
87 }
88 if let Some(slug) = ids.slug.as_ref() {
89 self.slug = Some(slug.clone());
90 }
91 if let Some(tvdb) = ids.tvdb {
92 self.tvdb = Some(tvdb);
93 }
94 if let Some(imdb) = ids.imdb.as_ref() {
95 self.imdb = Some(imdb.clone());
96 }
97 if let Some(tmdb) = ids.tmdb {
98 self.tmdb = Some(tmdb);
99 }
100 if let Some(other_ids) = ids.other_ids.as_ref() {
101 self.otherids = Some(other_ids.clone());
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::Episode;
109 use crate::domain::{
110 other_ids::OtherIds,
111 rs_ids::{ApplyRsIds, RsIds},
112 };
113 use serde_json::json;
114
115 #[test]
116 fn episode_otherids_serializes_as_array_and_rejects_string() {
117 let episode = Episode {
118 serie: "serie-1".to_string(),
119 season: 1,
120 number: 1,
121 otherids: Some(OtherIds(vec!["tvmaze:ep-1".to_string()])),
122 ..Default::default()
123 };
124 let value = serde_json::to_value(&episode).unwrap();
125 assert_eq!(value.get("otherids"), Some(&json!(["tvmaze:ep-1"])));
126
127 let parsed: Episode = serde_json::from_value(json!({
128 "serie": "serie-1",
129 "season": 1,
130 "number": 1,
131 "otherids": ["foo:bar"]
132 }))
133 .unwrap();
134 assert_eq!(parsed.otherids, Some(OtherIds(vec!["foo:bar".to_string()])));
135
136 let invalid = serde_json::from_value::<Episode>(json!({
137 "serie": "serie-1",
138 "season": 1,
139 "number": 1,
140 "otherids": "foo:bar"
141 }));
142 assert!(invalid.is_err());
143 }
144
145 #[test]
146 fn episode_apply_rs_ids_updates_only_present_values() {
147 let mut episode = Episode {
148 serie: "serie".to_string(),
149 season: 1,
150 number: 1,
151 tmdb: Some(22),
152 ..Default::default()
153 };
154 let ids = RsIds {
155 trakt: Some(55),
156 tvdb: Some(66),
157 imdb: Some("tt0000066".to_string()),
158 slug: Some("episode-slug".to_string()),
159 other_ids: Some(OtherIds(vec!["tvmaze:ep-1".to_string()])),
160 ..Default::default()
161 };
162
163 episode.apply_rs_ids(&ids);
164
165 assert_eq!(episode.trakt, Some(55));
166 assert_eq!(episode.tvdb, Some(66));
167 assert_eq!(episode.imdb.as_deref(), Some("tt0000066"));
168 assert_eq!(episode.slug.as_deref(), Some("episode-slug"));
169 assert_eq!(episode.tmdb, Some(22));
170 assert_eq!(
171 episode.otherids,
172 Some(OtherIds(vec!["tvmaze:ep-1".to_string()]))
173 );
174 }
175}