Merge remote-tracking branch 'origin/dev' into bandcamp
This commit is contained in:
commit
91e9309486
66 changed files with 926 additions and 143 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -32,7 +32,9 @@ jobs:
|
|||
- name: Build and run Tests
|
||||
run: |
|
||||
if [[ $GITHUB_EVENT_NAME == 'schedule' ]]; then
|
||||
echo running with real downloader
|
||||
./gradlew check --stacktrace -Ddownloader=REAL
|
||||
else
|
||||
echo running with mock downloader
|
||||
./gradlew check --stacktrace -Ddownloader=MOCK
|
||||
fi
|
||||
|
|
|
@ -40,4 +40,6 @@ public abstract class ChannelExtractor extends ListExtractor<StreamInfoItem> {
|
|||
public abstract String getParentChannelName() throws ParsingException;
|
||||
public abstract String getParentChannelUrl() throws ParsingException;
|
||||
public abstract String getParentChannelAvatarUrl() throws ParsingException;
|
||||
public abstract boolean isVerified() throws ParsingException;
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ChannelInfoItem extends InfoItem {
|
|||
private String description;
|
||||
private long subscriberCount = -1;
|
||||
private long streamCount = -1;
|
||||
|
||||
private boolean verified = false;
|
||||
|
||||
public ChannelInfoItem(int serviceId, String url, String name) {
|
||||
super(InfoType.CHANNEL, serviceId, url, name);
|
||||
|
@ -56,4 +56,12 @@ public class ChannelInfoItem extends InfoItem {
|
|||
public void setStreamCount(long stream_count) {
|
||||
this.streamCount = stream_count;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
public void setVerified(boolean verified) {
|
||||
this.verified = verified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,5 +27,8 @@ public interface ChannelInfoItemExtractor extends InfoItemExtractor {
|
|||
String getDescription() throws ParsingException;
|
||||
|
||||
long getSubscriberCount() throws ParsingException;
|
||||
|
||||
long getStreamCount() throws ParsingException;
|
||||
|
||||
boolean isVerified() throws ParsingException;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,12 @@ public class ChannelInfoItemsCollector extends InfoItemsCollector<ChannelInfoIte
|
|||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.setVerified(extractor.isVerified());
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
|
||||
return resultItem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public class CommentsInfoItem extends InfoItem {
|
|||
private String uploaderName;
|
||||
private String uploaderAvatarUrl;
|
||||
private String uploaderUrl;
|
||||
private boolean uploaderVerified;
|
||||
private String textualUploadDate;
|
||||
@Nullable
|
||||
private DateWrapper uploadDate;
|
||||
|
@ -103,4 +104,12 @@ public class CommentsInfoItem extends InfoItem {
|
|||
public void setPinned(boolean pinned) {
|
||||
this.pinned = pinned;
|
||||
}
|
||||
|
||||
public void setUploaderVerified(boolean uploaderVerified) {
|
||||
this.uploaderVerified = uploaderVerified;
|
||||
}
|
||||
|
||||
public boolean isUploaderVerified() {
|
||||
return uploaderVerified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,4 +53,9 @@ public interface CommentsInfoItemExtractor extends InfoItemExtractor {
|
|||
* Whether the comment is pinned
|
||||
*/
|
||||
boolean isPinned() throws ParsingException;
|
||||
|
||||
/**
|
||||
* Whether the uploader is verified by the service
|
||||
*/
|
||||
boolean isUploaderVerified() throws ParsingException;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> {
|
|||
public abstract String getUploaderUrl() throws ParsingException;
|
||||
public abstract String getUploaderName() throws ParsingException;
|
||||
public abstract String getUploaderAvatarUrl() throws ParsingException;
|
||||
public abstract boolean isUploaderVerified() throws ParsingException;
|
||||
|
||||
public abstract long getStreamCount() throws ParsingException;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import com.grack.nanojson.JsonArray;
|
|||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
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.StreamInfoItemsCollector;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MediaCCCConferenceExtractor extends ChannelExtractor {
|
||||
private JsonObject conferenceData;
|
||||
|
@ -69,6 +67,11 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
|
|||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() {
|
||||
|
|
|
@ -126,6 +126,11 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
|
|||
return conference.getString("conference");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
|
@ -180,7 +185,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
|
|||
for (int s = 0; s < room.getArray("streams").size(); s++) {
|
||||
final JsonObject stream = room.getArray("streams").getObject(s);
|
||||
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);
|
||||
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")) {
|
||||
final String resolution = stream.getArray("videoSize").getInt(0) + "x"
|
||||
+ 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")) {
|
||||
final JsonObject url = stream.getObject("urls").getObject(type);
|
||||
videoStreams.add(new VideoStream(
|
||||
|
@ -218,7 +223,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
|
|||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<SubtitlesStream> getSubtitlesDefault(){
|
||||
public List<SubtitlesStream> getSubtitlesDefault() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,11 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
|
|||
return "https://media.ccc.de/c/" + conferenceInfo.getString("slug");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() throws ParsingException {
|
||||
|
|
|
@ -68,6 +68,11 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
|
|||
.getUrl(); // web URL
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() throws ParsingException {
|
||||
|
|
|
@ -142,6 +142,11 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
|
|||
return item.getStreamCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return item.getName();
|
||||
|
|
|
@ -4,7 +4,6 @@ import com.grack.nanojson.JsonArray;
|
|||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
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.linkhandler.LinkHandler;
|
||||
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.MediaCCCStreamLinkHandlerFactory;
|
||||
|
||||
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 org.schabi.newpipe.extractor.stream.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class MediaCCCStreamExtractor extends StreamExtractor {
|
||||
private JsonObject data;
|
||||
|
@ -109,6 +96,11 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
|
|||
.replaceFirst("https://(api\\.)?media\\.ccc\\.de/public/conferences/", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
|
|
|
@ -28,6 +28,11 @@ public class MediaCCCConferenceInfoItemExtractor implements ChannelInfoItemExtra
|
|||
return ListExtractor.ITEM_COUNT_UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return conference.getString("title");
|
||||
|
|
|
@ -46,6 +46,11 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
|
|||
return event.getString("conference_url");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
|
|
|
@ -83,6 +83,11 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
|
|||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
|
||||
|
|
|
@ -90,6 +90,11 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
|
|||
return baseUrl + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
|
||||
|
|
|
@ -99,6 +99,11 @@ public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtrac
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
return JsonUtils.getString(item, "account.name") + "@" + JsonUtils.getString(item, "account.host");
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.schabi.newpipe.extractor.services.peertube.extractors;
|
|||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
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.utils.Utils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
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.services.peertube.PeertubeParsingHelper.*;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
public class PeertubePlaylistExtractor extends PlaylistExtractor {
|
||||
|
@ -59,6 +54,11 @@ public class PeertubePlaylistExtractor extends PlaylistExtractor {
|
|||
return getBaseUrl() + playlistInfo.getObject("ownerAccount").getObject("avatar").getString("path");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return playlistInfo.getLong("videosLength");
|
||||
|
|
|
@ -148,6 +148,11 @@ public class PeertubeStreamExtractor extends StreamExtractor {
|
|||
return JsonUtils.getString(json, "account.displayName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
|
|
|
@ -54,6 +54,11 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
|
|||
.fromId("accounts/" + name + "@" + host, baseUrl).getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
return JsonUtils.getString(item, "account.displayName");
|
||||
|
|
|
@ -96,6 +96,11 @@ public class SoundcloudChannelExtractor extends ChannelExtractor {
|
|||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return user.getBoolean("verified");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {
|
||||
|
|
|
@ -40,6 +40,11 @@ public class SoundcloudChannelInfoItemExtractor implements ChannelInfoItemExtrac
|
|||
return itemObject.getLong("track_count");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() {
|
||||
return itemObject.getBoolean("verified");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return itemObject.getString("description", EMPTY_STRING);
|
||||
|
|
|
@ -6,9 +6,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
|
||||
private JsonObject json;
|
||||
|
@ -49,6 +48,11 @@ public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtr
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return json.getObject("user").getBoolean("verified");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderUrl() {
|
||||
return json.getObject("user").getString("permalink_url");
|
||||
|
|
|
@ -111,6 +111,11 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
|
|||
return SoundcloudParsingHelper.getAvatarUrl(playlist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return playlist.getObject("user").getBoolean("verified");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return playlist.getLong("track_count");
|
||||
|
|
|
@ -131,6 +131,11 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
|
|||
return SoundcloudParsingHelper.getUploaderName(track);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return track.getObject("user").getBoolean("verified");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
|
|
|
@ -43,6 +43,11 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
|
|||
return replaceHttpWithHttps(itemObject.getObject("user").getString("permalink_url"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return itemObject.getObject("user").getBoolean("verified");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return itemObject.getString("created_at");
|
||||
|
@ -64,8 +69,7 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
|
|||
if (artworkUrl.isEmpty()) {
|
||||
artworkUrl = itemObject.getObject("user").getString("avatar_url");
|
||||
}
|
||||
String artworkUrlBetterResolution = artworkUrl.replace("large.jpg", "crop.jpg");
|
||||
return artworkUrlBetterResolution;
|
||||
return artworkUrl.replace("large.jpg", "crop.jpg");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,7 +8,7 @@ import static org.schabi.newpipe.extractor.services.youtube.ItagItem.ItagType.*;
|
|||
|
||||
public class ItagItem {
|
||||
/**
|
||||
* List can be found here https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L360
|
||||
* List can be found here https://github.com/ytdl-org/youtube-dl/blob/9fc5eafb8e384453a49f7cfe73147be491f0b19d/youtube_dl/extractor/youtube.py#L1071
|
||||
*/
|
||||
private static final ItagItem[] ITAG_LIST = {
|
||||
/////////////////////////////////////////////////////
|
||||
|
@ -155,4 +155,86 @@ public class ItagItem {
|
|||
public String resolutionString;
|
||||
public int fps = -1;
|
||||
|
||||
// Fields for Dash
|
||||
private int bitrate;
|
||||
private int width;
|
||||
private int height;
|
||||
private int initStart;
|
||||
private int initEnd;
|
||||
private int indexStart;
|
||||
private int indexEnd;
|
||||
private String quality;
|
||||
private String codec;
|
||||
|
||||
public int getBitrate() {
|
||||
return bitrate;
|
||||
}
|
||||
|
||||
public void setBitrate(int bitrate) {
|
||||
this.bitrate = bitrate;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getInitStart() {
|
||||
return initStart;
|
||||
}
|
||||
|
||||
public void setInitStart(int initStart) {
|
||||
this.initStart = initStart;
|
||||
}
|
||||
|
||||
public int getInitEnd() {
|
||||
return initEnd;
|
||||
}
|
||||
|
||||
public void setInitEnd(int initEnd) {
|
||||
this.initEnd = initEnd;
|
||||
}
|
||||
|
||||
public int getIndexStart() {
|
||||
return indexStart;
|
||||
}
|
||||
|
||||
public void setIndexStart(int indexStart) {
|
||||
this.indexStart = indexStart;
|
||||
}
|
||||
|
||||
public int getIndexEnd() {
|
||||
return indexEnd;
|
||||
}
|
||||
|
||||
public void setIndexEnd(int indexEnd) {
|
||||
this.indexEnd = indexEnd;
|
||||
}
|
||||
|
||||
public String getQuality() {
|
||||
return quality;
|
||||
}
|
||||
|
||||
public void setQuality(String quality) {
|
||||
this.quality = quality;
|
||||
}
|
||||
|
||||
public String getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
public void setCodec(String codec) {
|
||||
this.codec = codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,9 +358,19 @@ public class YoutubeParsingHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Only use in tests.
|
||||
* <p>
|
||||
* <b>Only use in tests.</b>
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Quick-and-dirty solution to reset global state in between test classes.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is needed for the mocks because in order to reach that state a network request has to
|
||||
* be made. If the global state is not reset and the RecordingDownloader is used,
|
||||
* then only the first test class has that request recorded. Meaning running the other
|
||||
* tests with mocks will fail, because the mock is missing.
|
||||
* </p>
|
||||
*/
|
||||
public static void resetClientVersionAndKey() {
|
||||
clientVersion = null;
|
||||
|
@ -794,4 +804,21 @@ public class YoutubeParsingHelper {
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,6 +216,14 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
|||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
final JsonArray badges = initialData.getObject("header").getObject("c4TabbedHeaderRenderer")
|
||||
.getArray("badges");
|
||||
|
||||
return YoutubeParsingHelper.isVerified(badges);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.grack.nanojson.JsonObject;
|
|||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
|
||||
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.utils.Utils;
|
||||
|
||||
|
@ -97,6 +98,11 @@ public class YoutubeChannelInfoItemExtractor implements ChannelInfoItemExtractor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return YoutubeParsingHelper.isVerified(channelInfoItem.getArray("ownerBadges"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() throws ParsingException {
|
||||
try {
|
||||
|
|
|
@ -125,6 +125,11 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
|
|||
return json.has("pinnedCommentBadge");
|
||||
}
|
||||
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
// impossible to get this information from the mobile layout
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
try {
|
||||
|
|
|
@ -50,6 +50,11 @@ public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
|
|||
return entryElement.select("author > uri").first().text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
|
|||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
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.StreamInfoItemsCollector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
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.services.youtube.YoutubeParsingHelper.*;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
/**
|
||||
|
@ -82,8 +76,8 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
|
|||
try {
|
||||
//fallback to thumbnail of current video. Always the case for channel mix
|
||||
return getThumbnailUrlFromVideoId(
|
||||
initialData.getObject("currentVideoEndpoint").getObject("watchEndpoint")
|
||||
.getString("videoId"));
|
||||
initialData.getObject("currentVideoEndpoint").getObject("watchEndpoint")
|
||||
.getString("videoId"));
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
throw new ParsingException("Could not get playlist thumbnail", e);
|
||||
|
@ -113,6 +107,11 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
|
|||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
// 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)
|
||||
throws ExtractionException, IOException {
|
||||
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)) {
|
||||
throw new IllegalArgumentException("Cooke '" + COOKIE_NAME + "' is missing");
|
||||
|
@ -180,7 +179,7 @@ public class YoutubeMixPlaylistExtractor extends PlaylistExtractor {
|
|||
for (final Object stream : streams) {
|
||||
if (stream instanceof JsonObject) {
|
||||
final JsonObject streamInfo = ((JsonObject) stream)
|
||||
.getObject("playlistPanelVideoRenderer");
|
||||
.getObject("playlistPanelVideoRenderer");
|
||||
if (streamInfo != null) {
|
||||
collector.commit(new YoutubeStreamInfoItemExtractor(streamInfo, timeAgoParser));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
|
|||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
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.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
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.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.services.youtube.YoutubeParsingHelper.*;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
|
@ -138,6 +133,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() throws ParsingException {
|
||||
try {
|
||||
|
@ -209,11 +209,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
|
|||
final JsonArray ajaxJson = getJsonResponse(page.getUrl(), getExtractorLocalization());
|
||||
|
||||
final JsonArray continuation = ajaxJson.getObject(1)
|
||||
.getObject("response")
|
||||
.getArray("onResponseReceivedActions")
|
||||
.getObject(0)
|
||||
.getObject("appendContinuationItemsAction")
|
||||
.getArray("continuationItems");
|
||||
.getObject("response")
|
||||
.getArray("onResponseReceivedActions")
|
||||
.getObject(0)
|
||||
.getObject("appendContinuationItemsAction")
|
||||
.getArray("continuationItems");
|
||||
|
||||
collectStreamsFrom(collector, continuation);
|
||||
|
||||
|
@ -228,10 +228,10 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
|
|||
final JsonObject lastElement = contents.getObject(contents.size() - 1);
|
||||
if (lastElement.has("continuationItemRenderer")) {
|
||||
final String continuation = lastElement
|
||||
.getObject("continuationItemRenderer")
|
||||
.getObject("continuationEndpoint")
|
||||
.getObject("continuationCommand")
|
||||
.getString("token");
|
||||
.getObject("continuationItemRenderer")
|
||||
.getObject("continuationEndpoint")
|
||||
.getObject("continuationCommand")
|
||||
.getString("token");
|
||||
return new Page("https://www.youtube.com/browse_ajax?continuation=" + continuation);
|
||||
} else {
|
||||
return null;
|
||||
|
@ -311,6 +311,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
|
|||
return YoutubePlaylistExtractor.this.getUploaderUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
|
|
|
@ -412,6 +412,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
return uploaderName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
final JsonArray badges = getVideoSecondaryInfoRenderer().getObject("owner")
|
||||
.getObject("videoOwnerRenderer").getArray("badges");
|
||||
|
||||
return YoutubeParsingHelper.isVerified(badges);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() throws ParsingException {
|
||||
|
@ -502,7 +510,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
for (Map.Entry<String, ItagItem> entry : getItags(ADAPTIVE_FORMATS, ItagItem.ItagType.AUDIO).entrySet()) {
|
||||
ItagItem itag = entry.getValue();
|
||||
|
||||
AudioStream audioStream = new AudioStream(entry.getKey(), itag.getMediaFormat(), itag.avgBitrate);
|
||||
AudioStream audioStream = new AudioStream(entry.getKey(), itag);
|
||||
if (!Stream.containSimilarStream(audioStream, audioStreams)) {
|
||||
audioStreams.add(audioStream);
|
||||
}
|
||||
|
@ -522,7 +530,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
for (Map.Entry<String, ItagItem> entry : getItags(FORMATS, ItagItem.ItagType.VIDEO).entrySet()) {
|
||||
ItagItem itag = entry.getValue();
|
||||
|
||||
VideoStream videoStream = new VideoStream(entry.getKey(), itag.getMediaFormat(), itag.resolutionString);
|
||||
VideoStream videoStream = new VideoStream(entry.getKey(), false, itag);
|
||||
if (!Stream.containSimilarStream(videoStream, videoStreams)) {
|
||||
videoStreams.add(videoStream);
|
||||
}
|
||||
|
@ -542,7 +550,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
for (Map.Entry<String, ItagItem> entry : getItags(ADAPTIVE_FORMATS, ItagItem.ItagType.VIDEO_ONLY).entrySet()) {
|
||||
ItagItem itag = entry.getValue();
|
||||
|
||||
VideoStream videoStream = new VideoStream(entry.getKey(), itag.getMediaFormat(), itag.resolutionString, true);
|
||||
VideoStream videoStream = new VideoStream(entry.getKey(), true, itag);
|
||||
if (!Stream.containSimilarStream(videoStream, videoOnlyStreams)) {
|
||||
videoOnlyStreams.add(videoStream);
|
||||
}
|
||||
|
@ -949,6 +957,22 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
+ deobfuscateSignature(cipher.get("s"));
|
||||
}
|
||||
|
||||
JsonObject initRange = formatData.getObject("initRange");
|
||||
JsonObject indexRange = formatData.getObject("indexRange");
|
||||
String mimeType = formatData.getString("mimeType", EMPTY_STRING);
|
||||
String codec = mimeType.contains("codecs") ? mimeType.split("\"")[1] : EMPTY_STRING;
|
||||
|
||||
itagItem.setBitrate(formatData.getInt("bitrate"));
|
||||
itagItem.setWidth(formatData.getInt("width"));
|
||||
itagItem.setHeight(formatData.getInt("height"));
|
||||
itagItem.setInitStart(Integer.parseInt(initRange.getString("start", "-1")));
|
||||
itagItem.setInitEnd(Integer.parseInt(initRange.getString("end", "-1")));
|
||||
itagItem.setIndexStart(Integer.parseInt(indexRange.getString("start", "-1")));
|
||||
itagItem.setIndexEnd(Integer.parseInt(indexRange.getString("end", "-1")));
|
||||
itagItem.fps = formatData.getInt("fps");
|
||||
itagItem.setQuality(formatData.getString("quality"));
|
||||
itagItem.setCodec(codec);
|
||||
|
||||
urlAndItags.put(streamUrl, itagItem);
|
||||
}
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
|
|
|
@ -158,6 +158,11 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
|||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return YoutubeParsingHelper.isVerified(videoInfo.getArray("ownerBadges"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() throws ParsingException {
|
||||
|
|
|
@ -11,6 +11,8 @@ import java.net.MalformedURLException;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -38,6 +40,7 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
|||
|
||||
private static final Pattern YOUTUBE_VIDEO_ID_REGEX_PATTERN = Pattern.compile("^([a-zA-Z0-9_-]{11})");
|
||||
private static final YoutubeStreamLinkHandlerFactory instance = new YoutubeStreamLinkHandlerFactory();
|
||||
private static final List<String> SUBPATHS = Arrays.asList("embed/", "shorts/", "watch/", "v/", "w/");
|
||||
|
||||
private YoutubeStreamLinkHandlerFactory() {
|
||||
}
|
||||
|
@ -124,7 +127,7 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
|||
switch (host.toUpperCase()) {
|
||||
case "WWW.YOUTUBE-NOCOOKIE.COM": {
|
||||
if (path.startsWith("embed/")) {
|
||||
String id = path.split("/")[1];
|
||||
String id = path.substring(6); // embed/
|
||||
|
||||
return assertIsId(id);
|
||||
}
|
||||
|
@ -150,11 +153,8 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
|||
return assertIsId(viewQueryValue);
|
||||
}
|
||||
|
||||
if (path.startsWith("embed/") || path.startsWith("shorts/")) {
|
||||
String id = path.split("/")[1];
|
||||
|
||||
return assertIsId(id);
|
||||
}
|
||||
String maybeId = getIdFromSubpathsInPath(path);
|
||||
if (maybeId != null) return maybeId;
|
||||
|
||||
String viewQueryValue = Utils.getQueryValue(url, "v");
|
||||
return assertIsId(viewQueryValue);
|
||||
|
@ -169,20 +169,7 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
|||
return assertIsId(path);
|
||||
}
|
||||
|
||||
case "HOOKTUBE.COM": {
|
||||
if (path.startsWith("v/")) {
|
||||
String id = path.substring("v/".length());
|
||||
|
||||
return assertIsId(id);
|
||||
}
|
||||
if (path.startsWith("watch/")) {
|
||||
String id = path.substring("watch/".length());
|
||||
|
||||
return assertIsId(id);
|
||||
}
|
||||
// there is no break-statement here on purpose so the next code-block gets also run for hooktube
|
||||
}
|
||||
|
||||
case "HOOKTUBE.COM":
|
||||
case "INVIDIO.US":
|
||||
case "DEV.INVIDIO.US":
|
||||
case "WWW.INVIDIO.US":
|
||||
|
@ -208,11 +195,8 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
|||
return assertIsId(viewQueryValue);
|
||||
}
|
||||
}
|
||||
if (path.startsWith("embed/")) {
|
||||
String id = path.substring("embed/".length());
|
||||
|
||||
return assertIsId(id);
|
||||
}
|
||||
String maybeId = getIdFromSubpathsInPath(path);
|
||||
if (maybeId != null) return maybeId;
|
||||
|
||||
String viewQueryValue = Utils.getQueryValue(url, "v");
|
||||
if (viewQueryValue != null) {
|
||||
|
@ -237,4 +221,14 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getIdFromSubpathsInPath(String path) throws ParsingException {
|
||||
for (final String subpath : SUBPATHS) {
|
||||
if (path.startsWith(subpath)) {
|
||||
String id = path.substring(subpath.length());
|
||||
return assertIsId(id);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,10 +21,21 @@ package org.schabi.newpipe.extractor.stream;
|
|||
*/
|
||||
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.services.youtube.ItagItem;
|
||||
|
||||
public class AudioStream extends Stream {
|
||||
public int average_bitrate = -1;
|
||||
|
||||
// Fields for Dash
|
||||
private int itag;
|
||||
private int bitrate;
|
||||
private int initStart;
|
||||
private int initEnd;
|
||||
private int indexStart;
|
||||
private int indexEnd;
|
||||
private String quality;
|
||||
private String codec;
|
||||
|
||||
/**
|
||||
* Create a new audio stream
|
||||
* @param url the url
|
||||
|
@ -36,6 +47,23 @@ public class AudioStream extends Stream {
|
|||
this.average_bitrate = averageBitrate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new audio stream
|
||||
* @param url the url
|
||||
* @param itag the ItagItem of the Stream
|
||||
*/
|
||||
public AudioStream(String url, ItagItem itag) {
|
||||
this(url, itag.getMediaFormat(), itag.avgBitrate);
|
||||
this.itag = itag.id;
|
||||
this.quality = itag.getQuality();
|
||||
this.bitrate = itag.getBitrate();
|
||||
this.initStart = itag.getInitStart();
|
||||
this.initEnd = itag.getInitEnd();
|
||||
this.indexStart = itag.getIndexStart();
|
||||
this.indexEnd = itag.getIndexEnd();
|
||||
this.codec = itag.getCodec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalStats(Stream cmp) {
|
||||
return super.equalStats(cmp) && cmp instanceof AudioStream &&
|
||||
|
@ -49,4 +77,36 @@ public class AudioStream extends Stream {
|
|||
public int getAverageBitrate() {
|
||||
return average_bitrate;
|
||||
}
|
||||
|
||||
public int getItag() {
|
||||
return itag;
|
||||
}
|
||||
|
||||
public int getBitrate() {
|
||||
return bitrate;
|
||||
}
|
||||
|
||||
public int getInitStart() {
|
||||
return initStart;
|
||||
}
|
||||
|
||||
public int getInitEnd() {
|
||||
return initEnd;
|
||||
}
|
||||
|
||||
public int getIndexStart() {
|
||||
return indexStart;
|
||||
}
|
||||
|
||||
public int getIndexEnd() {
|
||||
return indexEnd;
|
||||
}
|
||||
|
||||
public String getQuality() {
|
||||
return quality;
|
||||
}
|
||||
|
||||
public String getCodec() {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,13 @@ import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
|||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Scrapes information from a video/audio streaming service (eg, YouTube).
|
||||
*/
|
||||
|
@ -171,6 +170,15 @@ public abstract class StreamExtractor extends Extractor {
|
|||
@Nonnull
|
||||
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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The list of stream segments by timestamps for the stream.
|
||||
* If the segment list is not available you can simply return an empty list.
|
||||
* The list of stream segments by timestamps for the stream.
|
||||
* 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.
|
||||
* @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 contains information about a current health situation like the Covid-19 pandemic).
|
||||
* </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.
|
||||
* @throws ParsingException
|
||||
*/
|
||||
|
|
|
@ -33,11 +33,13 @@ public class StreamInfoItem extends InfoItem {
|
|||
|
||||
private String uploaderName;
|
||||
private String textualUploadDate;
|
||||
@Nullable private DateWrapper uploadDate;
|
||||
@Nullable
|
||||
private DateWrapper uploadDate;
|
||||
private long viewCount = -1;
|
||||
private long duration = -1;
|
||||
|
||||
private String uploaderUrl = null;
|
||||
private boolean uploaderVerified = false;
|
||||
|
||||
public StreamInfoItem(int serviceId, String url, String name, StreamType streamType) {
|
||||
super(InfoType.STREAM, serviceId, url, name);
|
||||
|
@ -98,6 +100,14 @@ public class StreamInfoItem extends InfoItem {
|
|||
this.uploadDate = uploadDate;
|
||||
}
|
||||
|
||||
public boolean isUploaderVerified() {
|
||||
return uploaderVerified;
|
||||
}
|
||||
|
||||
public void setUploaderVerified(boolean uploaderVerified) {
|
||||
this.uploaderVerified = uploaderVerified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StreamInfoItem{" +
|
||||
|
@ -112,6 +122,7 @@ public class StreamInfoItem extends InfoItem {
|
|||
", url='" + getUrl() + '\'' +
|
||||
", name='" + getName() + '\'' +
|
||||
", thumbnailUrl='" + getThumbnailUrl() + '\'' +
|
||||
", uploaderVerified='" + isUploaderVerified() + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -71,6 +71,15 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
|
|||
|
||||
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
|
||||
* {@link #getUploadDate()} isn't provided by the service, or it fails for some reason.
|
||||
|
|
|
@ -90,6 +90,12 @@ public class StreamInfoItemsCollector extends InfoItemsCollector<StreamInfoItem,
|
|||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.setUploaderVerified(extractor.isUploaderVerified());
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
|
||||
return resultItem;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,20 +21,46 @@ package org.schabi.newpipe.extractor.stream;
|
|||
*/
|
||||
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.services.youtube.ItagItem;
|
||||
|
||||
public class VideoStream extends Stream {
|
||||
public final String resolution;
|
||||
public final boolean isVideoOnly;
|
||||
|
||||
// Fields for Dash
|
||||
private int itag;
|
||||
private int bitrate;
|
||||
private int initStart;
|
||||
private int initEnd;
|
||||
private int indexStart;
|
||||
private int indexEnd;
|
||||
private int width;
|
||||
private int height;
|
||||
private int fps;
|
||||
private String quality;
|
||||
private String codec;
|
||||
|
||||
public VideoStream(String url, MediaFormat format, String resolution) {
|
||||
this(url, format, resolution, false);
|
||||
}
|
||||
|
||||
public VideoStream(String url, MediaFormat format, String resolution, boolean isVideoOnly) {
|
||||
super(url, format);
|
||||
this.resolution = resolution;
|
||||
this.isVideoOnly = isVideoOnly;
|
||||
this(url, null, format, resolution, isVideoOnly);
|
||||
}
|
||||
|
||||
public VideoStream(String url, boolean isVideoOnly, ItagItem itag) {
|
||||
this(url, itag.getMediaFormat(), itag.resolutionString, isVideoOnly);
|
||||
this.itag = itag.id;
|
||||
this.bitrate = itag.getBitrate();
|
||||
this.initStart = itag.getInitStart();
|
||||
this.initEnd = itag.getInitEnd();
|
||||
this.indexStart = itag.getIndexStart();
|
||||
this.indexEnd = itag.getIndexEnd();
|
||||
this.codec = itag.getCodec();
|
||||
this.height = itag.getHeight();
|
||||
this.width = itag.getWidth();
|
||||
this.quality = itag.getQuality();
|
||||
this.fps = itag.fps;
|
||||
}
|
||||
|
||||
public VideoStream(String url, String torrentUrl, MediaFormat format, String resolution) {
|
||||
|
@ -73,4 +99,48 @@ public class VideoStream extends Stream {
|
|||
public boolean isVideoOnly() {
|
||||
return isVideoOnly;
|
||||
}
|
||||
|
||||
public int getItag() {
|
||||
return itag;
|
||||
}
|
||||
|
||||
public int getBitrate() {
|
||||
return bitrate;
|
||||
}
|
||||
|
||||
public int getInitStart() {
|
||||
return initStart;
|
||||
}
|
||||
|
||||
public int getInitEnd() {
|
||||
return initEnd;
|
||||
}
|
||||
|
||||
public int getIndexStart() {
|
||||
return indexStart;
|
||||
}
|
||||
|
||||
public int getIndexEnd() {
|
||||
return indexEnd;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getFps() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
public String getQuality() {
|
||||
return quality;
|
||||
}
|
||||
|
||||
public String getCodec() {
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,15 +27,17 @@ class MockDownloader extends Downloader {
|
|||
public MockDownloader(@Nonnull String path) throws IOException {
|
||||
this.path = path;
|
||||
this.mocks = new HashMap<>();
|
||||
File folder = new File(path);
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.getName().startsWith(RecordingDownloader.FILE_NAME_PREFIX)) {
|
||||
final FileReader reader = new FileReader(file);
|
||||
final TestRequestResponse response = new GsonBuilder()
|
||||
.create()
|
||||
.fromJson(reader, TestRequestResponse.class);
|
||||
reader.close();
|
||||
mocks.put(response.getRequest(), response.getResponse());
|
||||
final File[] files = new File(path).listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.getName().startsWith(RecordingDownloader.FILE_NAME_PREFIX)) {
|
||||
final FileReader reader = new FileReader(file);
|
||||
final TestRequestResponse response = new GsonBuilder()
|
||||
.create()
|
||||
.fromJson(reader, TestRequestResponse.class);
|
||||
reader.close();
|
||||
mocks.put(response.getRequest(), response.getResponse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,10 @@ import javax.annotation.Nonnull;
|
|||
* The files <b>must</b> be created on the local dev environment
|
||||
* and recreated when the requests made by a test class change.
|
||||
* </p>
|
||||
* <p>
|
||||
* Run the test class as a whole and not each test separately.
|
||||
* Make sure the requests made by a class are unique.
|
||||
* </p>
|
||||
*/
|
||||
class RecordingDownloader extends Downloader {
|
||||
|
||||
|
|
|
@ -7,4 +7,5 @@ public interface BaseChannelExtractorTest extends BaseListExtractorTest {
|
|||
void testBannerUrl() throws Exception;
|
||||
void testFeedUrl() throws Exception;
|
||||
void testSubscriberCount() throws Exception;
|
||||
void testVerified() throws Exception;
|
||||
}
|
||||
|
|
|
@ -7,4 +7,5 @@ public interface BasePlaylistExtractorTest extends BaseListExtractorTest {
|
|||
void testUploaderName() throws Exception;
|
||||
void testUploaderAvatarUrl() throws Exception;
|
||||
void testStreamCount() throws Exception;
|
||||
void testUploaderVerified() throws Exception;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
|
|||
public abstract StreamType expectedStreamType();
|
||||
public abstract String expectedUploaderName();
|
||||
public abstract String expectedUploaderUrl();
|
||||
public boolean expectedUploaderVerified() { return false; }
|
||||
public String expectedSubChannelName() { 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
|
||||
|
@ -99,6 +100,11 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
|
|||
assertIsSecureUrl(extractor().getUploaderAvatarUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploaderVerified() throws Exception {
|
||||
assertEquals(expectedUploaderVerified(), extractor().isUploaderVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testSubChannelName() throws Exception {
|
||||
|
|
|
@ -104,6 +104,11 @@ public class PeertubeAccountExtractorTest {
|
|||
public void testSubscriberCount() throws ParsingException {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class FreeSoftwareFoundation implements BaseChannelExtractorTest {
|
||||
|
@ -200,5 +205,10 @@ public class PeertubeAccountExtractorTest {
|
|||
public void testSubscriberCount() throws ParsingException {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.schabi.newpipe.extractor.services.peertube;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
|
@ -119,6 +118,11 @@ public class PeertubeChannelExtractorTest {
|
|||
public void testSubscriberCount() throws ParsingException {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 230);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChatSceptique implements BaseChannelExtractorTest {
|
||||
|
@ -231,5 +235,10 @@ public class PeertubeChannelExtractorTest {
|
|||
public void testSubscriberCount() throws ParsingException {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 700);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,11 @@ public class SoundcloudChannelExtractorTest {
|
|||
public void testSubscriberCount() {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 1e6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class DubMatix implements BaseChannelExtractorTest {
|
||||
|
@ -195,5 +200,10 @@ public class SoundcloudChannelExtractorTest {
|
|||
public void testSubscriberCount() {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 2e6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,11 @@ public class SoundcloudPlaylistExtractorTest {
|
|||
public void testStreamCount() {
|
||||
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 {
|
||||
|
@ -203,6 +208,11 @@ public class SoundcloudPlaylistExtractorTest {
|
|||
public void testStreamCount() {
|
||||
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 {
|
||||
|
@ -310,6 +320,11 @@ public class SoundcloudPlaylistExtractorTest {
|
|||
public void testStreamCount() {
|
||||
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 {
|
||||
|
@ -409,5 +424,10 @@ public class SoundcloudPlaylistExtractorTest {
|
|||
public void testStreamCount() {
|
||||
assertEquals(2, extractor.getStreamCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUploaderVerified() throws Exception {
|
||||
assertFalse(extractor.isUploaderVerified());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,19 @@ import org.schabi.newpipe.extractor.InfoItem;
|
|||
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
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.services.DefaultSearchExtractorTest;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
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.services.DefaultTests.assertNoDuplicatedItems;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudSearchQueryHandlerFactory.*;
|
||||
|
@ -139,4 +144,40 @@ public class SoundcloudSearchExtractorTest {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
|
@ -15,14 +14,12 @@ import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelEx
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
|
||||
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.defaultTestMoreItems;
|
||||
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRelatedItems;
|
||||
import static org.schabi.newpipe.extractor.services.DefaultTests.*;
|
||||
|
||||
/**
|
||||
* Test for {@link ChannelExtractor}
|
||||
|
@ -127,10 +124,9 @@ public class YoutubeChannelExtractorTest {
|
|||
// ChannelExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Ignore("TODO fix, character ü makes problems")
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
assertTrue(extractor.getDescription().contains("Zart im Schmelz und süffig im Abgang. Ungebremster Spieltrieb"));
|
||||
assertThat(extractor.getDescription(), containsString("Ungebremster Spieltrieb seit 1896."));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -157,6 +153,12 @@ public class YoutubeChannelExtractorTest {
|
|||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 0);
|
||||
assertTrue("Subscriber count too small", extractor.getSubscriberCount() >= 4e6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Youtube RED/Premium ad blocking test
|
||||
|
@ -250,6 +252,11 @@ public class YoutubeChannelExtractorTest {
|
|||
assertTrue("Subscriber count too small", extractor.getSubscriberCount() >= 10e6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Kurzgesagt implements BaseChannelExtractorTest {
|
||||
|
@ -343,6 +350,11 @@ public class YoutubeChannelExtractorTest {
|
|||
public void testSubscriberCount() throws Exception {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 5e6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class KurzgesagtAdditional {
|
||||
|
@ -453,6 +465,11 @@ public class YoutubeChannelExtractorTest {
|
|||
public void testSubscriberCount() throws Exception {
|
||||
assertTrue("Wrong subscriber count", extractor.getSubscriberCount() >= 5e5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class RandomChannel implements BaseChannelExtractorTest {
|
||||
|
@ -550,6 +567,11 @@ public class YoutubeChannelExtractorTest {
|
|||
long subscribers = extractor.getSubscriberCount();
|
||||
assertTrue("Wrong subscriber count: " + subscribers, subscribers >= 50);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
|
||||
|
@ -132,7 +131,6 @@ public class YoutubeCommentsExtractorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO fix")
|
||||
public void testGetCommentsAllData() throws IOException, ExtractionException {
|
||||
final InfoItemsPage<CommentsInfoItem> comments = extractor.getInitialPage();
|
||||
|
||||
|
|
|
@ -13,11 +13,7 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
|||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
|
||||
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.ContinuationsTests;
|
||||
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.YoutubePlaylistExtractorTest.*;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
|
@ -39,7 +35,7 @@ import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRela
|
|||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({NotAvailable.class, TimelessPopHits.class, HugePlaylist.class,
|
||||
LearningPlaylist.class, ContinuationsTests.class})
|
||||
LearningPlaylist.class, ContinuationsTests.class})
|
||||
public class YoutubePlaylistExtractorTest {
|
||||
|
||||
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 {
|
||||
assertTrue("Error in the streams count", extractor.getStreamCount() > 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUploaderVerified() throws Exception {
|
||||
assertFalse(extractor.isUploaderVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class HugePlaylist implements BasePlaylistExtractorTest {
|
||||
|
@ -276,6 +277,11 @@ public class YoutubePlaylistExtractorTest {
|
|||
public void testStreamCount() throws Exception {
|
||||
assertTrue("Error in the streams count", extractor.getStreamCount() > 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUploaderVerified() throws Exception {
|
||||
assertTrue(extractor.isUploaderVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class LearningPlaylist implements BasePlaylistExtractorTest {
|
||||
|
@ -375,6 +381,11 @@ public class YoutubePlaylistExtractorTest {
|
|||
public void testStreamCount() throws Exception {
|
||||
assertTrue("Error in the streams count", extractor.getStreamCount() > 40);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUploaderVerified() throws Exception {
|
||||
assertTrue(extractor.isUploaderVerified());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContinuationsTests {
|
||||
|
@ -388,8 +399,8 @@ public class YoutubePlaylistExtractorTest {
|
|||
@Test
|
||||
public void testNoContinuations() throws Exception {
|
||||
final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube
|
||||
.getPlaylistExtractor(
|
||||
"https://www.youtube.com/playlist?list=PLXJg25X-OulsVsnvZ7RVtSDW-id9_RzAO");
|
||||
.getPlaylistExtractor(
|
||||
"https://www.youtube.com/playlist?list=PLXJg25X-OulsVsnvZ7RVtSDW-id9_RzAO");
|
||||
extractor.fetchPage();
|
||||
|
||||
assertNoMoreItems(extractor);
|
||||
|
@ -398,12 +409,12 @@ public class YoutubePlaylistExtractorTest {
|
|||
@Test
|
||||
public void testOnlySingleContinuation() throws Exception {
|
||||
final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube
|
||||
.getPlaylistExtractor(
|
||||
"https://www.youtube.com/playlist?list=PLjgwFL8urN2DFRuRkFTkmtHjyoNWHHdZX");
|
||||
.getPlaylistExtractor(
|
||||
"https://www.youtube.com/playlist?list=PLjgwFL8urN2DFRuRkFTkmtHjyoNWHHdZX");
|
||||
extractor.fetchPage();
|
||||
|
||||
final ListExtractor.InfoItemsPage<StreamInfoItem> page = defaultTestMoreItems(
|
||||
extractor);
|
||||
extractor);
|
||||
assertFalse("More items available when it shouldn't", page.hasNextPage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,9 @@ public class YoutubeStreamLinkHandlerFactoryTest {
|
|||
assertEquals("-cdveCh1kQk", linkHandler.fromUrl("HTTPS://youtu.be/-cdveCh1kQk)").getId());
|
||||
assertEquals("IOS2fqxwYbA", linkHandler.fromUrl("https://www.youtube.com/shorts/IOS2fqxwYbAhi").getId());
|
||||
assertEquals("IOS2fqxwYbA", linkHandler.fromUrl("http://www.youtube.com/shorts/IOS2fqxwYbA").getId());
|
||||
assertEquals("IOS2fqxwYbA", linkHandler.fromUrl("http://www.youtube.com/v/IOS2fqxwYbA").getId());
|
||||
assertEquals("IOS2fqxwYbA", linkHandler.fromUrl("http://www.youtube.com/w/IOS2fqxwYbA").getId());
|
||||
assertEquals("IOS2fqxwYbA", linkHandler.fromUrl("http://www.youtube.com/watch/IOS2fqxwYbA").getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -113,6 +116,9 @@ public class YoutubeStreamLinkHandlerFactoryTest {
|
|||
assertTrue(linkHandler.acceptUrl("vnd.youtube.launch:jZViOEv90dI"));
|
||||
assertTrue(linkHandler.acceptUrl("https://music.youtube.com/watch?v=O0EDx9WAelc"));
|
||||
assertTrue(linkHandler.acceptUrl("https://www.youtube.com/shorts/IOS2fqxwYbA"));
|
||||
assertTrue(linkHandler.acceptUrl("https://www.youtube.com/v/IOS2fqxwYbA"));
|
||||
assertTrue(linkHandler.acceptUrl("https://www.youtube.com/w/IOS2fqxwYbA"));
|
||||
assertTrue(linkHandler.acceptUrl("https://www.youtube.com/watch/IOS2fqxwYbA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -134,26 +140,33 @@ public class YoutubeStreamLinkHandlerFactoryTest {
|
|||
assertEquals("ocH3oSnZG3c", linkHandler.fromUrl("https://hooktube.com/watch?v=ocH3oSnZG3c&list=PLS2VU1j4vzuZwooPjV26XM9UEBY2CPNn2").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("hooktube.com/watch/3msbfr6pBNE").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("hooktube.com/v/3msbfr6pBNE").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("hooktube.com/w/3msbfr6pBNE").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("hooktube.com/embed/3msbfr6pBNE").getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptInvidioUrl() throws ParsingException {
|
||||
public void testAcceptInvidiousUrl() throws ParsingException {
|
||||
assertTrue(linkHandler.acceptUrl("https://invidio.us/watch?v=TglNG-yjabU"));
|
||||
assertTrue(linkHandler.acceptUrl("http://www.invidio.us/watch?v=TglNG-yjabU"));
|
||||
assertTrue(linkHandler.acceptUrl("http://invidio.us/watch?v=TglNG-yjabU"));
|
||||
assertTrue(linkHandler.acceptUrl("invidio.us/watch?v=3msbfr6pBNE"));
|
||||
assertTrue(linkHandler.acceptUrl("https://invidio.us/watch?v=ocH3oSnZG3c&test=PLS2VU1j4vzuZwooPjV26XM9UEBY2CPNn2"));
|
||||
assertTrue(linkHandler.acceptUrl("invidio.us/embed/3msbfr6pBNE"));
|
||||
assertTrue(linkHandler.acceptUrl("invidio.us/watch/3msbfr6pBNE"));
|
||||
assertTrue(linkHandler.acceptUrl("invidio.us/v/3msbfr6pBNE"));
|
||||
assertTrue(linkHandler.acceptUrl("invidio.us/w/3msbfr6pBNE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInvidioIdfromUrl() throws ParsingException {
|
||||
public void testGetInvidiousIdfromUrl() throws ParsingException {
|
||||
assertEquals("TglNG-yjabU", linkHandler.fromUrl("https://invidio.us/watch?v=TglNG-yjabU").getId());
|
||||
assertEquals("TglNG-yjabU", linkHandler.fromUrl("http://www.invidio.us/watch?v=TglNG-yjabU").getId());
|
||||
assertEquals("TglNG-yjabU", linkHandler.fromUrl("http://invidio.us/watch?v=TglNG-yjabU").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("invidio.us/watch?v=3msbfr6pBNE").getId());
|
||||
assertEquals("ocH3oSnZG3c", linkHandler.fromUrl("https://invidio.us/watch?v=ocH3oSnZG3c&test=PLS2VU1j4vzuZwooPjV26XM9UEBY2CPNn2").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("invidio.us/embed/3msbfr6pBNE").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("invidio.us/v/3msbfr6pBNE").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("invidio.us/w/3msbfr6pBNE").getId());
|
||||
assertEquals("3msbfr6pBNE", linkHandler.fromUrl("invidio.us/watch/3msbfr6pBNE").getId());
|
||||
}
|
||||
}
|
|
@ -13,14 +13,19 @@ import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
|
|||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
|
||||
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.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
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.PLAYLISTS;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.VIDEOS;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
|
||||
|
||||
public class YoutubeSearchExtractorTest {
|
||||
|
||||
|
@ -269,4 +273,41 @@ public class YoutubeSearchExtractorTest {
|
|||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
package org.schabi.newpipe.extractor.services.youtube.search;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.*;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.CHANNELS;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_SONGS;
|
||||
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.utils.Utils.EMPTY_STRING;
|
||||
|
||||
public class YoutubeSearchQHTest {
|
||||
|
||||
@Test
|
||||
@Ignore("TODO fix")
|
||||
public void testRegularValues() throws Exception {
|
||||
assertEquals("https://www.youtube.com/results?search_query=asdf", YouTube.getSearchQHFactory().fromQuery("asdf").getUrl());
|
||||
assertEquals("https://www.youtube.com/results?search_query=hans", YouTube.getSearchQHFactory().fromQuery("hans").getUrl());
|
||||
|
|
|
@ -41,6 +41,7 @@ public class YoutubeStreamExtractorAgeRestrictedTest extends DefaultStreamExtrac
|
|||
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
|
||||
@Override public String expectedUploaderName() { return "EpicFiveTV"; }
|
||||
@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 long expectedLength() { return 1790; }
|
||||
@Override public long expectedTimestamp() { return TIMESTAMP; }
|
||||
|
|
|
@ -103,6 +103,7 @@ public class YoutubeStreamExtractorDefaultTest {
|
|||
return Arrays.asList("https://www.youtube.com/channel/UC7l23W7gFi4Uho6WSzckZRA",
|
||||
"https://www.handcraftpictures.com/");
|
||||
}
|
||||
@Override public boolean expectedUploaderVerified() { return true; }
|
||||
@Override public long expectedLength() { return 381; }
|
||||
@Override public long expectedTimestamp() { return TIMESTAMP; }
|
||||
@Override public long expectedViewCountAtLeast() { return 26682500; }
|
||||
|
@ -150,6 +151,7 @@ public class YoutubeStreamExtractorDefaultTest {
|
|||
@Nullable @Override public String expectedTextualUploadDate() { return "2018-06-19"; }
|
||||
@Override public long expectedLikeCountAtLeast() { return 340100; }
|
||||
@Override public long expectedDislikeCountAtLeast() { return 18700; }
|
||||
@Override public boolean expectedUploaderVerified() { return true; }
|
||||
// @formatter:on
|
||||
@Override
|
||||
@Test
|
||||
|
@ -271,6 +273,7 @@ public class YoutubeStreamExtractorDefaultTest {
|
|||
@Override public String expectedUploaderName() { return "maiLab"; }
|
||||
@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 boolean expectedUploaderVerified() { return true; }
|
||||
@Override public long expectedLength() { return 1010; }
|
||||
@Override public long expectedViewCountAtLeast() { return 815500; }
|
||||
@Nullable @Override public String expectedUploadDate() { return "2020-11-18 00:00:00.000"; }
|
||||
|
@ -339,6 +342,7 @@ public class YoutubeStreamExtractorDefaultTest {
|
|||
Collections.singletonList("Wikipedia (German)")
|
||||
));
|
||||
}
|
||||
@Override public boolean expectedUploaderVerified() { return true; }
|
||||
// @formatter:on
|
||||
@Override
|
||||
@Ignore("TODO fix")
|
||||
|
|
|
@ -45,6 +45,7 @@ public class YoutubeStreamExtractorLivestreamTest extends DefaultStreamExtractor
|
|||
return Arrays.asList("https://bit.ly/chilledcow-playlists",
|
||||
"https://bit.ly/chilledcow-submissions");
|
||||
}
|
||||
@Override public boolean expectedUploaderVerified() { return true; }
|
||||
@Override public long expectedLength() { return 0; }
|
||||
@Override public long expectedTimestamp() { return TIMESTAMP; }
|
||||
@Override public long expectedViewCountAtLeast() { return 0; }
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue