feat: add more extraction errors

This commit is contained in:
ThetaDev 2022-11-04 21:28:13 +01:00
parent f748b98ccc
commit 68342cecab
4 changed files with 78 additions and 25 deletions

View file

@ -1075,11 +1075,8 @@ impl RustyPipeQuery {
Ok(mapres.c) Ok(mapres.c)
} }
Err(e) => { Err(e) => {
match e { if e.should_report() {
ExtractionError::VideoUnavailable(_, _) create_report(Level::ERR, Some(e.to_string()), Vec::new());
| ExtractionError::VideoAgeRestricted
| ExtractionError::ContentUnavailable(_) => (),
_ => create_report(Level::ERR, Some(e.to_string()), Vec::new()),
} }
Err(e.into()) Err(e.into())
} }

View file

@ -64,11 +64,22 @@ impl RustyPipeQuery {
match android_res { match android_res {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err(Error::Extraction( Err(Error::Extraction(e)) => {
ExtractionError::VideoAgeRestricted | ExtractionError::WrongResult(_), if e.switch_client() {
)) => { let tv_res = self
self.player_from_client(video_id, ClientType::TvHtml5Embed) .player_from_client(video_id, ClientType::TvHtml5Embed)
.await .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), Err(e) => Err(e),
} }
@ -138,11 +149,29 @@ impl MapResponse<VideoPlayer> for response::Player {
live_streamability.is_some() live_streamability.is_some()
} }
response::player::PlayabilityStatus::Unplayable { reason } => { 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 } => { 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." // or: "This video may be inappropriate for some users."
// reason (private): "This video is private"
if reason if reason
.split_whitespace() .split_whitespace()
.any(|word| word == "age" || word == "inappropriate") .any(|word| word == "age" || word == "inappropriate")
@ -158,10 +187,12 @@ impl MapResponse<VideoPlayer> for response::Player {
)) ))
} }
response::player::PlayabilityStatus::Error { reason } => { 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( return Err(ExtractionError::VideoUnavailable(
"deletion/censorship", "deletion/censorship",
reason, reason,
)) ));
} }
}; };

View file

@ -73,6 +73,10 @@ pub enum ExtractionError {
VideoUnavailable(&'static str, String), VideoUnavailable(&'static str, String),
#[error("Video is age restricted")] #[error("Video is age restricted")]
VideoAgeRestricted, 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}")] #[error("Content is not available. Reason: {0}")]
ContentUnavailable(Cow<'static, str>), ContentUnavailable(Cow<'static, str>),
#[error("deserialization error: {0}")] #[error("deserialization error: {0}")]
@ -84,3 +88,23 @@ pub enum ExtractionError {
#[error("Warnings during deserialization/mapping")] #[error("Warnings during deserialization/mapping")]
DeserializationWarnings, 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(_)
)
}
}

View file

@ -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. // This video is geoblocked outside of Japan, so expect this test case to fail when using a Japanese IP address.
#[case::geoblock( #[case::geoblock(
"sJL6WA-aGkQ", "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( #[case::drm(
"1bfOsni7EgI", "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(
// #[case::private( "s7_qI6_mIXc",
// "s7_qI6_mIXc", "extraction error: Video cant be played because of being private. Reason (from YT): "
// "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::age_restricted("CUO8secmc0g", "extraction error: Video is age restricted")]
#[tokio::test] #[tokio::test]
async fn get_player_error(#[case] id: &str, #[case] msg: &str) { async fn get_player_error(#[case] id: &str, #[case] msg: &str) {
let rp = RustyPipe::builder().strict().build(); 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 //#PLAYLIST