1use crate::proxies::{GenericProxyConfig, ProxyConfig, WebshareProxyConfig};
2use crate::TranscriptList;
3use thiserror::Error;
4
5#[derive(Debug, Error)]
12pub enum YouTubeTranscriptApiError {
13 #[error("YouTube Transcript API error")]
14 Generic,
15}
16
17#[derive(Debug, Error)]
24pub enum CookieError {
25 #[error("Cookie error")]
26 Generic,
27
28 #[error("Can't load the provided cookie file: {0}")]
30 PathInvalid(String),
31
32 #[error("The cookies provided are not valid (may have expired): {0}")]
34 Invalid(String),
35}
36
37pub type CookiePathInvalid = CookieError;
39
40pub type CookieInvalid = CookieError;
42
43#[derive(Debug, Error)]
77#[error("{}", self.build_error_message())]
78pub struct CouldNotRetrieveTranscript {
79 pub video_id: String,
81
82 pub reason: Option<CouldNotRetrieveTranscriptReason>,
84}
85
86#[derive(Debug)]
93pub enum CouldNotRetrieveTranscriptReason {
94 TranscriptsDisabled,
96
97 NoTranscriptFound {
99 requested_language_codes: Vec<String>,
101
102 transcript_data: TranscriptList,
104 },
105
106 VideoUnavailable,
108
109 VideoUnplayable {
111 reason: Option<String>,
113
114 sub_reasons: Vec<String>,
116 },
117
118 IpBlocked(Option<Box<dyn ProxyConfig>>),
120
121 RequestBlocked(Option<Box<dyn ProxyConfig>>),
123
124 NotTranslatable,
126
127 TranslationLanguageNotAvailable,
129
130 FailedToCreateConsentCookie,
132
133 YouTubeRequestFailed(String),
135
136 InvalidVideoId,
138
139 AgeRestricted,
141
142 YouTubeDataUnparsable,
144}
145
146impl CouldNotRetrieveTranscript {
147 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
240pub type TranscriptsDisabled = CouldNotRetrieveTranscript;
242
243pub type NoTranscriptFound = CouldNotRetrieveTranscript;
245
246pub type VideoUnavailable = CouldNotRetrieveTranscript;
248
249pub type VideoUnplayable = CouldNotRetrieveTranscript;
251
252pub type IpBlocked = CouldNotRetrieveTranscript;
254
255pub type RequestBlocked = CouldNotRetrieveTranscript;
257
258pub type NotTranslatable = CouldNotRetrieveTranscript;
260
261pub type TranslationLanguageNotAvailable = CouldNotRetrieveTranscript;
263
264pub type FailedToCreateConsentCookie = CouldNotRetrieveTranscript;
266
267pub type YouTubeRequestFailed = CouldNotRetrieveTranscript;
269
270pub type InvalidVideoId = CouldNotRetrieveTranscript;
272
273pub type AgeRestricted = CouldNotRetrieveTranscript;
275
276pub type YouTubeDataUnparsable = CouldNotRetrieveTranscript;