blob_dl/
analyzer.rs

1use url::Url;
2use dialoguer::console::Term;
3use dialoguer::{theme::ColorfulTheme, Select};
4
5use crate::error::{BlobdlError, BlobResult};
6
7#[derive(Debug, PartialOrd, PartialEq, Clone)]
8pub enum DownloadOption {
9    /// If the url refers to a video in a playlist and the user only wants to download the single video, YtVideo's value is the video's index in the playlist
10    YtVideo(usize),
11    YtPlaylist,
12}
13
14/// Analyzes the url provided by the user and deduces whether it
15/// refers to a youtube video or playlist
16pub fn analyze_url(command_line_url: &str) -> BlobResult<DownloadOption> {
17    return if let Ok(url) = Url::parse(command_line_url) {
18        if let Some(domain_name) = url.domain() {
19            // All youtube-related urls have "youtu" in them
20            if domain_name.contains("youtu") {
21                inspect_yt_url(url)
22            } else {
23                // The url isn't from youtube
24                Err(BlobdlError::UnsupportedWebsite)
25            }
26        } else {
27            Err(BlobdlError::DomainNotFound)
28        }
29    } else {
30        Err(BlobdlError::UrlParsingError)
31    }
32}
33
34/// Given a youtube url determines whether it refers to a video/playlist
35fn inspect_yt_url(yt_url: Url) -> BlobResult<DownloadOption> {
36    if let Some(query) = yt_url.query() {
37        if query.contains("&index=") {
38            // This video is part of a youtube playlist
39            let term = Term::buffered_stderr();
40
41            // Ask the user whether they want to download the whole playlist or just the video
42            let user_selection = Select::with_theme(&ColorfulTheme::default())
43                .with_prompt("The url refers to a video in a playlist, which do you want to download?")
44                .default(0)
45                .items(&["Only the video", "The whole playlist"])
46                .interact_on(&term)?;
47
48            return match user_selection {
49                0 => {
50                    let index = if let Some(index_location) = query.find("&index=") {
51                        let slice = &query[index_location + "&index=".len() ..];
52
53                        if let Some(second_ampersand_location) = slice.find('&') {
54                            // There are url parameters after &index=..
55                             &slice[..second_ampersand_location]
56                        } else {
57                            slice
58                        }
59                    } else {
60                        panic!("url has &index= but doesn't provide a numerical index")
61                    };
62
63                    if let Ok(parsed) = index.parse() {
64                        Ok(DownloadOption::YtVideo(parsed))
65                    } else {
66                        Err(BlobdlError::UrlIndexParsingError)
67                    }
68                }
69
70                _ => Ok(DownloadOption::YtPlaylist),
71            };
72        }
73        if yt_url.path().contains("playlist") || query.contains("list"){
74            return Ok(DownloadOption::YtPlaylist);
75        }
76
77        // This url is probably referring to a video or a short
78        return Ok(DownloadOption::YtVideo(0));
79    }
80
81    Err(BlobdlError::QueryCouldNotBeParsed)
82}