diff --git a/services/youtube/YoutubeStreamExtractor.java b/services/youtube/YoutubeStreamExtractor.java index d6631ac0..8238b90e 100644 --- a/services/youtube/YoutubeStreamExtractor.java +++ b/services/youtube/YoutubeStreamExtractor.java @@ -260,7 +260,7 @@ public class YoutubeStreamExtractor extends StreamExtractor { Parser.matchGroup1("ytplayer.config\\s*=\\s*(\\{.*?\\});", pageContent); return new JSONObject(ytPlayerConfigRaw); } catch (Parser.RegexException e) { - String errorReason = findErrorReason(doc); + String errorReason = getErrorMessage(); switch(errorReason) { case "GEMA": throw new GemaException(errorReason); @@ -948,15 +948,28 @@ public class YoutubeStreamExtractor extends StreamExtractor { return result == null ? "" : result.toString(); } - private String findErrorReason(Document doc) { - String errorMessage = doc.select("h1[id=\"unavailable-message\"]").first().text(); - if(errorMessage.contains("GEMA")) { + + /** + * {@inheritDoc} + */ + public String getErrorMessage() { + String errorMessage = doc.select("h1[id=\"unavailable-message\"]").first().text(); + StringBuilder errorReason; + + if (errorMessage == null || errorMessage.isEmpty()) { + errorReason = null; + } else if(errorMessage.contains("GEMA")) { // Gema sometimes blocks youtube music content in germany: // https://www.gema.de/en/ // Detailed description: // https://en.wikipedia.org/wiki/GEMA_%28German_organization%29 - return "GEMA"; + errorReason = new StringBuilder("GEMA"); + } else { + errorReason = new StringBuilder(errorMessage); + errorReason.append(" "); + errorReason.append(doc.select("[id=\"unavailable-submessage\"]").first().text()); } - return ""; + + return errorReason != null ? errorReason.toString() : null; } } diff --git a/stream_info/StreamExtractor.java b/stream_info/StreamExtractor.java index 1b7c1b5a..760e5833 100644 --- a/stream_info/StreamExtractor.java +++ b/stream_info/StreamExtractor.java @@ -49,7 +49,7 @@ public abstract class StreamExtractor { } } - public class ContentNotAvailableException extends ParsingException { + public static class ContentNotAvailableException extends ParsingException { public ContentNotAvailableException(String message) { super(message); } @@ -101,4 +101,11 @@ public abstract class StreamExtractor { public int getServiceId() { return serviceId; } + + /** + * Analyses the webpage's document and extracts any error message there might be. + * + * @return Error message; null if there is no error message. + */ + public abstract String getErrorMessage(); } diff --git a/stream_info/StreamInfo.java b/stream_info/StreamInfo.java index e99293ae..8aa56e59 100644 --- a/stream_info/StreamInfo.java +++ b/stream_info/StreamInfo.java @@ -74,19 +74,34 @@ public class StreamInfo extends AbstractStreamInfo { /**Fills out the video info fields which are common to all services. * Probably needs to be overridden by subclasses*/ public static StreamInfo getVideoInfo(StreamExtractor extractor) - throws ExtractionException, IOException { + throws ExtractionException, StreamExtractor.ContentNotAvailableException { StreamInfo streamInfo = new StreamInfo(); - streamInfo = extractImportantData(streamInfo, extractor); - streamInfo = extractStreams(streamInfo, extractor); - streamInfo = extractOptionalData(streamInfo, extractor); + try { + streamInfo = extractImportantData(streamInfo, extractor); + streamInfo = extractStreams(streamInfo, extractor); + streamInfo = extractOptionalData(streamInfo, extractor); + } catch (ExtractionException e) { + // Currently YouTube does not distinguish between age restricted videos and videos blocked + // by country. This means that during the initialisation of the extractor, the extractor + // will assume that a video is age restricted while in reality it it blocked by country. + // + // We will now detect whether the video is blocked by country or not. + String errorMsg = extractor.getErrorMessage(); + + if (errorMsg != null) { + throw new StreamExtractor.ContentNotAvailableException(errorMsg); + } else { + throw e; + } + } return streamInfo; } private static StreamInfo extractImportantData( StreamInfo streamInfo, StreamExtractor extractor) - throws ExtractionException, IOException { + throws ExtractionException { /* ---- important data, withoug the video can't be displayed goes here: ---- */ // if one of these is not available an exception is meant to be thrown directly into the frontend. @@ -104,7 +119,7 @@ public class StreamInfo extends AbstractStreamInfo { || (streamInfo.id == null || streamInfo.id.isEmpty()) || (streamInfo.title == null /* streamInfo.title can be empty of course */) || (streamInfo.age_limit == -1)) { - throw new ExtractionException("Some importand stream information was not given."); + throw new ExtractionException("Some important stream information was not given."); } return streamInfo; @@ -112,7 +127,7 @@ public class StreamInfo extends AbstractStreamInfo { private static StreamInfo extractStreams( StreamInfo streamInfo, StreamExtractor extractor) - throws ExtractionException, IOException { + throws ExtractionException { /* ---- stream extraction goes here ---- */ // At least one type of stream has to be available, // otherwise an exception will be thrown directly into the frontend.