feat: retry with different client after 403 error
This commit is contained in:
parent
97904d7737
commit
d875b5442d
3 changed files with 56 additions and 23 deletions
|
|
@ -94,21 +94,21 @@ enum Commands {
|
|||
#[clap(short, long)]
|
||||
resolution: Option<u32>,
|
||||
/// Download only the audio track
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
audio: bool,
|
||||
/// Number of videos downloaded in parallel
|
||||
#[clap(short, long, default_value_t = 8)]
|
||||
parallel: usize,
|
||||
/// Use YouTube Music for downloading playlists
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
music: bool,
|
||||
/// Limit the number of videos to download
|
||||
#[clap(long, default_value_t = 1000)]
|
||||
#[clap(short, long, default_value_t = 1000)]
|
||||
limit: usize,
|
||||
/// YT Client used to fetch player data
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
client_type: Option<Vec<ClientTypeArg>>,
|
||||
/// Pot token to circumvent bot detection
|
||||
/// `pot` token to circumvent bot detection
|
||||
#[clap(long)]
|
||||
pot: Option<String>,
|
||||
},
|
||||
|
|
@ -123,16 +123,16 @@ enum Commands {
|
|||
#[clap(long)]
|
||||
pretty: bool,
|
||||
/// Output as text
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
txt: bool,
|
||||
/// Limit the number of items to fetch
|
||||
#[clap(long, default_value_t = 20)]
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
limit: usize,
|
||||
/// Channel tab
|
||||
#[clap(long, default_value = "videos")]
|
||||
tab: ChannelTab,
|
||||
/// Use YouTube Music
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
music: bool,
|
||||
/// Get comments
|
||||
#[clap(long)]
|
||||
|
|
@ -144,7 +144,7 @@ enum Commands {
|
|||
#[clap(long)]
|
||||
player: bool,
|
||||
/// YT Client used to fetch player data
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
client_type: Option<ClientTypeArg>,
|
||||
},
|
||||
/// Search YouTube
|
||||
|
|
@ -158,10 +158,10 @@ enum Commands {
|
|||
#[clap(long)]
|
||||
pretty: bool,
|
||||
/// Output as text
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
txt: bool,
|
||||
/// Limit the number of items to fetch
|
||||
#[clap(long, default_value_t = 20)]
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
limit: usize,
|
||||
/// Filter results by item type
|
||||
#[clap(long)]
|
||||
|
|
@ -179,7 +179,7 @@ enum Commands {
|
|||
#[clap(long)]
|
||||
channel: Option<String>,
|
||||
/// YouTube Music search filter
|
||||
#[clap(long)]
|
||||
#[clap(short, long)]
|
||||
music: Option<MusicSearchCategory>,
|
||||
},
|
||||
/// Get a YouTube visitor data cookie
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use rand::Rng;
|
|||
use regex::Regex;
|
||||
use reqwest::{header, Client, StatusCode, Url};
|
||||
use rustypipe::{
|
||||
client::{ClientType, RustyPipe},
|
||||
client::{ClientType, RustyPipe, DEFAULT_PLAYER_CLIENT_ORDER},
|
||||
model::{
|
||||
traits::{FileFormat, YtEntity},
|
||||
AudioCodec, TrackItem, VideoCodec, VideoPlayer,
|
||||
|
|
@ -667,6 +667,7 @@ impl DownloadQuery {
|
|||
#[tracing::instrument(skip(self), level="error", fields(id = self.video.id))]
|
||||
pub async fn download(&self) -> Result<DownloadResult> {
|
||||
let mut last_err = None;
|
||||
let mut failed_client = None;
|
||||
|
||||
// Progress bar
|
||||
#[cfg(feature = "indicatif")]
|
||||
|
|
@ -683,14 +684,19 @@ impl DownloadQuery {
|
|||
let err = match self
|
||||
.download_attempt(
|
||||
n,
|
||||
failed_client,
|
||||
#[cfg(feature = "indicatif")]
|
||||
&pb,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(res) => return Ok(res),
|
||||
Err(DownloadError::Forbidden(c)) => {
|
||||
failed_client = Some(c);
|
||||
DownloadError::Forbidden(c)
|
||||
}
|
||||
Err(DownloadError::Http(e)) => {
|
||||
if !e.is_timeout() && e.status() != Some(StatusCode::FORBIDDEN) {
|
||||
if !e.is_timeout() {
|
||||
return Err(DownloadError::Http(e));
|
||||
}
|
||||
DownloadError::Http(e)
|
||||
|
|
@ -710,6 +716,7 @@ impl DownloadQuery {
|
|||
async fn download_attempt(
|
||||
&self,
|
||||
#[allow(unused_variables)] n: u32,
|
||||
failed_client: Option<ClientType>,
|
||||
#[cfg(feature = "indicatif")] pb: &Option<ProgressBar>,
|
||||
) -> Result<DownloadResult> {
|
||||
let filter = self.filter.as_ref().unwrap_or(&self.dl.i.filter);
|
||||
|
|
@ -750,14 +757,28 @@ impl DownloadQuery {
|
|||
}
|
||||
|
||||
let q = self.dl.i.rp.query();
|
||||
let player_data = match self
|
||||
.client_types
|
||||
.as_ref()
|
||||
.or(self.dl.i.client_types.as_ref())
|
||||
{
|
||||
Some(client_types) => q.player_from_clients(&self.video.id, client_types).await?,
|
||||
None => q.player(&self.video.id).await?,
|
||||
};
|
||||
|
||||
let mut client_types = Cow::Borrowed(
|
||||
self.client_types
|
||||
.as_ref()
|
||||
.or(self.dl.i.client_types.as_ref())
|
||||
.map(Vec::as_slice)
|
||||
.unwrap_or(DEFAULT_PLAYER_CLIENT_ORDER),
|
||||
);
|
||||
|
||||
// If the last download failed, try another client if possible
|
||||
if let Some(failed_client) = failed_client {
|
||||
if let Some(pos) = client_types.iter().position(|c| c == &failed_client) {
|
||||
let p2 = pos + 1;
|
||||
if p2 < client_types.len() {
|
||||
let mut v = client_types[p2..].to_vec();
|
||||
v.extend(&client_types[..p2]);
|
||||
client_types = v.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let player_data = q.player_from_clients(&self.video.id, &client_types).await?;
|
||||
let user_agent = q.user_agent(player_data.client_type);
|
||||
let pot = if matches!(
|
||||
player_data.client_type,
|
||||
|
|
@ -848,7 +869,15 @@ impl DownloadQuery {
|
|||
#[cfg(feature = "indicatif")]
|
||||
pb.clone(),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if let DownloadError::Http(e) = &e {
|
||||
if e.status() == Some(StatusCode::FORBIDDEN) {
|
||||
return DownloadError::Forbidden(player_data.client_type);
|
||||
}
|
||||
}
|
||||
e
|
||||
})?;
|
||||
|
||||
#[cfg(feature = "indicatif")]
|
||||
if let Some(pb) = &pb {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{borrow::Cow, collections::BTreeMap, path::PathBuf};
|
||||
|
||||
use reqwest::Url;
|
||||
use rustypipe::client::ClientType;
|
||||
|
||||
/// Error from the video downloader
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
|
@ -12,6 +13,9 @@ pub enum DownloadError {
|
|||
/// Error from the HTTP client
|
||||
#[error("http error: {0}")]
|
||||
Http(#[from] reqwest::Error),
|
||||
/// 403 error trying to download video
|
||||
#[error("YouTube returned 403 error")]
|
||||
Forbidden(ClientType),
|
||||
/// File IO error
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
|
|
|||
Reference in a new issue