Skip to main content

yt_feed_xml/
playlist.rs

1use color_eyre::eyre::Context;
2use serde::Deserialize;
3use serde::Serialize;
4
5use crate::{video::Video, xml_feed::Feed};
6
7#[derive(Serialize, Deserialize, Debug, Clone, derive_builder::Builder)]
8pub struct Playlist {
9    pub id: String,
10    pub title: String,
11    pub author: String,
12    pub channel_id: String,
13    pub url: String,
14    pub published: chrono::DateTime<chrono::Utc>,
15    pub videos: Option<Vec<Video>>,
16}
17
18impl Playlist {
19    pub async fn new(id: &str) -> Self {
20        let uri = format!(
21            "https://www.youtube.com/feeds/videos.xml?playlist_id={}",
22            &id
23        );
24
25        let feed: Feed = Feed::new(&uri)
26            .await
27            .wrap_err("Failed to create Feed from given ID.")
28            .unwrap();
29
30        feed.try_into()
31            .wrap_err("Failed to convert Feed to Playlist.")
32            .unwrap()
33    }
34}
35
36impl TryFrom<Feed> for Playlist {
37    type Error = color_eyre::Report;
38
39    fn try_from(f: Feed) -> Result<Self, Self::Error> {
40        let id = f.playlist_id.ok_or_else(|| {
41            color_eyre::eyre::eyre!("Could not find playlist id for given Playlist.")
42        })?;
43        Ok(Self {
44            id: id.clone(),
45            title: f.title,
46            author: f.author,
47            channel_id: f.channel_id,
48            url: format!("https://www.youtube.com/playlist?list={id}"),
49            published: f.published,
50            videos: f.videos,
51        })
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[tokio::test]
60    async fn test_sinclair_lore_playlist() {
61        let sinclair_lore_va_masq = Playlist::new("PLOIA4n5j7KcYj52DQ9orEBJDA9IqBTB3I").await;
62        assert_eq!(
63            sinclair_lore_va_masq.id,
64            "PLOIA4n5j7KcYj52DQ9orEBJDA9IqBTB3I"
65        );
66        assert_eq!(
67            sinclair_lore_va_masq.url,
68            "https://www.youtube.com/playlist?list=PLOIA4n5j7KcYj52DQ9orEBJDA9IqBTB3I"
69        );
70        assert_eq!(sinclair_lore_va_masq.channel_id, "UCH6IMeS2HVdTJZU4BlN6ODg");
71        assert_eq!(
72            sinclair_lore_va_masq.title,
73            "Vampire the Masquerade ► Down Under by Night | Actual Play"
74        );
75    }
76}