Add uploader verified by service extraction

This commit is contained in:
bopol 2021-01-22 01:44:58 +01:00
parent db253e202b
commit 1a322ad8ed
54 changed files with 474 additions and 91 deletions

View File

@ -40,4 +40,6 @@ public abstract class ChannelExtractor extends ListExtractor<StreamInfoItem> {
public abstract String getParentChannelName() throws ParsingException; public abstract String getParentChannelName() throws ParsingException;
public abstract String getParentChannelUrl() throws ParsingException; public abstract String getParentChannelUrl() throws ParsingException;
public abstract String getParentChannelAvatarUrl() throws ParsingException; public abstract String getParentChannelAvatarUrl() throws ParsingException;
public abstract boolean isVerified() throws ParsingException;
} }

View File

@ -27,7 +27,7 @@ public class ChannelInfoItem extends InfoItem {
private String description; private String description;
private long subscriberCount = -1; private long subscriberCount = -1;
private long streamCount = -1; private long streamCount = -1;
private boolean verified = false;
public ChannelInfoItem(int serviceId, String url, String name) { public ChannelInfoItem(int serviceId, String url, String name) {
super(InfoType.CHANNEL, serviceId, url, name); super(InfoType.CHANNEL, serviceId, url, name);
@ -56,4 +56,12 @@ public class ChannelInfoItem extends InfoItem {
public void setStreamCount(long stream_count) { public void setStreamCount(long stream_count) {
this.streamCount = stream_count; this.streamCount = stream_count;
} }
public boolean isVerified() {
return verified;
}
public void setVerified(boolean verified) {
this.verified = verified;
}
} }

View File

@ -27,5 +27,8 @@ public interface ChannelInfoItemExtractor extends InfoItemExtractor {
String getDescription() throws ParsingException; String getDescription() throws ParsingException;
long getSubscriberCount() throws ParsingException; long getSubscriberCount() throws ParsingException;
long getStreamCount() throws ParsingException; long getStreamCount() throws ParsingException;
boolean isVerified() throws ParsingException;
} }

View File

@ -59,6 +59,12 @@ public class ChannelInfoItemsCollector extends InfoItemsCollector<ChannelInfoIte
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try {
resultItem.setVerified(extractor.isVerified());
} catch (Exception e) {
addError(e);
}
return resultItem; return resultItem;
} }
} }

View File

@ -12,6 +12,7 @@ public class CommentsInfoItem extends InfoItem {
private String uploaderName; private String uploaderName;
private String uploaderAvatarUrl; private String uploaderAvatarUrl;
private String uploaderUrl; private String uploaderUrl;
private boolean uploaderVerified;
private String textualUploadDate; private String textualUploadDate;
@Nullable @Nullable
private DateWrapper uploadDate; private DateWrapper uploadDate;
@ -103,4 +104,12 @@ public class CommentsInfoItem extends InfoItem {
public void setPinned(boolean pinned) { public void setPinned(boolean pinned) {
this.pinned = pinned; this.pinned = pinned;
} }
public void setUploaderVerified(boolean uploaderVerified) {
this.uploaderVerified = uploaderVerified;
}
public boolean isUploaderVerified() {
return uploaderVerified;
}
} }

View File

@ -53,4 +53,9 @@ public interface CommentsInfoItemExtractor extends InfoItemExtractor {
* Whether the comment is pinned * Whether the comment is pinned
*/ */
boolean isPinned() throws ParsingException; boolean isPinned() throws ParsingException;
/**
* Whether the uploader is verified by the service
*/
boolean isUploaderVerified() throws ParsingException;
} }

View File

