Merge pull request #406 from Stypox/fix-decrypt

[YouTube] Fix some decryption exceptions by retrying
This commit is contained in:
Tobias Groza 2020-10-15 22:03:25 +02:00 committed by GitHub
commit 527945eadb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 39 additions and 30 deletions

View File

@ -633,11 +633,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
final String url = getUrl() + "&pbj=1"; final String url = getUrl() + "&pbj=1";
final String playerUrl;
initialAjaxJson = getJsonResponse(url, getExtractorLocalization()); initialAjaxJson = getJsonResponse(url, getExtractorLocalization());
final String playerUrl;
if (initialAjaxJson.getObject(2).has("response")) { // age-restricted videos if (initialAjaxJson.getObject(2).has("response")) { // age-restricted videos
initialData = initialAjaxJson.getObject(2).getObject("response"); initialData = initialAjaxJson.getObject(2).getObject("response");
ageLimit = 18; ageLimit = 18;
@ -647,12 +646,31 @@ public class YoutubeStreamExtractor extends StreamExtractor {
final String infoPageResponse = downloader.get(videoInfoUrl, getExtractorLocalization()).responseBody(); final String infoPageResponse = downloader.get(videoInfoUrl, getExtractorLocalization()).responseBody();
videoInfoPage.putAll(Parser.compatParseMap(infoPageResponse)); videoInfoPage.putAll(Parser.compatParseMap(infoPageResponse));
playerUrl = info.url; playerUrl = info.url;
} else {
initialData = initialAjaxJson.getObject(3).getObject("response");
ageLimit = NO_AGE_LIMIT;
playerArgs = getPlayerArgs(initialAjaxJson.getObject(2).getObject("player")); } else {
playerUrl = getPlayerUrl(initialAjaxJson.getObject(2).getObject("player")); ageLimit = NO_AGE_LIMIT;
JsonObject playerConfig;
// sometimes at random YouTube does not provide the player config,
// so just retry the same request three times
int attempts = 2;
while (true) {
playerConfig = initialAjaxJson.getObject(2).getObject("player", null);
if (playerConfig != null) {
break;
}
if (attempts <= 0) {
throw new ParsingException(
"YouTube did not provide player config even after three attempts");
}
initialAjaxJson = getJsonResponse(url, getExtractorLocalization());
--attempts;
}
initialData = initialAjaxJson.getObject(3).getObject("response");
playerArgs = getPlayerArgs(playerConfig);
playerUrl = getPlayerUrl(playerConfig);
} }
playerResponse = getPlayerResponse(); playerResponse = getPlayerResponse();
@ -674,36 +692,27 @@ public class YoutubeStreamExtractor extends StreamExtractor {
} }
} }
private JsonObject getPlayerArgs(JsonObject playerConfig) throws ParsingException { private JsonObject getPlayerArgs(final JsonObject playerConfig) throws ParsingException {
JsonObject playerArgs;
//attempt to load the youtube js player JSON arguments //attempt to load the youtube js player JSON arguments
try { final JsonObject playerArgs = playerConfig.getObject("args", null);
playerArgs = playerConfig.getObject("args"); if (playerArgs == null) {
} catch (Exception e) { throw new ParsingException("Could not extract args from YouTube player config");
throw new ParsingException("Could not parse yt player config", e);
} }
return playerArgs; return playerArgs;
} }
private String getPlayerUrl(JsonObject playerConfig) throws ParsingException { private String getPlayerUrl(final JsonObject playerConfig) throws ParsingException {
try { // The Youtube service needs to be initialized by downloading the
// The Youtube service needs to be initialized by downloading the // js-Youtube-player. This is done in order to get the algorithm
// js-Youtube-player. This is done in order to get the algorithm // for decrypting cryptic signatures inside certain stream URLs.
// for decrypting cryptic signatures inside certain stream urls. final String playerUrl = playerConfig.getObject("assets").getString("js");
String playerUrl;
JsonObject ytAssets = playerConfig.getObject("assets"); if (playerUrl == null) {
playerUrl = ytAssets.getString("js"); throw new ParsingException("Could not extract js URL from YouTube player config");
} else if (playerUrl.startsWith("//")) {
if (playerUrl.startsWith("//")) { return HTTPS + playerUrl;
playerUrl = HTTPS + playerUrl;
}
return playerUrl;
} catch (Exception e) {
throw new ParsingException("Could not load decryption code for the Youtube service.", e);
} }
return playerUrl;
} }
private JsonObject getPlayerResponse() throws ParsingException { private JsonObject getPlayerResponse() throws ParsingException {