Skip to main content

yt_dlp/extractor/
generic.rs

1//! Generic extractor for all non-YouTube sites supported by yt-dlp.
2//!
3//! This extractor provides universal video downloading from 1,800+ sites
4//! with optional authentication support.
5
6use std::path::PathBuf;
7use std::time::Duration;
8
9use async_trait::async_trait;
10
11use crate::error::Result;
12use crate::extractor::{ExtractorBase, VideoExtractor};
13use crate::model::Video;
14use crate::model::playlist::Playlist;
15
16/// Generic extractor for all non-YouTube sites.
17///
18/// This extractor provides a simple wrapper around yt-dlp that works
19/// with any supported site. It includes helpers for authentication.
20#[derive(Debug, Clone)]
21pub struct Generic {
22    executable_path: PathBuf,
23    extractor_name: Option<String>,
24    args: Vec<String>,
25    timeout: Duration,
26}
27
28crate::extractor::impl_extractor_config!(Generic);
29
30impl Generic {
31    /// Create a new generic extractor with automatic detection.
32    ///
33    /// # Arguments
34    ///
35    /// * `executable_path` - Path to the yt-dlp executable
36    ///
37    /// # Returns
38    ///
39    /// A new Generic extractor instance
40    pub fn new(executable_path: PathBuf) -> Self {
41        tracing::debug!(
42            executable = ?executable_path,
43            "⚙️ Creating new Generic extractor"
44        );
45
46        Self {
47            executable_path,
48            extractor_name: None,
49            args: Vec::new(),
50            timeout: crate::client::DEFAULT_TIMEOUT,
51        }
52    }
53
54    /// Create for specific extractor (skip detection).
55    ///
56    /// # Arguments
57    ///
58    /// * `executable_path` - Path to the yt-dlp executable
59    /// * `name` - Name of the extractor to use
60    ///
61    /// # Returns
62    ///
63    /// A new Generic extractor instance for the specified extractor
64    pub fn for_extractor(executable_path: PathBuf, name: String) -> Self {
65        tracing::debug!(
66            executable = ?executable_path,
67            extractor_name = name,
68            "⚙️ Creating Generic extractor for specific extractor"
69        );
70
71        Self {
72            executable_path,
73            extractor_name: Some(name),
74            args: Vec::new(),
75            timeout: crate::client::DEFAULT_TIMEOUT,
76        }
77    }
78
79    /// Add extractor-specific arguments.
80    ///
81    /// # Arguments
82    ///
83    /// * `extractor` - Name of the extractor
84    /// * `args` - Arguments to pass to the extractor
85    ///
86    /// # Returns
87    ///
88    /// Self for method chaining
89    ///
90    /// # Examples
91    /// ```rust,no_run
92    /// # use yt_dlp::extractor::Generic;
93    /// # use std::path::PathBuf;
94    /// let mut extractor = Generic::new(PathBuf::from("yt-dlp"));
95    /// extractor.with_extractor_args("tiktok", "api_hostname=api-h2.tiktokv.com");
96    /// ```
97    pub fn with_extractor_args(&mut self, extractor: &str, args: &str) -> &mut Self {
98        tracing::debug!(
99            extractor = extractor,
100            args = args,
101            "⚙️ Adding extractor-specific arguments"
102        );
103
104        self.args.push(format!("--extractor-args={}:{}", extractor, args));
105        self
106    }
107
108    /// Use credentials for sites requiring login.
109    ///
110    /// **Security note:** Credentials are passed via `--username` and `--password` CLI arguments,
111    /// which may be visible in process listings. For sensitive environments, prefer
112    /// [`with_netrc`] or [`with_cookies`] on the `Downloader` instead.
113    ///
114    /// # Arguments
115    ///
116    /// * `username` - Username for authentication
117    /// * `password` - Password for authentication
118    ///
119    /// # Returns
120    ///
121    /// Self for method chaining
122    ///
123    /// # Examples
124    /// ```rust,no_run
125    /// # use yt_dlp::extractor::Generic;
126    /// # use std::path::PathBuf;
127    /// let mut extractor = Generic::new(PathBuf::from("yt-dlp"));
128    /// extractor.with_credentials("user@email.com", "password");
129    /// ```
130    pub fn with_credentials(&mut self, username: &str, password: &str) -> &mut Self {
131        tracing::debug!(
132            has_password = !password.is_empty(),
133            "⚙️ Adding credentials for authentication"
134        );
135        tracing::warn!(
136            "Credentials passed as CLI arguments are visible in process listings — consider using netrc or cookies instead"
137        );
138
139        self.args.push(format!("--username={}", username));
140        self.args.push(format!("--password={}", password));
141        self
142    }
143}
144
145#[async_trait]
146impl ExtractorBase for Generic {
147    fn executable_path(&self) -> PathBuf {
148        self.executable_path.clone()
149    }
150
151    fn timeout(&self) -> Duration {
152        self.timeout
153    }
154
155    fn build_base_args(&self) -> Vec<String> {
156        let mut args = vec!["--no-progress".to_string(), "--dump-single-json".to_string()];
157        args.extend(self.args.clone());
158        args
159    }
160}
161
162#[async_trait]
163impl VideoExtractor for Generic {
164    async fn fetch_video(&self, url: &str) -> Result<Video> {
165        tracing::debug!(
166            url = url,
167            extractor_name = ?self.extractor_name,
168            arg_count = self.args.len(),
169            "📡 Fetching video with Generic extractor"
170        );
171        self.log_and_fetch_video(url, "Generic").await
172    }
173
174    async fn fetch_playlist(&self, url: &str) -> Result<Playlist> {
175        tracing::debug!(
176            url = url,
177            extractor_name = ?self.extractor_name,
178            arg_count = self.args.len(),
179            "📡 Fetching playlist with Generic extractor"
180        );
181        self.log_and_fetch_playlist(url, "Generic").await
182    }
183
184    fn name(&self) -> crate::extractor::ExtractorName {
185        crate::extractor::ExtractorName::Generic(self.extractor_name.clone())
186    }
187
188    fn supports_url(&self, _url: &str) -> bool {
189        // Generic extractor supports everything (will validate at runtime)
190        true
191    }
192}