@ -20,6 +20,7 @@ public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> {
public abstract String getUploaderUrl() throws ParsingException; public abstract String getUploaderUrl() throws ParsingException;
public abstract String getUploaderName() throws ParsingException; public abstract String getUploaderName() throws ParsingException;
public abstract String getUploaderAvatarUrl() throws ParsingException; public abstract String getUploaderAvatarUrl() throws ParsingException;
public abstract boolean isUploaderVerified() throws ParsingException;
public abstract long getStreamCount() throws ParsingException; public abstract long getStreamCount() throws ParsingException;

View File

@ -4,7 +4,6 @@ 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 org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
@ -17,9 +16,8 @@ import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConfe
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import java.io.IOException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException;
public class MediaCCCConferenceExtractor extends ChannelExtractor { public class MediaCCCConferenceExtractor extends ChannelExtractor {
private JsonObject conferenceData; private JsonObject conferenceData;
@ -69,6 +67,11 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
return ""; return "";
} }
@Override
public boolean isVerified() throws ParsingException {
return false;
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() { public InfoItemsPage<StreamInfoItem> getInitialPage() {

View File

@ -126,6 +126,11 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
return conference.getString("conference"); return conference.getString("conference");
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nonnull @Nonnull
@Override @Override
public String getUploaderAvatarUrl() { public String getUploaderAvatarUrl() {
@ -180,7 +185,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
for (int s = 0; s < room.getArray("streams").size(); s++) { for (int s = 0; s < room.getArray("streams").size(); s++) {
final JsonObject stream = room.getArray("streams").getObject(s); final JsonObject stream = room.getArray("streams").getObject(s);
if (stream.getString("type").equals("audio")) { if (stream.getString("type").equals("audio")) {
for (final String type :stream.getObject("urls").keySet()) { for (final String type : stream.getObject("urls").keySet()) {
final JsonObject url = stream.getObject("urls").getObject(type); final JsonObject url = stream.getObject("urls").getObject(type);
audioStreams.add(new AudioStream(url.getString("url"), MediaFormat.getFromSuffix(type), -1)); audioStreams.add(new AudioStream(url.getString("url"), MediaFormat.getFromSuffix(type), -1));
} }
@ -197,7 +202,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
if (stream.getString("type").equals("video")) { if (stream.getString("type").equals("video")) {
final String resolution = stream.getArray("videoSize").getInt(0) + "x" final String resolution = stream.getArray("videoSize").getInt(0) + "x"
+ stream.getArray("videoSize").getInt(1); + stream.getArray("videoSize").getInt(1);
for (final String type :stream.getObject("urls").keySet()) { for (final String type : stream.getObject("urls").keySet()) {
if (!type.equals("hls")) { if (!type.equals("hls")) {
final JsonObject url = stream.getObject("urls").getObject(type); final JsonObject url = stream.getObject("urls").getObject(type);
videoStreams.add(new VideoStream( videoStreams.add(new VideoStream(
@ -218,7 +223,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public List<SubtitlesStream> getSubtitlesDefault(){ public List<SubtitlesStream> getSubtitlesDefault() {
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@ -73,6 +73,11 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
return "https://media.ccc.de/c/" + conferenceInfo.getString("slug"); return "https://media.ccc.de/c/" + conferenceInfo.getString("slug");
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nullable @Nullable
@Override @Override
public String getTextualUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {

View File

@ -68,6 +68,11 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
.getUrl(); // web URL .getUrl(); // web URL
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nullable @Nullable
@Override @Override
public String getTextualUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {

View File

@ -142,6 +142,11 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
return item.getStreamCount(); return item.getStreamCount();
} }
@Override
public boolean isVerified() throws ParsingException {
return false;
}
@Override @Override
public String getName() { public String getName() {
return item.getName(); return item.getName();

View File

@ -4,7 +4,6 @@ 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 org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
@ -13,26 +12,14 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamSegment;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
public class MediaCCCStreamExtractor extends StreamExtractor { public class MediaCCCStreamExtractor extends StreamExtractor {
private JsonObject data; private JsonObject data;
@ -109,6 +96,11 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
.replaceFirst("https://(api\\.)?media\\.ccc\\.de/public/conferences/", ""); .replaceFirst("https://(api\\.)?media\\.ccc\\.de/public/conferences/", "");
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nonnull @Nonnull
@Override @Override
public String getUploaderAvatarUrl() { public String getUploaderAvatarUrl() {

View File

@ -28,6 +28,11 @@ public class MediaCCCConferenceInfoItemExtractor implements ChannelInfoItemExtra
return ListExtractor.ITEM_COUNT_UNKNOWN; return ListExtractor.ITEM_COUNT_UNKNOWN;
} }
@Override
public boolean isVerified() throws ParsingException {
return false;
}
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
return conference.getString("title"); return conference.getString("title");

View File

@ -46,6 +46,11 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
return event.getString("conference_url"); return event.getString("conference_url");
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nullable @Nullable
@Override @Override
public String getTextualUploadDate() { public String getTextualUploadDate() {

View File

@ -83,6 +83,11 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
return ""; return "";
} }
@Override
public boolean isVerified() throws ParsingException {
return false;
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {

View File

@ -90,6 +90,11 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
return baseUrl + value; return baseUrl + value;
} }
@Override
public boolean isVerified() throws ParsingException {
return false;
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {

View File

@ -99,6 +99,11 @@ public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtrac
return false; return false;
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Override @Override
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
return JsonUtils.getString(item, "account.name") + "@" + JsonUtils.getString(item, "account.host"); return JsonUtils.getString(item, "account.name") + "@" + JsonUtils.getString(item, "account.host");

View File

@ -3,7 +3,6 @@ package org.schabi.newpipe.extractor.services.peertube.extractors;
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 org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
@ -17,14 +16,10 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nonnull; import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.COUNT_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.START_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.collectStreamsFrom;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubePlaylistExtractor extends PlaylistExtractor { public class PeertubePlaylistExtractor extends PlaylistExtractor {
@ -59,6 +54,11 @@ public class PeertubePlaylistExtractor extends PlaylistExtractor {
return getBaseUrl() + playlistInfo.getObject("ownerAccount").getObject("avatar").getString("path"); return getBaseUrl() + playlistInfo.getObject("ownerAccount").getObject("avatar").getString("path");
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Override @Override
public long getStreamCount() { public long getStreamCount() {
return playlistInfo.getLong("videosLength"); return playlistInfo.getLong("videosLength");

View File

@ -148,6 +148,11 @@ public class PeertubeStreamExtractor extends StreamExtractor {
return JsonUtils.getString(json, "account.displayName"); return JsonUtils.getString(json, "account.displayName");
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nonnull @Nonnull
@Override @Override
public String getUploaderAvatarUrl() { public String getUploaderAvatarUrl() {

View File

@ -54,6 +54,11 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
.fromId("accounts/" + name + "@" + host, baseUrl).getUrl(); .fromId("accounts/" + name + "@" + host, baseUrl).getUrl();
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Override @Override
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
return JsonUtils.getString(item, "account.displayName"); return JsonUtils.getString(item, "account.displayName");

View File

@ -96,6 +96,11 @@ public class SoundcloudChannelExtractor extends ChannelExtractor {
return ""; return "";
} }
@Override
public boolean isVerified() throws ParsingException {
return user.getBoolean("verified");
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {

View File

@ -40,6 +40,11 @@ public class SoundcloudChannelInfoItemExtractor implements ChannelInfoItemExtrac
return itemObject.getLong("track_count"); return itemObject.getLong("track_count");
} }
@Override
public boolean isVerified() {
return itemObject.getBoolean("verified");
}
@Override @Override
public String getDescription() { public String getDescription() {
return itemObject.getString("description", EMPTY_STRING); return itemObject.getString("description", EMPTY_STRING);

View File

@ -6,9 +6,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import java.util.Objects;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Objects;
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor { public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
private JsonObject json; private JsonObject json;
@ -49,6 +48,11 @@ public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtr
return false; return false;
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return json.getObject("user").getBoolean("verified");
}
@Override @Override
public String getUploaderUrl() { public String getUploaderUrl() {
return json.getObject("user").getString("permalink_url"); return json.getObject("user").getString("permalink_url");

View File

@ -111,6 +111,11 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
return SoundcloudParsingHelper.getAvatarUrl(playlist); return SoundcloudParsingHelper.getAvatarUrl(playlist);
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return playlist.getObject("user").getBoolean("verified");
}
@Override @Override
public long getStreamCount() { public long getStreamCount() {
return playlist.getLong("track_count"); return playlist.getLong("track_count");

View File

@ -131,6 +131,11 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
return SoundcloudParsingHelper.getUploaderName(track); return SoundcloudParsingHelper.getUploaderName(track);
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return track.getObject("user").getBoolean("verified");
}
@Nonnull @Nonnull
@Override @Override
public String getUploaderAvatarUrl() { public String getUploaderAvatarUrl() {

View File

@ -43,6 +43,11 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
return replaceHttpWithHttps(itemObject.getObject("user").getString("permalink_url")); return replaceHttpWithHttps(itemObject.getObject("user").getString("permalink_url"));
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return itemObject.getObject("user").getBoolean("verified");
}
@Override @Override
public String getTextualUploadDate() { public String getTextualUploadDate() {
return itemObject.getString("created_at"); return itemObject.getString("created_at");
@ -64,8 +69,7 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
if (artworkUrl.isEmpty()) { if (artworkUrl.isEmpty()) {
artworkUrl = itemObject.getObject("user").getString("avatar_url"); artworkUrl = itemObject.getObject("user").getString("avatar_url");
} }
String artworkUrlBetterResolution = artworkUrl.replace("large.jpg", "crop.jpg"); return artworkUrl.replace("large.jpg", "crop.jpg");
return artworkUrlBetterResolution;
} }
@Override @Override

View File

@ -804,4 +804,21 @@ public class YoutubeParsingHelper {
} }
return url; return url;
} }
public static boolean isVerified(final JsonArray badges) {
if (Utils.isNullOrEmpty(badges)) {
return false;
}
for (Object badge : badges) {
final String style = ((JsonObject) badge).getObject("metadataBadgeRenderer")
.getString("style");
if (style != null && (style.equals("BADGE_STYLE_TYPE_VERIFIED")
|| style.equals("BADGE_STYLE_TYPE_VERIFIED_ARTIST"))) {
return true;
}
}
return false;
}
} }

View File

@ -216,6 +216,14 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
return ""; return "";
} }
@Override
public boolean isVerified() throws ParsingException {
final JsonArray badges = initialData.getObject("header").getObject("c4TabbedHeaderRenderer")
.getArray("badges");
return YoutubeParsingHelper.isVerified(badges);
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {

View File

@ -5,6 +5,7 @@ import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor; import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
@ -97,6 +98,11 @@ public class YoutubeChannelInfoItemExtractor implements ChannelInfoItemExtractor
} }
} }
@Override
public boolean isVerified() throws ParsingException {
return YoutubeParsingHelper.isVerified(channelInfoItem.getArray("ownerBadges"));
}
@Override @Override
public String getDescription() throws ParsingException { public String getDescription() throws ParsingException {
try { try {

View File

@ -125,6 +125,11 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
return json.has("pinnedCommentBadge"); return json.has("pinnedCommentBadge");
} }
public boolean isUploaderVerified() throws ParsingException {
// impossible to get this information from the mobile layout
return false;
}
@Override @Override
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
try { try {

View File

@ -50,6 +50,11 @@ public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
return entryElement.select("author > uri").first().text(); return entryElement.select("author > uri").first().text();
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Nullable @Nullable
@Override @Override
public String getTextualUploadDate() { public String getTextualUploadDate() {

View File

@ -2,7 +2,6 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
@ -16,18 +15,13 @@ import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.extractCookieValue;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonResponse;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getResponse;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.toJsonArray;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/** /**
@ -82,8 +76,8 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
try { try {
//fallback to thumbnail of current video. Always the case for channel mix //fallback to thumbnail of current video. Always the case for channel mix
return getThumbnailUrlFromVideoId( return getThumbnailUrlFromVideoId(
initialData.getObject("currentVideoEndpoint").getObject("watchEndpoint") initialData.getObject("currentVideoEndpoint").getObject("watchEndpoint")
.getString("videoId")); .getString("videoId"));
} catch (final Exception ignored) { } catch (final Exception ignored) {
} }
throw new ParsingException("Could not get playlist thumbnail", e); throw new ParsingException("Could not get playlist thumbnail", e);
@ -113,6 +107,11 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
return ""; return "";
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Override @Override
public long getStreamCount() { public long getStreamCount() {
// Auto-generated playlist always start with 25 videos and are endless // Auto-generated playlist always start with 25 videos and are endless
@ -144,7 +143,7 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
public InfoItemsPage<StreamInfoItem> getPage(final Page page) public InfoItemsPage<StreamInfoItem> getPage(final Page page)
throws ExtractionException, IOException { throws ExtractionException, IOException {
if (page == null || isNullOrEmpty(page.getUrl())) { if (page == null || isNullOrEmpty(page.getUrl())) {
throw new IllegalArgumentException("Page url is empty or null"); throw new IllegalArgumentException("Page url is empty or null");
} }
if (!page.getCookies().containsKey(COOKIE_NAME)) { if (!page.getCookies().containsKey(COOKIE_NAME)) {
throw new IllegalArgumentException("Cooke '" + COOKIE_NAME + "' is missing"); throw new IllegalArgumentException("Cooke '" + COOKIE_NAME + "' is missing");
@ -180,7 +179,7 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
for (final Object stream : streams) { for (final Object stream : streams) {
if (stream instanceof JsonObject) { if (stream instanceof JsonObject) {
final JsonObject streamInfo = ((JsonObject) stream) final JsonObject streamInfo = ((JsonObject) stream)
.getObject("playlistPanelVideoRenderer"); .getObject("playlistPanelVideoRenderer");
if (streamInfo != null) { if (streamInfo != null) {
collector.commit(new YoutubeStreamInfoItemExtractor(streamInfo, timeAgoParser)); collector.commit(new YoutubeStreamInfoItemExtractor(streamInfo, timeAgoParser));
} }

View File

@ -2,7 +2,6 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
@ -20,15 +19,11 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.fixThumbnailUrl; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonResponse;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -138,6 +133,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
} }
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return false;
}
@Override @Override
public long getStreamCount() throws ParsingException { public long getStreamCount() throws ParsingException {
try { try {
@ -209,11 +209,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
final JsonArray ajaxJson = getJsonResponse(page.getUrl(), getExtractorLocalization()); final JsonArray ajaxJson = getJsonResponse(page.getUrl(), getExtractorLocalization());
final JsonArray continuation = ajaxJson.getObject(1) final JsonArray continuation = ajaxJson.getObject(1)
.getObject("response") .getObject("response")
.getArray("onResponseReceivedActions") .getArray("onResponseReceivedActions")
.getObject(0) .getObject(0)
.getObject("appendContinuationItemsAction") .getObject("appendContinuationItemsAction")
.getArray("continuationItems"); .getArray("continuationItems");
collectStreamsFrom(collector, continuation); collectStreamsFrom(collector, continuation);
@ -228,10 +228,10 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
final JsonObject lastElement = contents.getObject(contents.size() - 1); final JsonObject lastElement = contents.getObject(contents.size() - 1);
if (lastElement.has("continuationItemRenderer")) { if (lastElement.has("continuationItemRenderer")) {
final String continuation = lastElement final String continuation = lastElement
.getObject("continuationItemRenderer") .getObject("continuationItemRenderer")
.getObject("continuationEndpoint") .getObject("continuationEndpoint")
.getObject("continuationCommand") .getObject("continuationCommand")
.getString("token"); .getString("token");
return new Page("https://www.youtube.com/browse_ajax?continuation=" + continuation); return new Page("https://www.youtube.com/browse_ajax?continuation=" + continuation);
} else { } else {
return null; return null;
@ -311,6 +311,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
return YoutubePlaylistExtractor.this.getUploaderUrl(); return YoutubePlaylistExtractor.this.getUploaderUrl();
} }
@Override
public boolean isUploaderVerified() {
return false;
}
@Nullable @Nullable
@Override @Override
public String getTextualUploadDate() { public String getTextualUploadDate() {

View File

@ -412,6 +412,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
return uploaderName; return uploaderName;
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
final JsonArray badges = getVideoSecondaryInfoRenderer().getObject("owner")
.getObject("videoOwnerRenderer").getArray("badges");
return YoutubeParsingHelper.isVerified(badges);
}
@Nonnull @Nonnull
@Override @Override
public String getUploaderAvatarUrl() throws ParsingException { public String getUploaderAvatarUrl() throws ParsingException {

View File

@ -158,6 +158,11 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
return url; return url;
} }
@Override
public boolean isUploaderVerified() throws ParsingException {
return YoutubeParsingHelper.isVerified(videoInfo.getArray("ownerBadges"));
}
@Nullable @Nullable
@Override @Override
public String getTextualUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {

View File

@ -31,14 +31,13 @@ import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
* Scrapes information from a video/audio streaming service (eg, YouTube). * Scrapes information from a video/audio streaming service (eg, YouTube).
*/ */
@ -171,6 +170,15 @@ public abstract class StreamExtractor extends Extractor {
@Nonnull @Nonnull
public abstract String getUploaderName() throws ParsingException; public abstract String getUploaderName() throws ParsingException;
/**
* Whether the uploader has been verified by the service's provider.
* If there is no verification implemented, return <code>false</code>.
*
* @return whether the uploader has been verified by the service's provider
* @throws ParsingException
*/
public abstract boolean isUploaderVerified() throws ParsingException;
/** /**
* The url to the image file/profile picture/avatar of the creator/uploader of the stream. * 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. * If the url is not available you can return an empty String.
@ -479,8 +487,8 @@ public abstract class StreamExtractor extends Extractor {
public abstract String getSupportInfo() throws ParsingException; public abstract String getSupportInfo() throws ParsingException;
/** /**
* The list of stream segments by timestamps for the stream. * The list of stream segments by timestamps for the stream.
* If the segment list is not available you can simply return an empty list. * If the segment list is not available you can simply return an empty list.
* *
* @return The list of segments of the stream or an empty list. * @return The list of segments of the stream or an empty list.
* @throws ParsingException * @throws ParsingException
@ -495,7 +503,8 @@ public abstract class StreamExtractor extends Extractor {
* or further information on the topic (e.g. hints that the video might contain conspiracy theories * or further information on the topic (e.g. hints that the video might contain conspiracy theories
* or contains information about a current health situation like the Covid-19 pandemic). * or contains information about a current health situation like the Covid-19 pandemic).
* </p> * </p>
* The meta information often contains links to external sources like Wikipedia or the WHO. * The meta information often contains links to external sources like Wikipedia or the WHO.
*
* @return The meta info of the stream or an empty List if not provided. * @return The meta info of the stream or an empty List if not provided.
* @throws ParsingException * @throws ParsingException
*/ */

View File

@ -33,11 +33,13 @@ public class StreamInfoItem extends InfoItem {
private String uploaderName; private String uploaderName;
private String textualUploadDate; private String textualUploadDate;
@Nullable private DateWrapper uploadDate; @Nullable
private DateWrapper uploadDate;
private long viewCount = -1; private long viewCount = -1;
private long duration = -1; private long duration = -1;
private String uploaderUrl = null; private String uploaderUrl = null;
private boolean uploaderVerified = false;
public StreamInfoItem(int serviceId, String url, String name, StreamType streamType) { public StreamInfoItem(int serviceId, String url, String name, StreamType streamType) {
super(InfoType.STREAM, serviceId, url, name); super(InfoType.STREAM, serviceId, url, name);
@ -98,6 +100,14 @@ public class StreamInfoItem extends InfoItem {
this.uploadDate = uploadDate; this.uploadDate = uploadDate;
} }
public boolean isUploaderVerified() {
return uploaderVerified;
}
public void setUploaderVerified(boolean uploaderVerified) {
this.uploaderVerified = uploaderVerified;
}
@Override @Override
public String toString() { public String toString() {
return "StreamInfoItem{" + return "StreamInfoItem{" +
@ -112,6 +122,7 @@ public class StreamInfoItem extends InfoItem {
", url='" + getUrl() + '\'' + ", url='" + getUrl() + '\'' +
", name='" + getName() + '\'' + ", name='" + getName() + '\'' +
", thumbnailUrl='" + getThumbnailUrl() + '\'' + ", thumbnailUrl='" + getThumbnailUrl() + '\'' +
", uploaderVerified='" + isUploaderVerified() + '\'' +
'}'; '}';
} }
} }

View File

@ -71,6 +71,15 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
String getUploaderUrl() throws ParsingException; String getUploaderUrl() throws ParsingException;
/**
* Whether the uploader has been verified by the service's provider.
* If there is no verification implemented, return <code>false</code>.
*
* @return whether the uploader has been verified by the service's provider
* @throws ParsingException
*/
boolean isUploaderVerified() throws ParsingException;
/** /**
* The original textual date provided by the service. Should be used as a fallback if * The original textual date provided by the service. Should be used as a fallback if
* {@link #getUploadDate()} isn't provided by the service, or it fails for some reason. * {@link #getUploadDate()} isn't provided by the service, or it fails for some reason.

View File

@ -90,6 +90,12 @@ public class StreamInfoItemsCollector extends InfoItemsCollector<StreamInfoItem,
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try {
resultItem.setUploaderVerified(extractor.isUploaderVerified());
} catch (Exception e) {
addError(e);
}
return resultItem; return resultItem;
} }

View File

@ -7,4 +7,5 @@ public interface BaseChannelExtractorTest extends BaseListExtractorTest {
void testBannerUrl() throws Exception; void testBannerUrl() throws Exception;
void testFeedUrl() throws Exception; void testFeedUrl() throws Exception;
void testSubscriberCount() throws Exception; void testSubscriberCount() throws Exception;
void testVerified() throws Exception;
} }

View File

@ -7,4 +7,5 @@ public interface BasePlaylistExtractorTest extends BaseListExtractorTest {
void testUploaderName() throws Exception; void testUploaderName() throws Exception;
void testUploaderAvatarUrl() throws Exception; void testUploaderAvatarUrl() throws Exception;
void testStreamCount() throws Exception; void testStreamCount() throws Exception;
void testUploaderVerified() throws Exception;
} }

View File

@ -45,6 +45,7 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
public abstract StreamType expectedStreamType(); public abstract StreamType expectedStreamType();
public abstract String expectedUploaderName(); public abstract String expectedUploaderName();
public abstract String expectedUploaderUrl(); public abstract String expectedUploaderUrl();
public boolean expectedUploaderVerified() { return false; }
public String expectedSubChannelName() { return ""; } // default: there is no subchannel public String expectedSubChannelName() { return ""; } // default: there is no subchannel
public String expectedSubChannelUrl() { return ""; } // default: there is no subchannel public String expectedSubChannelUrl() { return ""; } // default: there is no subchannel
public abstract List<String> expectedDescriptionContains(); // e.g. for full links public abstract List<String> expectedDescriptionContains(); // e.g. for full links
@ -99,6 +100,11 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
assertIsSecureUrl(extractor().getUploaderAvatarUrl()); assertIsSecureUrl(extractor().getUploaderAvatarUrl());
} }
@Test
public void testUploaderVerified() throws Exception {
assertEquals(expectedUploaderVerified(), extractor().isUploaderVerified());
}
@Test @Test
@Override @Override
public void testSubChannelName() throws Exception { public void testSubChannelName() throws Exception {

View File

@ -104,6 +104,11 @@ public class PeertubeAccountExtractorTest {
public void testSubscriberCount() throws ParsingException { public void testSubscriberCount() throws ParsingException {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 500); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 500);
} }
@Override
public void testVerified() throws Exception {
assertFalse(extractor.isVerified());
}
} }
public static class FreeSoftwareFoundation implements BaseChannelExtractorTest { public static class FreeSoftwareFoundation implements BaseChannelExtractorTest {
@ -200,5 +205,10 @@ public class PeertubeAccountExtractorTest {
public void testSubscriberCount() throws ParsingException { public void testSubscriberCount() throws ParsingException {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 100); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 100);
} }
@Override
public void testVerified() throws Exception {
assertFalse(extractor.isVerified());
}
} }
} }

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.extractor.services.peertube; package org.schabi.newpipe.extractor.services.peertube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.downloader.DownloaderTestImpl; import org.schabi.newpipe.downloader.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -119,6 +118,11 @@ public class PeertubeChannelExtractorTest {
public void testSubscriberCount() throws ParsingException { public void testSubscriberCount() throws ParsingException {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 230); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 230);
} }
@Override
public void testVerified() throws Exception {
assertFalse(extractor.isVerified());
}
} }
public static class ChatSceptique implements BaseChannelExtractorTest { public static class ChatSceptique implements BaseChannelExtractorTest {
@ -231,5 +235,10 @@ public class PeertubeChannelExtractorTest {
public void testSubscriberCount() throws ParsingException { public void testSubscriberCount() throws ParsingException {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 700); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 700);
} }
@Override
public void testVerified() throws Exception {
assertFalse(extractor.isVerified());
}
} }
} }

View File

@ -101,6 +101,11 @@ public class SoundcloudChannelExtractorTest {
public void testSubscriberCount() { public void testSubscriberCount() {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 1e6); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 1e6);
} }
@Override
public void testVerified() throws Exception {
assertTrue(extractor.isVerified());
}
} }
public static class DubMatix implements BaseChannelExtractorTest { public static class DubMatix implements BaseChannelExtractorTest {
@ -195,5 +200,10 @@ public class SoundcloudChannelExtractorTest {
public void testSubscriberCount() { public void testSubscriberCount() {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 2e6); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 2e6);
} }
@Override
public void testVerified() throws Exception {
assertTrue(extractor.isVerified());
}
} }
} }

View File

@ -111,6 +111,11 @@ public class SoundcloudPlaylistExtractorTest {
public void testStreamCount() { public void testStreamCount() {
assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 10); assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 10);
} }
@Override
public void testUploaderVerified() throws Exception {
assertTrue(extractor.isUploaderVerified());
}
} }
public static class RandomHouseMusic implements BasePlaylistExtractorTest { public static class RandomHouseMusic implements BasePlaylistExtractorTest {
@ -203,6 +208,11 @@ public class SoundcloudPlaylistExtractorTest {
public void testStreamCount() { public void testStreamCount() {
assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 10); assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 10);
} }
@Override
public void testUploaderVerified() throws Exception {
assertFalse(extractor.isUploaderVerified());
}
} }
public static class EDMxxx implements BasePlaylistExtractorTest { public static class EDMxxx implements BasePlaylistExtractorTest {
@ -310,6 +320,11 @@ public class SoundcloudPlaylistExtractorTest {
public void testStreamCount() { public void testStreamCount() {
assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 370); assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 370);
} }
@Override
public void testUploaderVerified() throws Exception {
assertFalse(extractor.isUploaderVerified());
}
} }
public static class SmallPlaylist implements BasePlaylistExtractorTest { public static class SmallPlaylist implements BasePlaylistExtractorTest {
@ -409,5 +424,10 @@ public class SoundcloudPlaylistExtractorTest {
public void testStreamCount() { public void testStreamCount() {
assertEquals(2, extractor.getStreamCount()); assertEquals(2, extractor.getStreamCount());
} }
@Override
public void testUploaderVerified() throws Exception {
assertFalse(extractor.isUploaderVerified());
}
} }
} }

View File

@ -8,14 +8,19 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest; import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.List;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.services.DefaultTests.assertNoDuplicatedItems; import static org.schabi.newpipe.extractor.services.DefaultTests.assertNoDuplicatedItems;
import static org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudSearchQueryHandlerFactory.*; import static org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudSearchQueryHandlerFactory.*;
@ -139,4 +144,40 @@ public class SoundcloudSearchExtractorTest {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public static class UserVerified extends DefaultSearchExtractorTest {
private static SearchExtractor extractor;
private static final String QUERY = "David Guetta";
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = SoundCloud.getSearchExtractor(QUERY, singletonList(USERS), "");
extractor.fetchPage();
}
@Override public SearchExtractor extractor() { return extractor; }
@Override public StreamingService expectedService() { return SoundCloud; }
@Override public String expectedName() { return QUERY; }
@Override public String expectedId() { return QUERY; }
@Override public String expectedUrlContains() { return "soundcloud.com/search/users?q=" + urlEncode(QUERY); }
@Override public String expectedOriginalUrlContains() { return "soundcloud.com/search/users?q=" + urlEncode(QUERY); }
@Override public String expectedSearchString() { return QUERY; }
@Nullable @Override public String expectedSearchSuggestion() { return null; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.CHANNEL; }
@Test
public void testIsVerified() throws IOException, ExtractionException {
final List<InfoItem> items = extractor.getInitialPage().getItems();
boolean verified = false;
for (InfoItem item : items) {
if (item.getUrl().equals("https://soundcloud.com/davidguetta")) {
verified = ((ChannelInfoItem) item).isVerified();
break;
}
}
assertTrue(verified);
}
}
} }

View File

@ -16,14 +16,10 @@ import java.io.IOException;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.ServiceList.YouTube;
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestGetPageInNewExtractor; import static org.schabi.newpipe.extractor.services.DefaultTests.*;
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestMoreItems;
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRelatedItems;
/** /**
* Test for {@link ChannelExtractor} * Test for {@link ChannelExtractor}
@ -157,6 +153,12 @@ public class YoutubeChannelExtractorTest {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 0); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 0);
assertTrue("Subscriber count too small", extractor.getSubscriberCount() >= 4e6); assertTrue("Subscriber count too small", extractor.getSubscriberCount() >= 4e6);
} }
@Override
public void testVerified() throws Exception {
assertTrue(extractor.isVerified());
}
} }
// Youtube RED/Premium ad blocking test // Youtube RED/Premium ad blocking test
@ -250,6 +252,11 @@ public class YoutubeChannelExtractorTest {
assertTrue("Subscriber count too small", extractor.getSubscriberCount() >= 10e6); assertTrue("Subscriber count too small", extractor.getSubscriberCount() >= 10e6);
} }
@Override
public void testVerified() throws Exception {
assertTrue(extractor.isVerified());
}
} }
public static class Kurzgesagt implements BaseChannelExtractorTest { public static class Kurzgesagt implements BaseChannelExtractorTest {
@ -343,6 +350,11 @@ public class YoutubeChannelExtractorTest {
public void testSubscriberCount() throws Exception { public void testSubscriberCount() throws Exception {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 5e6); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 5e6);
} }
@Override
public void testVerified() throws Exception {
assertTrue(extractor.isVerified());
}
} }
public static class KurzgesagtAdditional { public static class KurzgesagtAdditional {
@ -453,6 +465,11 @@ public class YoutubeChannelExtractorTest {
public void testSubscriberCount() throws Exception { public void testSubscriberCount() throws Exception {
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 5e5); assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 5e5);
} }
@Override
public void testVerified() throws Exception {
assertTrue(extractor.isVerified());
}
} }
public static class RandomChannel implements BaseChannelExtractorTest { public static class RandomChannel implements BaseChannelExtractorTest {
@ -550,6 +567,11 @@ public class YoutubeChannelExtractorTest {
long subscribers = extractor.getSubscriberCount(); long subscribers = extractor.getSubscriberCount();
assertTrue("Wrong subscriber count: " + subscribers, subscribers >= 50); assertTrue("Wrong subscriber count: " + subscribers, subscribers >= 50);
} }
@Override
public void testVerified() throws Exception {
assertFalse(extractor.isVerified());
}
} }
} }

View File

@ -13,11 +13,7 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest; import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.ContinuationsTests; import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.*;
import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.HugePlaylist;
import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.LearningPlaylist;
import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.NotAvailable;
import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.TimelessPopHits;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
@ -39,7 +35,7 @@ import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRela
*/ */
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({NotAvailable.class, TimelessPopHits.class, HugePlaylist.class, @SuiteClasses({NotAvailable.class, TimelessPopHits.class, HugePlaylist.class,
LearningPlaylist.class, ContinuationsTests.class}) LearningPlaylist.class, ContinuationsTests.class})
public class YoutubePlaylistExtractorTest { public class YoutubePlaylistExtractorTest {
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/playlist/"; private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/playlist/";
@ -163,6 +159,11 @@ public class YoutubePlaylistExtractorTest {
public void testStreamCount() throws Exception { public void testStreamCount() throws Exception {
assertTrue("Error in the streams count", extractor.getStreamCount() > 100); assertTrue("Error in the streams count", extractor.getStreamCount() > 100);
} }
@Override
public void testUploaderVerified() throws Exception {
assertFalse(extractor.isUploaderVerified());
}
} }
public static class HugePlaylist implements BasePlaylistExtractorTest { public static class HugePlaylist implements BasePlaylistExtractorTest {
@ -276,6 +277,11 @@ public class YoutubePlaylistExtractorTest {
public void testStreamCount() throws Exception { public void testStreamCount() throws Exception {
assertTrue("Error in the streams count", extractor.getStreamCount() > 100); assertTrue("Error in the streams count", extractor.getStreamCount() > 100);
} }
@Override
public void testUploaderVerified() throws Exception {
assertTrue(extractor.isUploaderVerified());
}
} }
public static class LearningPlaylist implements BasePlaylistExtractorTest { public static class LearningPlaylist implements BasePlaylistExtractorTest {
@ -375,6 +381,11 @@ public class YoutubePlaylistExtractorTest {
public void testStreamCount() throws Exception { public void testStreamCount() throws Exception {
assertTrue("Error in the streams count", extractor.getStreamCount() > 40); assertTrue("Error in the streams count", extractor.getStreamCount() > 40);
} }
@Override
public void testUploaderVerified() throws Exception {
assertTrue(extractor.isUploaderVerified());
}
} }
public static class ContinuationsTests { public static class ContinuationsTests {
@ -388,8 +399,8 @@ public class YoutubePlaylistExtractorTest {
@Test @Test
public void testNoContinuations() throws Exception { public void testNoContinuations() throws Exception {
final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube
.getPlaylistExtractor( .getPlaylistExtractor(
"https://www.youtube.com/playlist?list=PLXJg25X-OulsVsnvZ7RVtSDW-id9_RzAO"); "https://www.youtube.com/playlist?list=PLXJg25X-OulsVsnvZ7RVtSDW-id9_RzAO");
extractor.fetchPage(); extractor.fetchPage();
assertNoMoreItems(extractor); assertNoMoreItems(extractor);
@ -398,12 +409,12 @@ public class YoutubePlaylistExtractorTest {
@Test @Test
public void testOnlySingleContinuation() throws Exception { public void testOnlySingleContinuation() throws Exception {
final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube
.getPlaylistExtractor( .getPlaylistExtractor(
"https://www.youtube.com/playlist?list=PLjgwFL8urN2DFRuRkFTkmtHjyoNWHHdZX"); "https://www.youtube.com/playlist?list=PLjgwFL8urN2DFRuRkFTkmtHjyoNWHHdZX");
extractor.fetchPage(); extractor.fetchPage();
final ListExtractor.InfoItemsPage<StreamInfoItem> page = defaultTestMoreItems( final ListExtractor.InfoItemsPage<StreamInfoItem> page = defaultTestMoreItems(
extractor); extractor);
assertFalse("More items available when it shouldn't", page.hasNextPage()); assertFalse("More items available when it shouldn't", page.hasNextPage());
} }
} }

View File

@ -13,14 +13,19 @@ import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.downloader.DownloaderTestImpl;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import javax.annotation.Nullable;
import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -31,7 +36,6 @@ import static org.schabi.newpipe.extractor.services.DefaultTests.assertNoDuplica
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.CHANNELS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.CHANNELS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.PLAYLISTS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.PLAYLISTS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.VIDEOS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.VIDEOS;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
public class YoutubeSearchExtractorTest { public class YoutubeSearchExtractorTest {
@ -269,4 +273,41 @@ public class YoutubeSearchExtractorTest {
@Override public String expectedOriginalUrlContains() throws Exception { return "youtube.com/results?search_query=" + QUERY; } @Override public String expectedOriginalUrlContains() throws Exception { return "youtube.com/results?search_query=" + QUERY; }
} }
public static class ChannelVerified extends DefaultSearchExtractorTest {
private static SearchExtractor extractor;
private static final String QUERY = "bbc";
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = YouTube.getSearchExtractor(QUERY, singletonList(CHANNELS), "");
extractor.fetchPage();
}
@Override public SearchExtractor extractor() { return extractor; }
@Override public StreamingService expectedService() { return YouTube; }
@Override public String expectedName() { return QUERY; }
@Override public String expectedId() { return QUERY; }
@Override public String expectedUrlContains() { return "youtube.com/results?search_query=" + QUERY; }
@Override public String expectedOriginalUrlContains() { return "youtube.com/results?search_query=" + QUERY; }
@Override public String expectedSearchString() { return QUERY; }
@Nullable @Override public String expectedSearchSuggestion() { return null; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.CHANNEL; }
@Test
public void testAtLeastOneVerified() throws IOException, ExtractionException {
final List<InfoItem> items = extractor.getInitialPage().getItems();
boolean verified = false;
for (InfoItem item : items) {
if (((ChannelInfoItem) item).isVerified()) {
verified = true;
break;
}
}
assertTrue(verified);
}
}
} }

View File

@ -41,6 +41,7 @@ public class YoutubeStreamExtractorAgeRestrictedTest extends DefaultStreamExtrac
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; } @Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
@Override public String expectedUploaderName() { return "EpicFiveTV"; } @Override public String expectedUploaderName() { return "EpicFiveTV"; }
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCuPUHlLP5POZphOIrjrNxiw"; } @Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCuPUHlLP5POZphOIrjrNxiw"; }
@Override public boolean expectedUploaderVerified() { return true; }
@Override public List<String> expectedDescriptionContains() { return Arrays.asList("http://instagram.com/Ruben_Sole", "AVN"); } @Override public List<String> expectedDescriptionContains() { return Arrays.asList("http://instagram.com/Ruben_Sole", "AVN"); }
@Override public long expectedLength() { return 1790; } @Override public long expectedLength() { return 1790; }
@Override public long expectedTimestamp() { return TIMESTAMP; } @Override public long expectedTimestamp() { return TIMESTAMP; }

View File

@ -103,6 +103,7 @@ public class YoutubeStreamExtractorDefaultTest {
return Arrays.asList("https://www.youtube.com/channel/UC7l23W7gFi4Uho6WSzckZRA", return Arrays.asList("https://www.youtube.com/channel/UC7l23W7gFi4Uho6WSzckZRA",
"https://www.handcraftpictures.com/"); "https://www.handcraftpictures.com/");
} }
@Override public boolean expectedUploaderVerified() { return true; }
@Override public long expectedLength() { return 381; } @Override public long expectedLength() { return 381; }
@Override public long expectedTimestamp() { return TIMESTAMP; } @Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 26682500; } @Override public long expectedViewCountAtLeast() { return 26682500; }
@ -150,6 +151,7 @@ public class YoutubeStreamExtractorDefaultTest {
@Nullable @Override public String expectedTextualUploadDate() { return "2018-06-19"; } @Nullable @Override public String expectedTextualUploadDate() { return "2018-06-19"; }
@Override public long expectedLikeCountAtLeast() { return 340100; } @Override public long expectedLikeCountAtLeast() { return 340100; }
@Override public long expectedDislikeCountAtLeast() { return 18700; } @Override public long expectedDislikeCountAtLeast() { return 18700; }
@Override public boolean expectedUploaderVerified() { return true; }
// @formatter:on // @formatter:on
@Override @Override
@Test @Test
@ -271,6 +273,7 @@ public class YoutubeStreamExtractorDefaultTest {
@Override public String expectedUploaderName() { return "maiLab"; } @Override public String expectedUploaderName() { return "maiLab"; }
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCyHDQ5C6z1NDmJ4g6SerW8g"; } @Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCyHDQ5C6z1NDmJ4g6SerW8g"; }
@Override public List<String> expectedDescriptionContains() {return Arrays.asList("Vitamin", "2:44", "Was ist Vitamin D?");} @Override public List<String> expectedDescriptionContains() {return Arrays.asList("Vitamin", "2:44", "Was ist Vitamin D?");}
@Override public boolean expectedUploaderVerified() { return true; }
@Override public long expectedLength() { return 1010; } @Override public long expectedLength() { return 1010; }
@Override public long expectedViewCountAtLeast() { return 815500; } @Override public long expectedViewCountAtLeast() { return 815500; }
@Nullable @Override public String expectedUploadDate() { return "2020-11-18 00:00:00.000"; } @Nullable @Override public String expectedUploadDate() { return "2020-11-18 00:00:00.000"; }
@ -339,6 +342,7 @@ public class YoutubeStreamExtractorDefaultTest {
Collections.singletonList("Wikipedia (German)") Collections.singletonList("Wikipedia (German)")
)); ));
} }
@Override public boolean expectedUploaderVerified() { return true; }
// @formatter:on // @formatter:on
@Override @Override
@Ignore("TODO fix") @Ignore("TODO fix")

View File

@ -45,6 +45,7 @@ public class YoutubeStreamExtractorLivestreamTest extends DefaultStreamExtractor
return Arrays.asList("https://bit.ly/chilledcow-playlists", return Arrays.asList("https://bit.ly/chilledcow-playlists",
"https://bit.ly/chilledcow-submissions"); "https://bit.ly/chilledcow-submissions");
} }
@Override public boolean expectedUploaderVerified() { return true; }
@Override public long expectedLength() { return 0; } @Override public long expectedLength() { return 0; }
@Override public long expectedTimestamp() { return TIMESTAMP; } @Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 0; } @Override public long expectedViewCountAtLeast() { return 0; }