yt_transcript_rs/
errors.rs

1use crate::proxies::{GenericProxyConfig, ProxyConfig, WebshareProxyConfig};
2use crate::TranscriptList;
3use thiserror::Error;
4
5/// # YouTubeTranscriptApiError
6///
7/// The base error type for the library.
8///
9/// This is mainly used as a generic error type for cases that don't fall into
10/// more specific error categories.
11#[derive(Debug, Error)]
12pub enum YouTubeTranscriptApiError {
13    #[error("YouTube Transcript API error")]
14    Generic,
15}
16
17/// # CookieError
18///
19/// Errors related to cookie handling and authentication.
20///
21/// These errors occur when there are issues with loading or using cookies
22/// for authenticated requests to YouTube.
23#[derive(Debug, Error)]
24pub enum CookieError {
25    #[error("Cookie error")]
26    Generic,
27
28    /// Error when the specified cookie file path is invalid or inaccessible
29    #[error("Can't load the provided cookie file: {0}")]
30    PathInvalid(String),
31
32    /// Error when the cookies are invalid (possibly expired or malformed)
33    #[error("The cookies provided are not valid (may have expired): {0}")]
34    Invalid(String),
35}
36
37/// Type alias for cookie path invalid errors
38pub type CookiePathInvalid = CookieError;
39
40/// Type alias for invalid cookie errors
41pub type CookieInvalid = CookieError;
42
43/// # CouldNotRetrieveTranscript
44///
45/// The primary error type when transcript retrieval fails.
46///
47/// This error provides detailed information about why a transcript couldn't be retrieved,
48/// with specific reasons and helpful suggestions for resolving the issue.
49///
50/// ## Usage Example
51///
52/// ```rust,no_run
53/// # use yt_transcript_rs::YouTubeTranscriptApi;
54/// # async fn example() {
55/// let api = YouTubeTranscriptApi::new(None, None, None).unwrap();
56///
57/// match api.fetch_transcript("dQw4w9WgXcQ", &["en"], false).await {
58///     Ok(transcript) => {
59///         println!("Successfully retrieved transcript");
60///     },
61///     Err(err) => {
62///         // The error message contains detailed information about what went wrong
63///         eprintln!("Error: {}", err);
64///         
65///         // You can also check for specific error types
66///         if err.reason.is_some() {
67///             match err.reason.as_ref().unwrap() {
68///                 // Handle specific error cases
69///                 _ => {}
70///             }
71///         }
72///     }
73/// }
74/// # }
75/// ```
76#[derive(Debug, Error)]
77#[error("{}", self.build_error_message())]
78pub struct CouldNotRetrieveTranscript {
79    /// The YouTube video ID that was being accessed
80    pub video_id: String,
81
82    /// The specific reason why the transcript couldn't be retrieved
83    pub reason: Option<CouldNotRetrieveTranscriptReason>,
84}
85
86/// # CouldNotRetrieveTranscriptReason
87///
88/// Detailed reasons why a transcript couldn't be retrieved.
89///
90/// This enum provides specific information about why transcript retrieval failed,
91/// which is useful for error handling and providing helpful feedback to users.
92#[derive(Debug)]
93pub enum CouldNotRetrieveTranscriptReason {
94    /// Subtitles/transcripts are disabled for this video
95    TranscriptsDisabled,
96
97    /// No transcript was found in any of the requested languages
98    NoTranscriptFound {
99        /// The language codes that were requested but not found
100        requested_language_codes: Vec<String>,
101
102        /// Information about available transcripts that could be used instead
103        transcript_data: TranscriptList,
104    },
105
106    /// The video is no longer available (removed, private, etc.)
107    VideoUnavailable,
108
109    /// The video cannot be played for some reason
110    VideoUnplayable {
111        /// The main reason why the video is unplayable
112        reason: Option<String>,
113
114        /// Additional details about why the video is unplayable
115        sub_reasons: Vec<String>,
116    },
117
118    /// YouTube is blocking requests from your IP address
119    IpBlocked(Option<Box<dyn ProxyConfig>>),
120
121    /// YouTube is blocking your request (rate limiting, etc.)
122    RequestBlocked(Option<Box<dyn ProxyConfig>>),
123
124    /// The requested transcript cannot be translated
125    NotTranslatable,
126
127    /// The requested translation language is not available
128    TranslationLanguageNotAvailable,
129
130    /// Failed to create a consent cookie required by YouTube
131    FailedToCreateConsentCookie,
132
133    /// The request to YouTube failed with a specific error
134    YouTubeRequestFailed(String),
135
136    /// The provided video ID is invalid
137    InvalidVideoId,
138
139    /// The video is age-restricted and requires authentication
140    AgeRestricted,
141
142    /// The YouTube data structure couldn't be parsed
143    YouTubeDataUnparsable,
144}
145
146impl CouldNotRetrieveTranscript {
147    /// Builds a detailed error message based on the error reason
148    fn build_error_message(&self) -> String {
149        let base_error = format!(
150            "Could not retrieve a transcript for the video {}!",
151            self.video_id.replace("{video_id}", &self.video_id)
152        );
153
154        match &self.reason {
155            Some(reason) => {
156                let cause = match reason {
157                    CouldNotRetrieveTranscriptReason::TranscriptsDisabled => {
158                        "Subtitles are disabled for this video".to_string()
159                    },
160                    CouldNotRetrieveTranscriptReason::NoTranscriptFound { requested_language_codes, transcript_data } => {
161                        format!("No transcripts were found for any of the requested language codes: {:?}\n\n{}", 
162                            requested_language_codes, transcript_data)
163                    },
164                    CouldNotRetrieveTranscriptReason::VideoUnavailable => {
165                        "The video is no longer available".to_string()
166                    },
167                    CouldNotRetrieveTranscriptReason::VideoUnplayable { reason, sub_reasons } => {
168                        let reason_str = reason.clone().unwrap_or_else(|| "No reason specified!".to_string());
169                        let mut message = format!("The video is unplayable for the following reason: {}", reason_str);
170                        if !sub_reasons.is_empty() {
171                            message.push_str("\n\nAdditional Details:\n");
172                            for sub_reason in sub_reasons {
173                                message.push_str(&format!(" - {}\n", sub_reason));
174                            }
175                        }
176                        message
177                    },
178                    CouldNotRetrieveTranscriptReason::IpBlocked(proxy_config) => {
179                        let base_cause = "YouTube is blocking requests from your IP. This usually is due to one of the following reasons:
180- You have done too many requests and your IP has been blocked by YouTube
181- You are doing requests from an IP belonging to a cloud provider (like AWS, Google Cloud Platform, Azure, etc.). Unfortunately, most IPs from cloud providers are blocked by YouTube.";
182                        match proxy_config {
183                            Some(config) if config.as_any().is::<WebshareProxyConfig>() => {
184                                format!("{}\n\nYouTube is blocking your requests, despite you using Webshare proxies. Please make sure that you have purchased \"Residential\" proxies and NOT \"Proxy Server\" or \"Static Residential\", as those won't work as reliably! The free tier also uses \"Proxy Server\" and will NOT work!\n\nThe only reliable option is using \"Residential\" proxies (not \"Static Residential\"), as this allows you to rotate through a pool of over 30M IPs, which means you will always find an IP that hasn't been blocked by YouTube yet!", base_cause)
185                            },
186                            Some(config) if config.as_any().is::<GenericProxyConfig>() => {
187                                format!("{}\n\nYouTube is blocking your requests, despite you using proxies. Keep in mind a proxy is just a way to hide your real IP behind the IP of that proxy, but there is no guarantee that the IP of that proxy won't be blocked as well.\n\nThe only truly reliable way to prevent IP blocks is rotating through a large pool of residential IPs, by using a provider like Webshare.", base_cause)
188                            },
189                            _ => {
190                                format!("{}\n\nIp blocked.", base_cause)
191                            }
192                        }
193                    },
194                    CouldNotRetrieveTranscriptReason::RequestBlocked(proxy_config) => {
195                        let base_cause = "YouTube is blocking requests from your IP. This usually is due to one of the following reasons:
196- You have done too many requests and your IP has been blocked by YouTube
197- You are doing requests from an IP belonging to a cloud provider (like AWS, Google Cloud Platform, Azure, etc.). Unfortunately, most IPs from cloud providers are blocked by YouTube.";
198                        match proxy_config {
199                            Some(config) if config.as_any().is::<WebshareProxyConfig>() => {
200                                format!("{}\n\nYouTube is blocking your requests, despite you using Webshare proxies. Please make sure that you have purchased \"Residential\" proxies and NOT \"Proxy Server\" or \"Static Residential\", as those won't work as reliably! The free tier also uses \"Proxy Server\" and will NOT work!\n\nThe only reliable option is using \"Residential\" proxies (not \"Static Residential\"), as this allows you to rotate through a pool of over 30M IPs, which means you will always find an IP that hasn't been blocked by YouTube yet!", base_cause)
201                            },
202                            Some(config) if config.as_any().is::<GenericProxyConfig>() => {
203                                format!("{}\n\nYouTube is blocking your requests, despite you using proxies. Keep in mind a proxy is just a way to hide your real IP behind the IP of that proxy, but there is no guarantee that the IP of that proxy won't be blocked as well.\n\nThe only truly reliable way to prevent IP blocks is rotating through a large pool of residential IPs.", base_cause)
204                            },
205                            _ => {
206                                format!("{}\n\nRequest blocked.", base_cause)
207                            }
208                        }
209                    },
210                    CouldNotRetrieveTranscriptReason::NotTranslatable => {
211                        "The requested language is not translatable".to_string()
212                    },
213                    CouldNotRetrieveTranscriptReason::TranslationLanguageNotAvailable => {
214                        "The requested translation language is not available".to_string()
215                    },
216                    CouldNotRetrieveTranscriptReason::FailedToCreateConsentCookie => {
217                        "Failed to automatically give consent to saving cookies".to_string()
218                    },
219                    CouldNotRetrieveTranscriptReason::YouTubeRequestFailed(reason) => {
220                        format!("Request to YouTube failed: {}", reason)
221                    },
222                    CouldNotRetrieveTranscriptReason::InvalidVideoId => {
223                        "You provided an invalid video id. Make sure you are using the video id and NOT the url!`".to_string()
224                    },
225                    CouldNotRetrieveTranscriptReason::AgeRestricted => {
226                        "This video is age-restricted. Therefore, you will have to authenticate to be able to retrieve transcripts for it. You will have to provide a cookie to authenticate yourself.".to_string()
227                    },
228                    CouldNotRetrieveTranscriptReason::YouTubeDataUnparsable => {
229                        "The data required to fetch the transcript is not parsable. This should not happen, please open an issue (make sure to include the video ID)!".to_string()
230                    }
231                };
232
233                format!("{} This is most likely caused by:\n\n{}", base_error, cause)
234            }
235            None => base_error,
236        }
237    }
238}
239
240/// Type alias for when transcripts are disabled for a video
241pub type TranscriptsDisabled = CouldNotRetrieveTranscript;
242
243/// Type alias for when no transcript is found in the requested languages
244pub type NoTranscriptFound = CouldNotRetrieveTranscript;
245
246/// Type alias for when the video is no longer available
247pub type VideoUnavailable = CouldNotRetrieveTranscript;
248
249/// Type alias for when the video cannot be played
250pub type VideoUnplayable = CouldNotRetrieveTranscript;
251
252/// Type alias for when YouTube is blocking your IP address
253pub type IpBlocked = CouldNotRetrieveTranscript;
254
255/// Type alias for when YouTube is blocking your request
256pub type RequestBlocked = CouldNotRetrieveTranscript;
257
258/// Type alias for when the requested transcript cannot be translated
259pub type NotTranslatable = CouldNotRetrieveTranscript;
260
261/// Type alias for when the requested translation language is not available
262pub type TranslationLanguageNotAvailable = CouldNotRetrieveTranscript;
263
264/// Type alias for when creating a consent cookie fails
265pub type FailedToCreateConsentCookie = CouldNotRetrieveTranscript;
266
267/// Type alias for when a request to YouTube fails
268pub type YouTubeRequestFailed = CouldNotRetrieveTranscript;
269
270/// Type alias for when an invalid video ID is provided
271pub type InvalidVideoId = CouldNotRetrieveTranscript;
272
273/// Type alias for when the video is age-restricted and requires authentication
274pub type AgeRestricted = CouldNotRetrieveTranscript;
275
276/// Type alias for when YouTube data cannot be parsed
277pub type YouTubeDataUnparsable = CouldNotRetrieveTranscript;