1use crate::types::{Enclosure, Entry, Tag};
29
30pub const MEDIA_NAMESPACE: &str = "http://search.yahoo.com/mrss/";
32
33#[derive(Debug, Clone, Default, PartialEq)]
60#[allow(clippy::derive_partial_eq_without_eq)]
61pub struct MediaContent {
62 pub url: String,
69 pub type_: Option<String>,
71 pub medium: Option<String>,
73 pub file_size: Option<u64>,
75 pub bitrate: Option<u32>,
77 pub framerate: Option<f32>,
79 pub width: Option<u32>,
81 pub height: Option<u32>,
83 pub duration: Option<u32>,
85 pub expression: Option<String>,
91 pub is_default: Option<bool>,
93}
94
95#[derive(Debug, Clone, Default, PartialEq, Eq)]
119pub struct MediaThumbnail {
120 pub url: String,
127 pub width: Option<u32>,
129 pub height: Option<u32>,
131 pub time: Option<String>,
135}
136
137pub fn handle_entry_element(element: &str, text: &str, entry: &mut Entry) {
148 match element {
149 "title" => {
150 if entry.title.is_none() {
151 entry.title = Some(text.to_string());
152 }
153 }
154 "description" => {
155 if entry.summary.is_none() {
156 entry.summary = Some(text.to_string());
157 }
158 }
159 "keywords" => {
160 for keyword in text.split(',') {
162 let keyword = keyword.trim();
163 if !keyword.is_empty() {
164 entry.tags.push(Tag::new(keyword));
165 }
166 }
167 }
168 "category" => {
169 if !text.is_empty() {
170 entry.tags.push(Tag::new(text));
171 }
172 }
173 _ => {
174 }
178 }
179}
180
181pub fn media_content_to_enclosure(content: &MediaContent) -> Enclosure {
204 Enclosure {
205 url: content.url.clone().into(),
206 enclosure_type: content.type_.as_ref().map(|t| t.clone().into()),
207 length: content.file_size,
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_media_title() {
217 let mut entry = Entry::default();
218 handle_entry_element("title", "Video Title", &mut entry);
219
220 assert_eq!(entry.title.as_deref(), Some("Video Title"));
221 }
222
223 #[test]
224 fn test_media_description() {
225 let mut entry = Entry::default();
226 handle_entry_element("description", "Video description", &mut entry);
227
228 assert_eq!(entry.summary.as_deref(), Some("Video description"));
229 }
230
231 #[test]
232 fn test_media_keywords() {
233 let mut entry = Entry::default();
234 handle_entry_element("keywords", "tech, programming, rust", &mut entry);
235
236 assert_eq!(entry.tags.len(), 3);
237 assert_eq!(entry.tags[0].term, "tech");
238 assert_eq!(entry.tags[1].term, "programming");
239 assert_eq!(entry.tags[2].term, "rust");
240 }
241
242 #[test]
243 fn test_media_keywords_with_spaces() {
244 let mut entry = Entry::default();
245 handle_entry_element("keywords", " tech , programming ", &mut entry);
246
247 assert_eq!(entry.tags.len(), 2);
248 assert_eq!(entry.tags[0].term, "tech");
249 assert_eq!(entry.tags[1].term, "programming");
250 }
251
252 #[test]
253 fn test_media_category() {
254 let mut entry = Entry::default();
255 handle_entry_element("category", "Technology", &mut entry);
256
257 assert_eq!(entry.tags.len(), 1);
258 assert_eq!(entry.tags[0].term, "Technology");
259 }
260
261 #[test]
262 fn test_media_content_default() {
263 let content = MediaContent::default();
264 assert!(content.url.is_empty());
265 assert!(content.type_.is_none());
266 assert!(content.medium.is_none());
267 assert!(content.file_size.is_none());
268 assert!(content.bitrate.is_none());
269 assert!(content.framerate.is_none());
270 assert!(content.width.is_none());
271 assert!(content.height.is_none());
272 assert!(content.duration.is_none());
273 assert!(content.expression.is_none());
274 assert!(content.is_default.is_none());
275 }
276
277 #[test]
278 fn test_media_content_full_attributes() {
279 let content = MediaContent {
280 url: "https://example.com/video.mp4".to_string(),
281 type_: Some("video/mp4".to_string()),
282 medium: Some("video".to_string()),
283 file_size: Some(10_485_760), bitrate: Some(1500), framerate: Some(30.0),
286 width: Some(1920),
287 height: Some(1080),
288 duration: Some(600), expression: Some("full".to_string()),
290 is_default: Some(true),
291 };
292
293 assert_eq!(content.url, "https://example.com/video.mp4");
294 assert_eq!(content.type_.as_deref(), Some("video/mp4"));
295 assert_eq!(content.medium.as_deref(), Some("video"));
296 assert_eq!(content.file_size, Some(10_485_760));
297 assert_eq!(content.bitrate, Some(1500));
298 assert_eq!(content.framerate, Some(30.0));
299 assert_eq!(content.width, Some(1920));
300 assert_eq!(content.height, Some(1080));
301 assert_eq!(content.duration, Some(600));
302 assert_eq!(content.expression.as_deref(), Some("full"));
303 assert_eq!(content.is_default, Some(true));
304 }
305
306 #[test]
307 fn test_media_content_audio() {
308 let content = MediaContent {
309 url: "https://example.com/audio.mp3".to_string(),
310 type_: Some("audio/mpeg".to_string()),
311 medium: Some("audio".to_string()),
312 file_size: Some(5_242_880), bitrate: Some(128), duration: Some(180), ..Default::default()
316 };
317
318 assert_eq!(content.medium.as_deref(), Some("audio"));
319 assert_eq!(content.bitrate, Some(128));
320 assert!(content.width.is_none());
321 assert!(content.height.is_none());
322 assert!(content.framerate.is_none());
323 }
324
325 #[test]
326 fn test_media_content_image() {
327 let content = MediaContent {
328 url: "https://example.com/image.jpg".to_string(),
329 type_: Some("image/jpeg".to_string()),
330 medium: Some("image".to_string()),
331 width: Some(800),
332 height: Some(600),
333 ..Default::default()
334 };
335
336 assert_eq!(content.medium.as_deref(), Some("image"));
337 assert_eq!(content.width, Some(800));
338 assert_eq!(content.height, Some(600));
339 assert!(content.duration.is_none());
340 }
341
342 #[test]
343 fn test_media_content_expression_variants() {
344 let full = MediaContent {
345 expression: Some("full".to_string()),
346 ..Default::default()
347 };
348 let sample = MediaContent {
349 expression: Some("sample".to_string()),
350 ..Default::default()
351 };
352 let nonstop = MediaContent {
353 expression: Some("nonstop".to_string()),
354 ..Default::default()
355 };
356
357 assert_eq!(full.expression.as_deref(), Some("full"));
358 assert_eq!(sample.expression.as_deref(), Some("sample"));
359 assert_eq!(nonstop.expression.as_deref(), Some("nonstop"));
360 }
361
362 #[test]
363 fn test_media_thumbnail_default() {
364 let thumbnail = MediaThumbnail::default();
365 assert!(thumbnail.url.is_empty());
366 assert!(thumbnail.width.is_none());
367 assert!(thumbnail.height.is_none());
368 assert!(thumbnail.time.is_none());
369 }
370
371 #[test]
372 fn test_media_thumbnail_full_attributes() {
373 let thumbnail = MediaThumbnail {
374 url: "https://example.com/thumb.jpg".to_string(),
375 width: Some(640),
376 height: Some(480),
377 time: Some("12:05:01.123".to_string()),
378 };
379
380 assert_eq!(thumbnail.url, "https://example.com/thumb.jpg");
381 assert_eq!(thumbnail.width, Some(640));
382 assert_eq!(thumbnail.height, Some(480));
383 assert_eq!(thumbnail.time.as_deref(), Some("12:05:01.123"));
384 }
385
386 #[test]
387 fn test_media_thumbnail_without_time() {
388 let thumbnail = MediaThumbnail {
389 url: "https://example.com/poster.jpg".to_string(),
390 width: Some(1920),
391 height: Some(1080),
392 time: None,
393 };
394
395 assert_eq!(thumbnail.width, Some(1920));
396 assert_eq!(thumbnail.height, Some(1080));
397 assert!(thumbnail.time.is_none());
398 }
399
400 #[test]
401 fn test_media_content_to_enclosure() {
402 let content = MediaContent {
403 url: "https://example.com/video.mp4".to_string(),
404 type_: Some("video/mp4".to_string()),
405 file_size: Some(1_024_000),
406 width: Some(1920), height: Some(1080),
408 ..Default::default()
409 };
410
411 let enclosure = media_content_to_enclosure(&content);
412
413 assert_eq!(enclosure.url, "https://example.com/video.mp4");
414 assert_eq!(enclosure.enclosure_type.as_deref(), Some("video/mp4"));
415 assert_eq!(enclosure.length, Some(1_024_000));
416 }
417
418 #[test]
419 fn test_media_content_to_enclosure_minimal() {
420 let content = MediaContent {
421 url: "https://example.com/file.bin".to_string(),
422 ..Default::default()
423 };
424
425 let enclosure = media_content_to_enclosure(&content);
426
427 assert_eq!(enclosure.url, "https://example.com/file.bin");
428 assert!(enclosure.enclosure_type.is_none());
429 assert!(enclosure.length.is_none());
430 }
431
432 #[test]
433 fn test_empty_keywords() {
434 let mut entry = Entry::default();
435 handle_entry_element("keywords", "", &mut entry);
436
437 assert!(entry.tags.is_empty());
438 }
439
440 #[test]
441 fn test_keywords_with_empty_values() {
442 let mut entry = Entry::default();
443 handle_entry_element("keywords", "tech, , programming", &mut entry);
444
445 assert_eq!(entry.tags.len(), 2);
446 assert_eq!(entry.tags[0].term, "tech");
447 assert_eq!(entry.tags[1].term, "programming");
448 }
449}