1use color_eyre::eyre::Context;
2use serde::Deserialize;
3use serde::Serialize;
4
5use crate::video::Video;
6use crate::xml_feed::Feed;
7
8#[derive(Serialize, Deserialize, Debug, Clone, derive_builder::Builder)]
9pub struct Channel {
10 pub id: String,
11 pub title: String,
12 pub author: String,
13 pub url: String,
14 pub published: chrono::DateTime<chrono::Utc>,
15 pub videos: Option<Vec<Video>>,
16}
17
18impl Channel {
19 pub async fn new(id: &str) -> Self {
20 let uri = format!(
21 "https://www.youtube.com/feeds/videos.xml?channel_id={}",
22 &id
23 );
24
25 let feed: Feed = Feed::new(&uri)
26 .await
27 .wrap_err("Failed to create Channel.")
28 .unwrap();
29 feed.into()
30 }
31}
32
33impl From<Feed> for Channel {
34 fn from(f: Feed) -> Self {
35 Self {
36 id: f.channel_id,
37 title: f.title,
38 author: f.author,
39 url: f.url,
40 published: f.published,
41 videos: f.videos,
42 }
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[tokio::test]
51 async fn test_linus() {
52 let linus = Channel::new("UCXuqSBlHAE6Xw-yeJA0Tunw").await;
53 assert_eq!(linus.id, "UCXuqSBlHAE6Xw-yeJA0Tunw");
54 assert_eq!(linus.title, "Linus Tech Tips");
55 }
56
57 #[tokio::test]
58 #[should_panic]
59 async fn test_linus_missing_playlist() {
60 let linus = Feed::new(
61 "https://www.youtube.com/feeds/videos.xml?channel_id=UCXuqSBlHAE6Xw-yeJA0Tunw",
62 )
63 .await
64 .unwrap();
65 let _panic = linus.playlist_id.unwrap();
66 }
67}