diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
index 6c7ac66b8..10a20393b 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
@@ -45,6 +45,7 @@ import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
+import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_UNKNOWN;
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.UTF_8;
@@ -437,6 +438,20 @@ public class YoutubeStreamExtractor extends StreamExtractor {
return fixThumbnailUrl(url);
}
+ @Override
+ public long getUploaderSubscriberCount() throws ParsingException {
+ final JsonObject videoOwnerRenderer = JsonUtils.getObject(videoSecondaryInfoRenderer, "owner.videoOwnerRenderer");
+ if (videoOwnerRenderer.has("subscriberCountText")) {
+ try {
+ return Utils.mixedNumberWordToLong(getTextFromObject(videoOwnerRenderer.getObject("subscriberCountText")));
+ } catch (final NumberFormatException e) {
+ throw new ParsingException("Could not get subscriber count", e);
+ }
+ } else {
+ return ITEM_COUNT_UNKNOWN;
+ }
+ }
+
@Nonnull
@Override
public String getDashMpdUrl() throws ParsingException {
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java
index b78f8664e..f7bac660e 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java
@@ -202,6 +202,17 @@ public abstract class StreamExtractor extends Extractor {
return false;
}
+ /**
+ * The subscriber count of the uploader.
+ * If the subscriber count is not implemented, or is unavailable, return -1
.
+ *
+ * @return the subscriber count of the uploader or -1 if not available
+ * @throws ParsingException
+ */
+ public long getUploaderSubscriberCount() throws ParsingException {
+ return -1;
+ }
+
/**
* The url to the image file/profile picture/avatar of the creator/uploader of the stream.
* If the url is not available you can return an empty String.
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java
index 455f0dc46..42ca495cc 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java
@@ -235,6 +235,11 @@ public class StreamInfo extends Info {
} catch (Exception e) {
streamInfo.addError(e);
}
+ try {
+ streamInfo.setUploaderSubscriberCount(extractor.getUploaderSubscriberCount());
+ } catch (Exception e) {
+ streamInfo.addError(e);
+ }
try {
streamInfo.setSubChannelName(extractor.getSubChannelName());
@@ -367,6 +372,7 @@ public class StreamInfo extends Info {
private String uploaderUrl = "";
private String uploaderAvatarUrl = "";
private boolean uploaderVerified = false;
+ private long uploaderSubscriberCount = -1;
private String subChannelName = "";
private String subChannelUrl = "";
@@ -540,6 +546,14 @@ public class StreamInfo extends Info {
this.uploaderVerified = uploaderVerified;
}
+ public long getUploaderSubscriberCount() {
+ return uploaderSubscriberCount;
+ }
+
+ public void setUploaderSubscriberCount(long uploaderSubscriberCount) {
+ this.uploaderSubscriberCount = uploaderSubscriberCount;
+ }
+
public String getSubChannelName() {
return subChannelName;
}
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java
index f32a60907..da418660a 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/BaseStreamExtractorTest.java
@@ -5,6 +5,7 @@ public interface BaseStreamExtractorTest extends BaseExtractorTest {
void testUploaderName() throws Exception;
void testUploaderUrl() throws Exception;
void testUploaderAvatarUrl() throws Exception;
+ void testSubscriberCount() throws Exception;
void testSubChannelName() throws Exception;
void testSubChannelUrl() throws Exception;
void testSubChannelAvatarUrl() throws Exception;
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
index 1563abac4..5f02d55a5 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/DefaultStreamExtractorTest.java
@@ -45,6 +45,7 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest expectedDescriptionContains() {
return Arrays.asList("https://www.youtube.com/channel/UC7l23W7gFi4Uho6WSzckZRA",
"https://www.handcraftpictures.com/");
@@ -182,6 +183,7 @@ public class YoutubeStreamExtractorDefaultTest {
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
@Override public String expectedUploaderName() { return "Unbox Therapy"; }
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCsTcErHg8oDvUnTzoqsYeNw"; }
+ @Override public long expectedUploaderSubscriberCountAtLeast() { return 18_000_000; }
@Override public List expectedDescriptionContains() {
return Arrays.asList("https://www.youtube.com/watch?v=X7FLCHVXpsA&list=PL7u4lWXQ3wfI_7PgX0C-VTiwLeu0S4v34",
"https://www.youtube.com/watch?v=Lqv6G0pDNnw&list=PL7u4lWXQ3wfI_7PgX0C-VTiwLeu0S4v34",
@@ -274,6 +276,7 @@ public class YoutubeStreamExtractorDefaultTest {
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
@Override public String expectedUploaderName() { return "tagesschau"; }
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UC5NOEUbkLheQcaaRldYW5GA"; }
+ @Override public long expectedUploaderSubscriberCountAtLeast() { return 1_000_000; }
@Override public boolean expectedUploaderVerified() { return true; }
@Override public List expectedDescriptionContains() {
return Arrays.asList("Themen der Sendung", "07:15", "Wetter", "Sendung nachträglich bearbeitet");
@@ -336,6 +339,7 @@ public class YoutubeStreamExtractorDefaultTest {
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
@Override public String expectedUploaderName() { return "maiLab"; }
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCyHDQ5C6z1NDmJ4g6SerW8g"; }
+ @Override public long expectedUploaderSubscriberCountAtLeast() { return 1_400_000; }
@Override public List expectedDescriptionContains() {return Arrays.asList("Vitamin", "2:44", "Was ist Vitamin D?");}
@Override public boolean expectedUploaderVerified() { return true; }
@Override public long expectedLength() { return 1010; }
@@ -405,6 +409,7 @@ public class YoutubeStreamExtractorDefaultTest {
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
@Override public String expectedUploaderName() { return "Dinge Erklärt – Kurzgesagt"; }
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCwRH985XgMYXQ6NxXDo8npw"; }
+ @Override public long expectedUploaderSubscriberCountAtLeast() { return 1_500_000; }
@Override public List expectedDescriptionContains() { return Arrays.asList("Lasst uns abtauchen!", "Angebot von funk", "Dinge"); }
@Override public long expectedLength() { return 631; }
@Override public long expectedTimestamp() { return TIMESTAMP; }