Use the youtubei API for YouTube videos + update client version

Update the hardcoded client version to 2.20210520.09.00
Use the player and next endpoints of the Innertube API for YouTube videos
This commit is contained in:
TiA4f8R 2021-05-23 17:55:19 +02:00
parent f73c923f60
commit e7d589edbf
No known key found for this signature in database
GPG key ID: E6D3E7F5949450DD
2 changed files with 24 additions and 17 deletions

View file

@ -64,7 +64,7 @@ public class YoutubeParsingHelper {
private YoutubeParsingHelper() { private YoutubeParsingHelper() {
} }
private static final String HARDCODED_CLIENT_VERSION = "2.20210506.07.00"; private static final String HARDCODED_CLIENT_VERSION = "2.20210520.09.00";
private static final String HARDCODED_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; private static final String HARDCODED_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
private static final String YOUTUBEI_V1_URL = "https://www.youtube.com/youtubei/v1/"; private static final String YOUTUBEI_V1_URL = "https://www.youtube.com/youtubei/v1/";
private static String clientVersion; private static String clientVersion;

View file

@ -4,6 +4,7 @@ import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import com.grack.nanojson.JsonWriter;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
@ -27,6 +28,7 @@ import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException; import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
@ -52,6 +54,7 @@ import java.util.*;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.UTF_8;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/* /*
@ -89,11 +92,15 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Nullable @Nullable
private static String cachedDeobfuscationCode = null; private static String cachedDeobfuscationCode = null;
@Nullable
private String playerJsUrl = null;
@Nonnull @Nonnull
private final Map<String, String> videoInfoPage = new HashMap<>(); private final Map<String, String> videoInfoPage = new HashMap<>();
private JsonArray initialAjaxJson; private JsonArray initialAjaxJson;
private JsonObject initialData; private JsonObject initialData;
private JsonObject playerResponse; private JsonObject playerResponse;
private JsonObject nextResponse;
private JsonObject videoPrimaryInfoRenderer; private JsonObject videoPrimaryInfoRenderer;
private JsonObject videoSecondaryInfoRenderer; private JsonObject videoSecondaryInfoRenderer;
private int ageLimit = -1; private int ageLimit = -1;
@ -632,7 +639,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector( final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(
getServiceId()); getServiceId());
final JsonArray results = initialData.getObject("contents") final JsonArray results = nextResponse.getObject("contents")
.getObject("twoColumnWatchNextResults").getObject("secondaryResults") .getObject("twoColumnWatchNextResults").getObject("secondaryResults")
.getObject("secondaryResults").getArray("results"); .getObject("secondaryResults").getArray("results");
@ -684,17 +691,16 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException { throws IOException, ExtractionException {
initialAjaxJson = getJsonResponse(getUrl() + "&pbj=1", getExtractorLocalization()); final String videoId = super.getId();
final Localization localization = getExtractorLocalization();
final ContentCountry contentCountry = getExtractorContentCountry();
final byte[] body = JsonWriter.string(prepareJsonBuilder(localization,
contentCountry)
.value("videoId", videoId)
.done())
.getBytes(UTF_8);
playerResponse = getJsonPostResponse("player", body, localization);
initialData = initialAjaxJson.getObject(3).getObject("response", null);
if (initialData == null) {
initialData = initialAjaxJson.getObject(2).getObject("response", null);
if (initialData == null) {
throw new ParsingException("Could not get initial data");
}
}
playerResponse = initialAjaxJson.getObject(2).getObject("playerResponse", null);
// Save the playerResponse from the youtube.com website, // Save the playerResponse from the youtube.com website,
// because there can be restrictions on the embedded player. // because there can be restrictions on the embedded player.
// E.g. if a video is age-restricted, the embedded player's playabilityStatus says, // E.g. if a video is age-restricted, the embedded player's playabilityStatus says,
@ -770,6 +776,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
throw new ContentNotAvailableException("Got error: \"" + reason + "\""); throw new ContentNotAvailableException("Got error: \"" + reason + "\"");
} }
nextResponse = getJsonPostResponse("next", body, localization);
} }
private void fetchVideoInfoPage() throws ParsingException, ReCaptchaException, IOException { private void fetchVideoInfoPage() throws ParsingException, ReCaptchaException, IOException {
@ -900,7 +907,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
private JsonObject getVideoPrimaryInfoRenderer() throws ParsingException { private JsonObject getVideoPrimaryInfoRenderer() throws ParsingException {
if (this.videoPrimaryInfoRenderer != null) return this.videoPrimaryInfoRenderer; if (this.videoPrimaryInfoRenderer != null) return this.videoPrimaryInfoRenderer;
final JsonArray contents = initialData.getObject("contents") final JsonArray contents = nextResponse.getObject("contents")
.getObject("twoColumnWatchNextResults").getObject("results").getObject("results") .getObject("twoColumnWatchNextResults").getObject("results").getObject("results")
.getArray("contents"); .getArray("contents");
JsonObject videoPrimaryInfoRenderer = null; JsonObject videoPrimaryInfoRenderer = null;
@ -924,7 +931,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
private JsonObject getVideoSecondaryInfoRenderer() throws ParsingException { private JsonObject getVideoSecondaryInfoRenderer() throws ParsingException {
if (this.videoSecondaryInfoRenderer != null) return this.videoSecondaryInfoRenderer; if (this.videoSecondaryInfoRenderer != null) return this.videoSecondaryInfoRenderer;
final JsonArray contents = initialData.getObject("contents") final JsonArray contents = nextResponse.getObject("contents")
.getObject("twoColumnWatchNextResults").getObject("results").getObject("results") .getObject("twoColumnWatchNextResults").getObject("results").getObject("results")
.getArray("contents"); .getArray("contents");
JsonObject videoSecondaryInfoRenderer = null; JsonObject videoSecondaryInfoRenderer = null;
@ -1143,8 +1150,8 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
public List<StreamSegment> getStreamSegments() throws ParsingException { public List<StreamSegment> getStreamSegments() throws ParsingException {
final ArrayList<StreamSegment> segments = new ArrayList<>(); final ArrayList<StreamSegment> segments = new ArrayList<>();
if (initialData.has("engagementPanels")) { if (nextResponse.has("engagementPanels")) {
final JsonArray panels = initialData.getArray("engagementPanels"); final JsonArray panels = nextResponse.getArray("engagementPanels");
JsonArray segmentsArray = null; JsonArray segmentsArray = null;
// Search for correct panel containing the data // Search for correct panel containing the data
@ -1207,7 +1214,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
public List<MetaInfo> getMetaInfo() throws ParsingException { public List<MetaInfo> getMetaInfo() throws ParsingException {
return YoutubeParsingHelper.getMetaInfo( return YoutubeParsingHelper.getMetaInfo(
initialData.getObject("contents").getObject("twoColumnWatchNextResults") nextResponse.getObject("contents").getObject("twoColumnWatchNextResults")
.getObject("results").getObject("results").getArray("contents")); .getObject("results").getObject("results").getArray("contents"));
} }
} }