From b84d42f931c0f34a748567dadb0682b23b44c14b Mon Sep 17 00:00:00 2001 From: Igor Date: Mon, 18 Oct 2021 21:08:26 +0300 Subject: [PATCH] spoof android to fix throttling speed issue --- README.md | 4 +- pom.xml | 2 +- .../kiulian/downloader/parser/ParserImpl.java | 71 +++++++++++++++++-- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ca92199..685788a 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ Include com.github.sealedtx java-youtube-downloader - 3.0.1 + 3.0.2 ``` @@ -283,7 +283,7 @@ allprojects { ``` ```gradle dependencies { - implementation 'com.github.sealedtx:java-youtube-downloader:3.0.1' + implementation 'com.github.sealedtx:java-youtube-downloader:3.0.2' } ``` ### Android diff --git a/pom.xml b/pom.xml index fe796eb..0467e15 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.ikiulian java-youtube-downloader - 3.0.1 + 3.0.2 Java youtube video downloader Java parser for retrieving youtube video meta info diff --git a/src/main/java/com/github/kiulian/downloader/parser/ParserImpl.java b/src/main/java/com/github/kiulian/downloader/parser/ParserImpl.java index c9eea3f..db58215 100644 --- a/src/main/java/com/github/kiulian/downloader/parser/ParserImpl.java +++ b/src/main/java/com/github/kiulian/downloader/parser/ParserImpl.java @@ -1,7 +1,6 @@ package com.github.kiulian.downloader.parser; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -33,6 +32,7 @@ import java.util.concurrent.Future; public class ParserImpl implements Parser { + private static final String ANDROID_APIKEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; private final Config config; private final Downloader downloader; @@ -61,7 +61,65 @@ public Response parseVideo(RequestVideoInfo request) { } } - public VideoInfo parseVideo(String videoId, YoutubeCallback callback) throws YoutubeException { + private VideoInfo parseVideo(String videoId, YoutubeCallback callback) throws YoutubeException { + // try to spoof android + // workaround for issue https://github.com/sealedtx/java-youtube-downloader/issues/97 + VideoInfo videoInfo = parseVideoAndroid(videoId); + if (videoInfo == null) { + videoInfo = parseVideoWeb(videoId, callback); + } + if (callback != null) { + callback.onFinished(videoInfo); + } + return videoInfo; + } + + private VideoInfo parseVideoAndroid(String videoId) throws YoutubeException { + String url = "https://youtubei.googleapis.com/youtubei/v1/player?key=" + ANDROID_APIKEY; + + String body = + "{" + + " \"videoId\": \"" + videoId + "\"," + + " \"context\": {" + + " \"client\": {" + + " \"hl\": \"en\"," + + " \"gl\": \"US\"," + + " \"clientName\": \"ANDROID\"," + + " \"clientVersion\": \"16.02\"" + + " }" + + " }" + + "}"; + + RequestWebpage request = new RequestWebpage(url, "POST", body) + .header("Content-Type", "application/json"); + + Response response = downloader.downloadWebpage(request); + if (!response.ok()) { + return null; + } + + JSONObject playerResponse; + try { + playerResponse = JSONObject.parseObject(response.data()); + } catch (Exception ignore) { + return null; + } + + VideoDetails videoDetails = parseVideoDetails(videoId, playerResponse); + if (videoDetails.isDownloadable()) { + JSONObject context = playerResponse.getJSONObject("responseContext"); + String clientVersion = extractor.extractClientVersionFromContext(context); + List formats = parseFormats(playerResponse, null, clientVersion); + + List subtitlesInfo = parseCaptions(playerResponse); + return new VideoInfo(videoDetails, formats, subtitlesInfo); + } else { + return new VideoInfo(videoDetails, Collections.emptyList(), Collections.emptyList()); + } + + } + + private VideoInfo parseVideoWeb(String videoId, YoutubeCallback callback) throws YoutubeException { String htmlUrl = "https://www.youtube.com/watch?v=" + videoId; Response response = downloader.downloadWebpage(new RequestWebpage(htmlUrl)); @@ -198,7 +256,7 @@ private Format parseFormat(JSONObject json, String jsUrl, Itag itag, boolean isA if (urlWithSig.contains("signature") || (!jsonCipher.containsKey("s") && (urlWithSig.contains("&sig=") || urlWithSig.contains("&lsig=")))) { // do nothing, this is pre-signed videos with signature - } else { + } else if (jsUrl != null) { String s = jsonCipher.getString("s"); try { s = URLDecoder.decode(s, "UTF-8"); @@ -210,6 +268,8 @@ private Format parseFormat(JSONObject json, String jsUrl, Itag itag, boolean isA String signature = cipher.getSignature(s); String decipheredUrl = urlWithSig + "&sig=" + signature; json.put("url", decipheredUrl); + } else { + throw new YoutubeException.BadPageException("deciphering is required but no js url"); } } @@ -403,7 +463,8 @@ private void populatePlaylist(JSONObject content, List vid } } - private void loadPlaylistContinuation(String continuation, String ctp, List videos, String clientVersion) throws YoutubeException { JSONObject content; + private void loadPlaylistContinuation(String continuation, String ctp, List videos, String clientVersion) throws YoutubeException { + JSONObject content; String url = "https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; JSONObject body = new JSONObject() @@ -483,7 +544,7 @@ private PlaylistInfo parseChannelsUploads(String channelId, YoutubeCallback