From 68342cecab6bb44c351d62e1abc4605b7adfbccf Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 4 Nov 2022 21:28:13 +0100 Subject: [PATCH] feat: add more extraction errors --- src/client/mod.rs | 7 ++----- src/client/player.rs | 47 ++++++++++++++++++++++++++++++++++++-------- src/error.rs | 24 ++++++++++++++++++++++ tests/youtube.rs | 25 ++++++++++++----------- 4 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index c28d518..c2b325c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1075,11 +1075,8 @@ impl RustyPipeQuery { Ok(mapres.c) } Err(e) => { - match e { - ExtractionError::VideoUnavailable(_, _) - | ExtractionError::VideoAgeRestricted - | ExtractionError::ContentUnavailable(_) => (), - _ => create_report(Level::ERR, Some(e.to_string()), Vec::new()), + if e.should_report() { + create_report(Level::ERR, Some(e.to_string()), Vec::new()); } Err(e.into()) } diff --git a/src/client/player.rs b/src/client/player.rs index 129a9fb..c8d0c71 100644 --- a/src/client/player.rs +++ b/src/client/player.rs @@ -64,11 +64,22 @@ impl RustyPipeQuery { match android_res { Ok(res) => Ok(res), - Err(Error::Extraction( - ExtractionError::VideoAgeRestricted | ExtractionError::WrongResult(_), - )) => { - self.player_from_client(video_id, ClientType::TvHtml5Embed) - .await + Err(Error::Extraction(e)) => { + if e.switch_client() { + let tv_res = self + .player_from_client(video_id, ClientType::TvHtml5Embed) + .await; + + match tv_res { + // Output android client error if the tv client is unsupported + Err(Error::Extraction(ExtractionError::VideoClientUnsupported(_))) => { + Err(Error::Extraction(e)) + } + _ => tv_res, + } + } else { + Err(Error::Extraction(e)) + } } Err(e) => Err(e), } @@ -138,11 +149,29 @@ impl MapResponse for response::Player { live_streamability.is_some() } response::player::PlayabilityStatus::Unplayable { reason } => { - return Err(ExtractionError::VideoUnavailable("DRM/Geoblock", reason)) + for word in reason.split_whitespace() { + match word { + // reason: "This video requires payment to watch." + "payment" => return Err(ExtractionError::VideoUnavailable("DRM", reason)), + // reason: "The uploader has not made this video available in your country." + "country" => return Err(ExtractionError::VideoGeoblock), + // reason (Android): "This video can only be played on newer versions of Android or other supported devices." + // reason (TV client): "Playback on other websites has been disabled by the video owner." + "Android" | "websites" => { + return Err(ExtractionError::VideoClientUnsupported(reason)) + } + _ => {} + } + } + return Err(ExtractionError::VideoUnavailable( + "being unplayable", + reason, + )); } response::player::PlayabilityStatus::LoginRequired { reason } => { - // reason: "Sign in to confirm your age" + // reason (age restriction): "Sign in to confirm your age" // or: "This video may be inappropriate for some users." + // reason (private): "This video is private" if reason .split_whitespace() .any(|word| word == "age" || word == "inappropriate") @@ -158,10 +187,12 @@ impl MapResponse for response::Player { )) } response::player::PlayabilityStatus::Error { reason } => { + // reason (censored): "This video has been removed for violating YouTube's policy on hate speech. Learn more about combating hate speech in your country." + // reason: "This video is unavailable" return Err(ExtractionError::VideoUnavailable( "deletion/censorship", reason, - )) + )); } }; diff --git a/src/error.rs b/src/error.rs index 23cb14a..d514e26 100644 --- a/src/error.rs +++ b/src/error.rs @@ -73,6 +73,10 @@ pub enum ExtractionError { VideoUnavailable(&'static str, String), #[error("Video is age restricted")] VideoAgeRestricted, + #[error("Video is not available in your country")] + VideoGeoblock, + #[error("Video cant be played with this client. Reason (from YT): {0}")] + VideoClientUnsupported(String), #[error("Content is not available. Reason: {0}")] ContentUnavailable(Cow<'static, str>), #[error("deserialization error: {0}")] @@ -84,3 +88,23 @@ pub enum ExtractionError { #[error("Warnings during deserialization/mapping")] DeserializationWarnings, } + +impl ExtractionError { + pub(crate) fn should_report(&self) -> bool { + matches!( + self, + ExtractionError::Deserialization(_) + | ExtractionError::InvalidData(_) + | ExtractionError::WrongResult(_) + ) + } + + pub(crate) fn switch_client(&self) -> bool { + matches!( + self, + ExtractionError::VideoClientUnsupported(_) + | ExtractionError::VideoAgeRestricted + | ExtractionError::WrongResult(_) + ) + } +} diff --git a/tests/youtube.rs b/tests/youtube.rs index cdaf0ac..6fb9d40 100644 --- a/tests/youtube.rs +++ b/tests/youtube.rs @@ -271,27 +271,28 @@ async fn get_player( // This video is geoblocked outside of Japan, so expect this test case to fail when using a Japanese IP address. #[case::geoblock( "sJL6WA-aGkQ", - "extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): " + "extraction error: Video is not available in your country" )] #[case::drm( "1bfOsni7EgI", - "extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): " + "extraction error: Video cant be played because of DRM. Reason (from YT): " )] -// YouTube sometimes returns "Video unavailable" for this video -// #[case::private( -// "s7_qI6_mIXc", -// "extraction error: Video cant be played because of being private. Reason (from YT): " -// )] -#[case::t1( - "CUO8secmc0g", - "extraction error: Video cant be played because of DRM/Geoblock. Reason (from YT): " +#[case::private( + "s7_qI6_mIXc", + "extraction error: Video cant be played because of being private. Reason (from YT): " )] +#[case::age_restricted("CUO8secmc0g", "extraction error: Video is age restricted")] #[tokio::test] async fn get_player_error(#[case] id: &str, #[case] msg: &str) { let rp = RustyPipe::builder().strict().build(); - let err = rp.query().player(id).await.unwrap_err(); + let err = rp.query().player(id).await.unwrap_err().to_string(); - assert!(err.to_string().starts_with(msg), "got error msg: {}", err); + assert!( + err.starts_with(msg), + "got error msg: `{}`, expected: `{}`", + err, + msg + ); } //#PLAYLIST