[YouTube] Parse watching count in live streams items

This commit is contained in:
Mauricio Colli 2019-04-28 17:03:17 -03:00
parent 3638f0e0ea
commit d8280ce0da
No known key found for this signature in database
GPG Key ID: F200BFD6F29DDD85
3 changed files with 86 additions and 15 deletions

View File

@ -304,12 +304,65 @@ public class YoutubeStreamExtractor extends StreamExtractor {
public long getViewCount() throws ParsingException { public long getViewCount() throws ParsingException {
assertPageFetched(); assertPageFetched();
try { try {
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return getLiveStreamWatchingCount();
}
return Long.parseLong(doc.select("meta[itemprop=interactionCount]").attr(CONTENT)); return Long.parseLong(doc.select("meta[itemprop=interactionCount]").attr(CONTENT));
} catch (Exception e) {//todo: find fallback method } catch (Exception e) {//todo: find fallback method
throw new ParsingException("Could not get number of views", e); throw new ParsingException("Could not get number of views", e);
} }
} }
private long getLiveStreamWatchingCount() throws ExtractionException, IOException, JsonParserException {
// https://www.youtube.com/youtubei/v1/updated_metadata?alt=json&key=
String innerTubeKey = null, clientVersion = null;
if (playerArgs != null && !playerArgs.isEmpty()) {
innerTubeKey = playerArgs.getString("innertube_api_key");
clientVersion = playerArgs.getString("innertube_context_client_version");
} else if (!videoInfoPage.isEmpty()) {
innerTubeKey = videoInfoPage.get("innertube_api_key");
clientVersion = videoInfoPage.get("innertube_context_client_version");
}
if (innerTubeKey == null || innerTubeKey.isEmpty()) {
throw new ExtractionException("Couldn't get innerTube key");
}
if (clientVersion == null || clientVersion.isEmpty()) {
throw new ExtractionException("Couldn't get innerTube client version");
}
final String metadataUrl = "https://www.youtube.com/youtubei/v1/updated_metadata?alt=json&key=" + innerTubeKey;
final byte[] dataBody = ("{\"context\":{\"client\":{\"clientName\":1,\"clientVersion\":\"" + clientVersion + "\"}}" +
",\"videoId\":\"" + getId() + "\"}").getBytes("UTF-8");
final Response response = getDownloader().execute(Request.newBuilder()
.post(metadataUrl, dataBody)
.addHeader("Content-Type", "application/json")
.build());
final JsonObject jsonObject = JsonParser.object().from(response.responseBody());
for (Object actionEntry : jsonObject.getArray("actions")) {
if (!(actionEntry instanceof JsonObject)) continue;
final JsonObject entry = (JsonObject) actionEntry;
final JsonObject updateViewershipAction = entry.getObject("updateViewershipAction", null);
if (updateViewershipAction == null) continue;
final JsonArray viewCountRuns = JsonUtils.getArray(updateViewershipAction, "viewership.videoViewCountRenderer.viewCount.runs");
if (viewCountRuns.isEmpty()) continue;
final JsonObject textObject = viewCountRuns.getObject(0);
if (!textObject.has("text")) {
throw new ExtractionException("Response don't have \"text\" element");
}
return Long.parseLong(Utils.removeNonDigitCharacters(textObject.getString("text")));
}
throw new ExtractionException("Could not find correct results in response");
}
@Override @Override
public long getLikeCount() throws ParsingException { public long getLikeCount() throws ParsingException {
assertPageFetched(); assertPageFetched();

View File

@ -3,10 +3,10 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -141,6 +141,10 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
@Override @Override
public String getTextualUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return null;
}
if (cachedUploadDate != null) { if (cachedUploadDate != null) {
return cachedUploadDate; return cachedUploadDate;
} }
@ -160,9 +164,12 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
@Override @Override
public Calendar getUploadDate() throws ParsingException { public Calendar getUploadDate() throws ParsingException {
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return null;
}
String textualUploadDate = getTextualUploadDate(); String textualUploadDate = getTextualUploadDate();
if (timeAgoParser != null if (timeAgoParser != null && textualUploadDate != null && !textualUploadDate.isEmpty()) {
&& textualUploadDate != null && !"".equals(textualUploadDate)) {
return timeAgoParser.parse(textualUploadDate); return timeAgoParser.parse(textualUploadDate);
} else { } else {
return null; return null;
@ -172,24 +179,35 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
@Override @Override
public long getViewCount() throws ParsingException { public long getViewCount() throws ParsingException {
String input; String input;
try {
// TODO: Return the actual live stream's watcher count
// -1 for no view count
if (getStreamType() == StreamType.LIVE_STREAM) return -1;
Element meta = item.select("div[class=\"yt-lockup-meta\"]").first(); if (getStreamType().equals(StreamType.LIVE_STREAM)) {
if (meta == null) return -1; Element meta = item.select("ul[class=\"yt-lockup-meta-info\"]").first();
if (meta == null) return 0;
// This case can happen if google releases a special video final Elements li = meta.select("li");
if(meta.select("li").size() < 2) return -1; if (li.isEmpty()) return 0;
input = meta.select("li").get(1).text(); input = li.first().text();
} else {
try {
Element meta = item.select("div[class=\"yt-lockup-meta\"]").first();
if (meta == null) return -1;
} catch (IndexOutOfBoundsException e) { // This case can happen if google releases a special video
throw new ParsingException("Could not parse yt-lockup-meta although available: " + getUrl(), e); if (meta.select("li").size() < 2) return -1;
input = meta.select("li").get(1).text();
} catch (IndexOutOfBoundsException e) {
throw new ParsingException("Could not parse yt-lockup-meta although available: " + getUrl(), e);
}
}
if (input == null) {
throw new ParsingException("Input is null");
} }
try { try {
return Long.parseLong(Utils.removeNonDigitCharacters(input)); return Long.parseLong(Utils.removeNonDigitCharacters(input));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// if this happens the video probably has no views // if this happens the video probably has no views

View File

@ -68,7 +68,7 @@ public class YoutubeStreamExtractorLivestreamTest {
@Test @Test
public void testGetViewCount() throws ParsingException { public void testGetViewCount() throws ParsingException {
long count = extractor.getViewCount(); long count = extractor.getViewCount();
assertTrue(Long.toString(count), count >= 7148995); assertTrue(Long.toString(count), count > -1);
} }
@Test @Test