stream_download/process/
yt_dlp.rs

1use std::ffi::OsString;
2use std::io;
3
4use super::{Command, SpawnCommand, SpawnedCommand};
5
6/// Helper to construct a valid `yt-dlp` command that outputs to `stdout`.
7#[derive(Debug, Clone)]
8pub struct YtDlpCommand {
9    url: String,
10    cmd_name: OsString,
11    extract_audio: bool,
12    format: Option<String>,
13}
14
15impl From<YtDlpCommand> for Command {
16    fn from(value: YtDlpCommand) -> Self {
17        value.into_command()
18    }
19}
20
21impl SpawnCommand for YtDlpCommand {
22    fn spawn(self) -> io::Result<SpawnedCommand> {
23        self.into_command().spawn()
24    }
25}
26
27impl YtDlpCommand {
28    /// Creates a mew [`YtDlpCommand`].
29    pub fn new<S>(url: S) -> Self
30    where
31        S: Into<String>,
32    {
33        Self {
34            url: url.into(),
35            cmd_name: "yt-dlp".into(),
36            extract_audio: false,
37            format: None,
38        }
39    }
40
41    /// Creates a [`Command`] from the given parameters.
42    #[must_use]
43    pub fn into_command(self) -> Command {
44        let mut cmd = Command::new(&self.cmd_name).args([
45            &self.url,
46            "--quiet",
47            // don't store .part files because they can conflict with each other during concurrent
48            // downloads
49            "--no-part",
50            // don't reuse previous fragments since they could be from a different file
51            "--no-continue",
52            "-o",
53            "-",
54        ]);
55        if self.extract_audio {
56            cmd = cmd.arg("-x");
57        }
58        if let Some(format) = &self.format {
59            cmd = cmd.args(["-f", format]);
60        }
61        cmd
62    }
63
64    /// Extract audio from the given URL.
65    #[must_use]
66    pub fn extract_audio(mut self, extract_audio: bool) -> Self {
67        self.extract_audio = extract_audio;
68        self
69    }
70
71    /// Extract content using the provided format.
72    /// An error will be thrown when running the command if the format is not available.
73    #[must_use]
74    pub fn format<S>(mut self, format: S) -> Self
75    where
76        S: Into<String>,
77    {
78        self.format = Some(format.into());
79        self
80    }
81
82    /// Sets the path to the `yt-dlp` binary.
83    #[must_use]
84    pub fn yt_dlp_path<S>(mut self, path: S) -> Self
85    where
86        S: Into<OsString>,
87    {
88        self.cmd_name = path.into();
89        self
90    }
91}