diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/PaidContentException.java b/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/PaidContentException.java new file mode 100644 index 000000000..f49c75046 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/PaidContentException.java @@ -0,0 +1,11 @@ +package org.schabi.newpipe.extractor.exceptions; + +public class PaidContentException extends ContentNotAvailableException { + public PaidContentException(String message) { + super(message); + } + + public PaidContentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/PrivateContentException.java b/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/PrivateContentException.java new file mode 100644 index 000000000..e6a1b66c8 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/PrivateContentException.java @@ -0,0 +1,11 @@ +package org.schabi.newpipe.extractor.exceptions; + +public class PrivateContentException extends ContentNotAvailableException { + public PrivateContentException(String message) { + super(message); + } + + public PrivateContentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/YoutubeMusicPremiumContentException.java b/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/YoutubeMusicPremiumContentException.java new file mode 100644 index 000000000..b57debb2b --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/exceptions/YoutubeMusicPremiumContentException.java @@ -0,0 +1,11 @@ +package org.schabi.newpipe.extractor.exceptions; + +public class YoutubeMusicPremiumContentException extends ContentNotAvailableException { + public YoutubeMusicPremiumContentException() { + super("This video is a YouTube Music Premium video"); + } + + public YoutubeMusicPremiumContentException(Throwable cause) { + super("This video is a YouTube Music Premium video", cause); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index 39418f612..eba4d8295 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -18,8 +18,12 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException; +import org.schabi.newpipe.extractor.exceptions.PaidContentException; import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.exceptions.PrivateContentException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; +import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException; import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.Localization; @@ -720,10 +724,46 @@ public class YoutubeStreamExtractor extends StreamExtractor { final JsonObject playabilityStatus = playerResponse.getObject("playabilityStatus"); final String status = playabilityStatus.getString("status"); - // If status exist, and is not "OK", throw a ContentNotAvailableException with the reason. + // If status exist, and is not "OK", throw the specific exception based on error message + // or a ContentNotAvailableException with the reason text if it's an unknown reason. if (status != null && !status.toLowerCase().equals("ok")) { final String reason = playabilityStatus.getString("reason"); - throw new ContentNotAvailableException("Got error: \"" + reason + "\""); + + if (status.toLowerCase().equals("login_required")) { + if (reason == null) { + final String message = playabilityStatus.getArray("messages").getString(0); + if (message != null && message.equals("This is a private video. Please sign in to verify that you may see it.")) { + throw new PrivateContentException("This video is private."); + } + } + } + if (status.toLowerCase().equals("unplayable")) { + if (reason != null) { + if (reason.equals("This video is only available to Music Premium members")) { + throw new YoutubeMusicPremiumContentException(); + } + if (reason.equals("This video requires payment to watch.")) { + throw new PaidContentException("This video is a paid video"); + } + if (reason.equals("Join this channel to get access to members-only content like this video, and other exclusive perks.")) { + throw new PaidContentException("This video is only available for members of the channel of this video"); + } + if (reason.equals("Video unavailable")) { + final String detailedErrorMessage = playabilityStatus.getObject("errorScreen") + .getObject("playerErrorMessageRenderer") + .getObject("subreason") + .getArray("runs") + .getObject(0) + .getString("text"); + if (detailedErrorMessage != null) { + if (detailedErrorMessage.equals("The uploader has not made this video available in your country.")) { + throw new GeographicRestrictionException("This video is not available in user's country."); + } + } + } + } + throw new ContentNotAvailableException("Got error: \"" + reason + "\""); + } } } @@ -774,7 +814,6 @@ public class YoutubeStreamExtractor extends StreamExtractor { } } - private String getDeobfuscationFuncName(final String playerCode) throws DeobfuscateException { Parser.RegexException exception = null; for (final String regex : REGEXES) {