ytitler/
playlist.rs

1use std::path::PathBuf;
2use std::io;
3use std::collections::HashSet;
4use std::fs::File;
5use std::io::prelude::*;
6use std::string::String;
7use std::vec::Vec;
8
9use crate::scrapper;
10
11
12pub struct Playlist {
13    pub urls: Vec<String>
14}
15
16impl Playlist {
17
18    pub fn try_from(path: &PathBuf) -> Result<Self, io::Error> {
19
20        // Open and load the file.
21        let f = File::open(path)?;
22        let reader = io::BufReader::new(f);
23
24        // Read lines and filter the URLs.
25        let urls = reader.lines()
26            .filter_map(|line| {
27                let line = line.ok().unwrap();
28
29                if !line.starts_with('#') && line.starts_with("http") {
30                    return Some(line.trim().to_string());
31                }
32
33                None
34            })
35            .collect::<Vec<String>>();
36
37        // Return new instance.
38        Ok(Playlist {
39            urls,
40        })
41    }
42
43    pub fn save(&self, target: PathBuf, videos: &[scrapper::Video]) -> Result<(), io::Error> {
44
45        fn format_title(video: &scrapper::Video) -> String {
46            format!(
47                "({} | {}) - {}",
48                video.published, video.channel, video.title.content,
49            )
50        }
51
52        let mut f = File::create(target)?;
53
54        // Write header.
55        f.write_all(b"# Created by YTitler\n")?;
56        f.write_all(b"# See: https://gitlab.com/n1_/ytitler\n\n")?;
57        f.write_all(b"#EXTM3U\n")?;
58
59        // Write content.
60        for v in videos {
61
62            f.write_fmt(format_args!(
63                "#EXTINF:{},{}\n",
64                v.duration.as_secs(),
65                format_title(v)
66            ))?;
67            f.write_fmt(format_args!("{}\n", v.url))?;
68        }
69
70        f.sync_all()?;
71
72        Ok(())
73    }
74
75    pub fn remove_duplicities(&mut self) {
76        // Walks thru vector and with help of
77        // hash set determines which items are
78        // already in the vector and which are
79        // not. Preserves order of the vector
80        // items.
81        let mut set = HashSet::new();
82        self.urls.retain(|i| set.insert(i.clone()));
83    }
84
85    pub fn to_chunks(&self) -> Vec<Vec<String>> {
86
87        self.urls.chunks(10).map(|s| s.to_vec()).collect()
88    }
89}