Merge branch 'origin/dev' into dev

This commit is contained in:
Fynn Godau 2020-06-03 21:13:40 +02:00
commit dd955c7f0c
102 changed files with 1716 additions and 589 deletions

8
.gitignore vendored
View File

@ -16,3 +16,11 @@ gradle-app.setting
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties # gradle/wrapper/gradle-wrapper.properties
# vscode / eclipse files
*.classpath
*.project
*.settings
**/bin
**.vscode
*.code-workspace

View File

@ -3,8 +3,7 @@ jdk:
- openjdk8 - openjdk8
script: script:
- ./gradlew check - ./gradlew check aggregatedJavadocs
- ./gradlew aggregatedJavadocs
deploy: deploy:
provider: pages provider: pages

View File

@ -11,7 +11,7 @@ NewPipe Extractor is available at JitPack's Maven repo.
If you're using Gradle, you could add NewPipe Extractor as a dependency with the following steps: If you're using Gradle, you could add NewPipe Extractor as a dependency with the following steps:
1. Add `maven { url 'https://jitpack.io' }` to the `repositories` in your `build.gradle`. 1. Add `maven { url 'https://jitpack.io' }` to the `repositories` in your `build.gradle`.
2. Add `implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.19.0'`the `dependencies` in your `build.gradle`. Replace `v0.19.0` with the latest release. 2. Add `implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.19.5'`the `dependencies` in your `build.gradle`. Replace `v0.19.5` with the latest release.
### Testing changes ### Testing changes

View File

@ -5,7 +5,7 @@ allprojects {
sourceCompatibility = 1.7 sourceCompatibility = 1.7
targetCompatibility = 1.7 targetCompatibility = 1.7
version 'v0.19.0' version 'v0.19.5'
group 'com.github.TeamNewPipe' group 'com.github.TeamNewPipe'
repositories { repositories {

View File

@ -2,10 +2,10 @@ dependencies {
implementation project(':timeago-parser') implementation project(':timeago-parser')
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751' implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'org.jsoup:jsoup:1.9.2' implementation 'org.jsoup:jsoup:1.13.1'
implementation 'org.mozilla:rhino:1.7.7.1' implementation 'org.mozilla:rhino:1.7.12'
implementation 'com.github.spotbugs:spotbugs-annotations:3.1.0' implementation 'com.github.spotbugs:spotbugs-annotations:4.0.2'
implementation 'org.nibor.autolink:autolink:0.8.0' implementation 'org.nibor.autolink:autolink:0.10.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.13'
} }

View File

@ -2,6 +2,7 @@ package org.schabi.newpipe.extractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -9,6 +10,8 @@ import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/** /**
* Base class to extractors that have a list (e.g. playlists, users). * Base class to extractors that have a list (e.g. playlists, users).
*/ */
@ -63,8 +66,7 @@ public abstract class ListExtractor<R extends InfoItem> extends Extractor {
public abstract InfoItemsPage<R> getPage(final String pageUrl) throws IOException, ExtractionException; public abstract InfoItemsPage<R> getPage(final String pageUrl) throws IOException, ExtractionException;
public boolean hasNextPage() throws IOException, ExtractionException { public boolean hasNextPage() throws IOException, ExtractionException {
final String nextPageUrl = getNextPageUrl(); return !isNullOrEmpty(getNextPageUrl());
return nextPageUrl != null && !nextPageUrl.isEmpty();
} }
@Override @Override
@ -123,7 +125,7 @@ public abstract class ListExtractor<R extends InfoItem> extends Extractor {
} }
public boolean hasNextPage() { public boolean hasNextPage() {
return nextPageUrl != null && !nextPageUrl.isEmpty(); return !isNullOrEmpty(nextPageUrl);
} }
public List<T> getItems() { public List<T> getItems() {

View File

@ -4,6 +4,8 @@ import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public abstract class ListInfo<T extends InfoItem> extends Info { public abstract class ListInfo<T extends InfoItem> extends Info {
private List<T> relatedItems; private List<T> relatedItems;
private String nextPageUrl = null; private String nextPageUrl = null;
@ -37,7 +39,7 @@ public abstract class ListInfo<T extends InfoItem> extends Info {
} }
public boolean hasNextPage() { public boolean hasNextPage() {
return nextPageUrl != null && !nextPageUrl.isEmpty(); return !isNullOrEmpty(nextPageUrl);
} }
public String getNextPageUrl() { public String getNextPageUrl() {

View File

@ -37,4 +37,7 @@ public abstract class ChannelExtractor extends ListExtractor<StreamInfoItem> {
public abstract String getFeedUrl() throws ParsingException; public abstract String getFeedUrl() throws ParsingException;
public abstract long getSubscriberCount() throws ParsingException; public abstract long getSubscriberCount() throws ParsingException;
public abstract String getDescription() throws ParsingException; public abstract String getDescription() throws ParsingException;
public abstract String getParentChannelName() throws ParsingException;
public abstract String getParentChannelUrl() throws ParsingException;
public abstract String getParentChannelAvatarUrl() throws ParsingException;
} }

View File

@ -94,16 +94,61 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
info.addError(e); info.addError(e);
} }
try {
info.setParentChannelName(extractor.getParentChannelName());
} catch (Exception e) {
info.addError(e);
}
try {
info.setParentChannelUrl(extractor.getParentChannelUrl());
} catch (Exception e) {
info.addError(e);
}
try {
info.setParentChannelAvatarUrl(extractor.getParentChannelAvatarUrl());
} catch (Exception e) {
info.addError(e);
}
return info; return info;
} }
private String avatarUrl; private String avatarUrl;
private String parentChannelName;
private String parentChannelUrl;
private String parentChannelAvatarUrl;
private String bannerUrl; private String bannerUrl;
private String feedUrl; private String feedUrl;
private long subscriberCount = -1; private long subscriberCount = -1;
private String description; private String description;
private String[] donationLinks; private String[] donationLinks;
public String getParentChannelName() {
return parentChannelName;
}
public void setParentChannelName(String parentChannelName) {
this.parentChannelName = parentChannelName;
}
public String getParentChannelUrl() {
return parentChannelUrl;
}
public void setParentChannelUrl(String parentChannelUrl) {
this.parentChannelUrl = parentChannelUrl;
}
public String getParentChannelAvatarUrl() {
return parentChannelAvatarUrl;
}
public void setParentChannelAvatarUrl(String parentChannelAvatarUrl) {
this.parentChannelAvatarUrl = parentChannelAvatarUrl;
}
public String getAvatarUrl() { public String getAvatarUrl() {
return avatarUrl; return avatarUrl;
} }

View File

@ -2,8 +2,11 @@ package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import javax.annotation.Nonnull;
public abstract class CommentsExtractor extends ListExtractor<CommentsInfoItem> { public abstract class CommentsExtractor extends ListExtractor<CommentsInfoItem> {
public CommentsExtractor(StreamingService service, ListLinkHandler uiHandler) { public CommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
@ -11,4 +14,9 @@ public abstract class CommentsExtractor extends ListExtractor<CommentsInfoItem>
// TODO Auto-generated constructor stub // TODO Auto-generated constructor stub
} }
@Nonnull
@Override
public String getName() throws ParsingException {
return "Comments";
}
} }

View File

@ -9,11 +9,12 @@ public class CommentsInfoItem extends InfoItem {
private String commentId; private String commentId;
private String commentText; private String commentText;
private String authorName; private String uploaderName;
private String authorThumbnail; private String uploaderAvatarUrl;
private String authorEndpoint; private String uploaderUrl;
private String textualPublishedTime; private String textualUploadDate;
@Nullable private DateWrapper publishedTime; @Nullable
private DateWrapper uploadDate;
private int likeCount; private int likeCount;
public CommentsInfoItem(int serviceId, String url, String name) { public CommentsInfoItem(int serviceId, String url, String name) {
@ -36,45 +37,45 @@ public class CommentsInfoItem extends InfoItem {
this.commentText = commentText; this.commentText = commentText;
} }
public String getAuthorName() { public String getUploaderName() {
return authorName; return uploaderName;
} }
public void setAuthorName(String authorName) { public void setUploaderName(String uploaderName) {
this.authorName = authorName; this.uploaderName = uploaderName;
} }
public String getAuthorThumbnail() { public String getUploaderAvatarUrl() {
return authorThumbnail; return uploaderAvatarUrl;
} }
public void setAuthorThumbnail(String authorThumbnail) { public void setUploaderAvatarUrl(String uploaderAvatarUrl) {
this.authorThumbnail = authorThumbnail; this.uploaderAvatarUrl = uploaderAvatarUrl;
} }
public String getAuthorEndpoint() { public String getUploaderUrl() {
return authorEndpoint; return uploaderUrl;
} }
public void setAuthorEndpoint(String authorEndpoint) { public void setUploaderUrl(String uploaderUrl) {
this.authorEndpoint = authorEndpoint; this.uploaderUrl = uploaderUrl;
} }
public String getTextualPublishedTime() { public String getTextualUploadDate() {
return textualPublishedTime; return textualUploadDate;
} }
public void setTextualPublishedTime(String textualPublishedTime) { public void setTextualUploadDate(String textualUploadDate) {
this.textualPublishedTime = textualPublishedTime; this.textualUploadDate = textualUploadDate;
} }
@Nullable @Nullable
public DateWrapper getPublishedTime() { public DateWrapper getUploadDate() {
return publishedTime; return uploadDate;
} }
public void setPublishedTime(@Nullable DateWrapper publishedTime) { public void setUploadDate(@Nullable DateWrapper uploadDate) {
this.publishedTime = publishedTime; this.uploadDate = uploadDate;
} }
public int getLikeCount() { public int getLikeCount() {

View File

@ -3,17 +3,41 @@ package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.InfoItemExtractor; import org.schabi.newpipe.extractor.InfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; 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.stream.StreamExtractor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public interface CommentsInfoItemExtractor extends InfoItemExtractor { public interface CommentsInfoItemExtractor extends InfoItemExtractor {
String getCommentId() throws ParsingException;
String getCommentText() throws ParsingException; /**
String getAuthorName() throws ParsingException; * Return the like count of the comment, or -1 if it's unavailable
String getAuthorThumbnail() throws ParsingException; * @see StreamExtractor#getLikeCount()
String getAuthorEndpoint() throws ParsingException; */
String getTextualPublishedTime() throws ParsingException;
@Nullable
DateWrapper getPublishedTime() throws ParsingException;
int getLikeCount() throws ParsingException; int getLikeCount() throws ParsingException;
/**
* The text of the comment
*/
String getCommentText() throws ParsingException;
/**
* The upload date given by the service, unmodified
* @see StreamExtractor#getTextualUploadDate()
*/
String getTextualUploadDate() throws ParsingException;
/**
* The upload date wrapped with DateWrapper class
* @see StreamExtractor#getUploadDate()
*/
@Nullable
DateWrapper getUploadDate() throws ParsingException;
String getCommentId() throws ParsingException;
String getUploaderUrl() throws ParsingException;
String getUploaderName() throws ParsingException;
String getUploaderAvatarUrl() throws ParsingException;
} }

View File

@ -35,27 +35,27 @@ public class CommentsInfoItemsCollector extends InfoItemsCollector<CommentsInfoI
addError(e); addError(e);
} }
try { try {
resultItem.setAuthorName(extractor.getAuthorName()); resultItem.setUploaderName(extractor.getUploaderName());
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setAuthorThumbnail(extractor.getAuthorThumbnail()); resultItem.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setAuthorEndpoint(extractor.getAuthorEndpoint()); resultItem.setUploaderUrl(extractor.getUploaderUrl());
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setTextualPublishedTime(extractor.getTextualPublishedTime()); resultItem.setTextualUploadDate(extractor.getTextualUploadDate());
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setPublishedTime(extractor.getPublishedTime()); resultItem.setUploadDate(extractor.getUploadDate());
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }

View File

@ -6,6 +6,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import javax.annotation.Nonnull;
public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> { public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> {
public PlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) { public PlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
@ -20,4 +22,9 @@ public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> {
public abstract String getUploaderAvatarUrl() throws ParsingException; public abstract String getUploaderAvatarUrl() throws ParsingException;
public abstract long getStreamCount() throws ParsingException; public abstract long getStreamCount() throws ParsingException;
@Nonnull public abstract String getSubChannelName() throws ParsingException;
@Nonnull public abstract String getSubChannelUrl() throws ParsingException;
@Nonnull public abstract String getSubChannelAvatarUrl() throws ParsingException;
} }

View File

@ -84,6 +84,21 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
info.setUploaderAvatarUrl(""); info.setUploaderAvatarUrl("");
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try {
info.setSubChannelUrl(extractor.getSubChannelUrl());
} catch (Exception e) {
uploaderParsingErrors.add(e);
}
try {
info.setSubChannelName(extractor.getSubChannelName());
} catch (Exception e) {
uploaderParsingErrors.add(e);
}
try {
info.setSubChannelAvatarUrl(extractor.getSubChannelAvatarUrl());
} catch (Exception e) {
uploaderParsingErrors.add(e);
}
try { try {
info.setBannerUrl(extractor.getBannerUrl()); info.setBannerUrl(extractor.getBannerUrl());
} catch (Exception e) { } catch (Exception e) {
@ -107,6 +122,9 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
private String uploaderUrl; private String uploaderUrl;
private String uploaderName; private String uploaderName;
private String uploaderAvatarUrl; private String uploaderAvatarUrl;
private String subChannelUrl;
private String subChannelName;
private String subChannelAvatarUrl;
private long streamCount = 0; private long streamCount = 0;
public String getThumbnailUrl() { public String getThumbnailUrl() {
@ -149,6 +167,30 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
this.uploaderAvatarUrl = uploaderAvatarUrl; this.uploaderAvatarUrl = uploaderAvatarUrl;
} }
public String getSubChannelUrl() {
return subChannelUrl;
}
public void setSubChannelUrl(String subChannelUrl) {
this.subChannelUrl = subChannelUrl;
}
public String getSubChannelName() {
return subChannelName;
}
public void setSubChannelName(String subChannelName) {
this.subChannelName = subChannelName;
}
public String getSubChannelAvatarUrl() {
return subChannelAvatarUrl;
}
public void setSubChannelAvatarUrl(String subChannelAvatarUrl) {
this.subChannelAvatarUrl = subChannelAvatarUrl;
}
public long getStreamCount() { public long getStreamCount() {
return streamCount; return streamCount;
} }

View File

@ -25,6 +25,16 @@ public abstract class SearchExtractor extends ListExtractor<InfoItem> {
return getLinkHandler().getSearchString(); return getLinkHandler().getSearchString();
} }
/**
* The search suggestion provided by the service.
* <p>
* This method also returns the corrected query if
* {@link SearchExtractor#isCorrectedSearch()} is true.
*
* @return a suggestion to another query, the corrected query, or an empty String.
* @throws ParsingException
*/
@Nonnull
public abstract String getSearchSuggestion() throws ParsingException; public abstract String getSearchSuggestion() throws ParsingException;
@Override @Override
@ -37,4 +47,14 @@ public abstract class SearchExtractor extends ListExtractor<InfoItem> {
public String getName() { public String getName() {
return getLinkHandler().getSearchString(); return getLinkHandler().getSearchString();
} }
/**
* Tell if the search was corrected by the service (if it's not exactly the search you typed).
* <p>
* Example: on YouTube, if you search for "pewdeipie",
* it will give you results for "pewdiepie", then isCorrectedSearch should return true.
*
* @return whether the results comes from a corrected query or not.
*/
public abstract boolean isCorrectedSearch() throws ParsingException;
} }

View File

@ -15,6 +15,7 @@ public class SearchInfo extends ListInfo<InfoItem> {
private String searchString; private String searchString;
private String searchSuggestion; private String searchSuggestion;
private boolean isCorrectedSearch;
public SearchInfo(int serviceId, public SearchInfo(int serviceId,
SearchQueryHandler qIHandler, SearchQueryHandler qIHandler,
@ -42,7 +43,12 @@ public class SearchInfo extends ListInfo<InfoItem> {
info.addError(e); info.addError(e);
} }
try { try {
info.searchSuggestion = extractor.getSearchSuggestion(); info.setSearchSuggestion(extractor.getSearchSuggestion());
} catch (Exception e) {
info.addError(e);
}
try {
info.setIsCorrectedSearch(extractor.isCorrectedSearch());
} catch (Exception e) { } catch (Exception e) {
info.addError(e); info.addError(e);
} }
@ -64,10 +70,22 @@ public class SearchInfo extends ListInfo<InfoItem> {
// Getter // Getter
public String getSearchString() { public String getSearchString() {
return searchString; return this.searchString;
} }
public String getSearchSuggestion() { public String getSearchSuggestion() {
return searchSuggestion; return this.searchSuggestion;
}
public boolean isCorrectedSearch() {
return this.isCorrectedSearch;
}
public void setIsCorrectedSearch(boolean isCorrectedSearch) {
this.isCorrectedSearch = isCorrectedSearch;
}
public void setSearchSuggestion(String searchSuggestion) {
this.searchSuggestion = searchSuggestion;
} }
} }

View File

@ -52,6 +52,21 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
return null; return null;
} }
@Override
public String getParentChannelName() throws ParsingException {
return "";
}
@Override
public String getParentChannelUrl() throws ParsingException {
return "";
}
@Override
public String getParentChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() { public InfoItemsPage<StreamInfoItem> getInitialPage() {

View File

@ -11,6 +11,7 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor; import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector; import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
@ -42,9 +43,15 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
} }
} }
@Nonnull
@Override @Override
public String getSearchSuggestion() { public String getSearchSuggestion() {
return null; return "";
}
@Override
public boolean isCorrectedSearch() {
return false;
} }
@Nonnull @Nonnull

View File

@ -112,7 +112,25 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public String getDashMpdUrl() { public String getSubChannelUrl() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelName() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getDashMpdUrl() throws ParsingException {
return ""; return "";
} }

View File

@ -3,13 +3,14 @@ package org.schabi.newpipe.extractor.services.peertube;
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.jsoup.helper.StringUtil;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response; import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
@ -43,7 +44,7 @@ public class PeertubeInstance {
throw new Exception("unable to configure instance " + url, e); throw new Exception("unable to configure instance " + url, e);
} }
if (response == null || StringUtil.isBlank(response.responseBody())) { if (response == null || Utils.isBlank(response.responseBody())) {
throw new Exception("unable to configure instance " + url); throw new Exception("unable to configure instance " + url);
} }

View File

@ -1,9 +1,11 @@
package org.schabi.newpipe.extractor.services.peertube; package org.schabi.newpipe.extractor.services.peertube;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.jsoup.helper.StringUtil;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; 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.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -13,12 +15,17 @@ import java.util.TimeZone;
public class PeertubeParsingHelper { public class PeertubeParsingHelper {
public static final String START_KEY = "start";
public static final String COUNT_KEY = "count";
public static final int ITEMS_PER_PAGE = 12;
public static final String START_PATTERN = "start=(\\d*)";
private PeertubeParsingHelper() { private PeertubeParsingHelper() {
} }
public static void validate(JsonObject json) throws ContentNotAvailableException { public static void validate(JsonObject json) throws ContentNotAvailableException {
String error = json.getString("error"); String error = json.getString("error");
if (!StringUtil.isBlank(error)) { if (!Utils.isBlank(error)) {
throw new ContentNotAvailableException(error); throw new ContentNotAvailableException(error);
} }
} }
@ -38,4 +45,26 @@ public class PeertubeParsingHelper {
return uploadDate; return uploadDate;
} }
public static String getNextPageUrl(String prevPageUrl, long total) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (Parser.RegexException e) {
return "";
}
if (Utils.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.parseLong(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + nextStart);
}
}
} }

View File

@ -44,8 +44,7 @@ public class PeertubeService extends StreamingService {
@Override @Override
public ListLinkHandlerFactory getPlaylistLHFactory() { public ListLinkHandlerFactory getPlaylistLHFactory() {
// TODO Auto-generated method stub return PeertubePlaylistLinkHandlerFactory.getInstance();
return null;
} }
@Override @Override
@ -70,7 +69,6 @@ public class PeertubeService extends StreamingService {
@Override @Override
public SubscriptionExtractor getSubscriptionExtractor() { public SubscriptionExtractor getSubscriptionExtractor() {
// TODO Auto-generated method stub
return null; return null;
} }
@ -88,8 +86,7 @@ public class PeertubeService extends StreamingService {
@Override @Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
// TODO Auto-generated method stub return new PeertubePlaylistExtractor(this, linkHandler);
return null;
} }
@Override @Override

View File

@ -4,7 +4,7 @@ import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.jsoup.helper.StringUtil;
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;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
@ -16,17 +16,13 @@ import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
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 org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Utils;
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException; import java.io.IOException;
public class PeertubeAccountExtractor extends ChannelExtractor { import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start"; public class PeertubeAccountExtractor extends ChannelExtractor {
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
private InfoItemsPage<StreamInfoItem> initPage; private InfoItemsPage<StreamInfoItem> initPage;
private long total; private long total;
@ -75,6 +71,21 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
} }
} }
@Override
public String getParentChannelName() throws ParsingException {
return "";
}
@Override
public String getParentChannelUrl() throws ParsingException {
return "";
}
@Override
public String getParentChannelAvatarUrl() throws ParsingException {
return "";
}
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
super.fetchPage(); super.fetchPage();
@ -109,7 +120,7 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Response response = getDownloader().get(pageUrl); Response response = getDownloader().get(pageUrl);
JsonObject json = null; JsonObject json = null;
if (response != null && !StringUtil.isBlank(response.responseBody())) { if (response != null && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); json = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (Exception e) {
@ -120,36 +131,12 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
if (json != null) { if (json != null) {
PeertubeParsingHelper.validate(json); PeertubeParsingHelper.validate(json);
Number number = JsonUtils.getNumber(json, "total"); total = JsonUtils.getNumber(json, "total").longValue();
if (number != null) this.total = number.longValue();
collectStreamsFrom(collector, json, pageUrl); collectStreamsFrom(collector, json, pageUrl);
} else { } else {
throw new ExtractionException("Unable to get PeerTube kiosk info"); throw new ExtractionException("Unable to get PeerTube kiosk info");
} }
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl)); return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
} }
@Override @Override

View File

@ -4,7 +4,7 @@ import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.jsoup.helper.StringUtil;
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;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
@ -18,15 +18,13 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException; import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
public class PeertubeChannelExtractor extends ChannelExtractor { import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start"; public class PeertubeChannelExtractor extends ChannelExtractor {
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
private InfoItemsPage<StreamInfoItem> initPage; private InfoItemsPage<StreamInfoItem> initPage;
private long total; private long total;
@ -75,6 +73,27 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
} }
} }
@Override
public String getParentChannelName() throws ParsingException {
return JsonUtils.getString(json, "ownerAccount.name");
}
@Override
public String getParentChannelUrl() throws ParsingException {
return JsonUtils.getString(json, "ownerAccount.url");
}
@Override
public String getParentChannelAvatarUrl() throws ParsingException {
String value;
try {
value = JsonUtils.getString(json, "ownerAccount.avatar.path");
} catch (Exception e) {
value = "/client/assets/images/default-avatar.png";
}
return baseUrl + value;
}
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
super.fetchPage(); super.fetchPage();
@ -109,7 +128,7 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Response response = getDownloader().get(pageUrl); Response response = getDownloader().get(pageUrl);
JsonObject json = null; JsonObject json = null;
if (null != response && !StringUtil.isBlank(response.responseBody())) { if (response != null && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); json = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (Exception e) {
@ -120,36 +139,12 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
if (json != null) { if (json != null) {
PeertubeParsingHelper.validate(json); PeertubeParsingHelper.validate(json);
Number number = JsonUtils.getNumber(json, "total"); this.total = JsonUtils.getNumber(json, "total").longValue();
if (number != null) this.total = number.longValue();
collectStreamsFrom(collector, json, pageUrl); collectStreamsFrom(collector, json, pageUrl);
} else { } else {
throw new ExtractionException("Unable to get PeerTube kiosk info"); throw new ExtractionException("Unable to get PeerTube kiosk info");
} }
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl)); return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
} }
@Override @Override
@ -161,8 +156,7 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
throw new ExtractionException("Unable to extract PeerTube channel data"); throw new ExtractionException("Unable to extract PeerTube channel data");
} }
String pageUrl = getUrl() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE; this.initPage = getPage(getUrl() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
this.initPage = getPage(pageUrl);
} }
private void setInitialData(String responseBody) throws ExtractionException { private void setInitialData(String responseBody) throws ExtractionException {

View File

@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonArray; 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 org.jsoup.helper.StringUtil;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.comments.CommentsExtractor; import org.schabi.newpipe.extractor.comments.CommentsExtractor;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
@ -13,18 +13,17 @@ import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException; import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
public class PeertubeCommentsExtractor extends CommentsExtractor { import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start"; public class PeertubeCommentsExtractor extends CommentsExtractor {
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
private InfoItemsPage<CommentsInfoItem> initPage; private InfoItemsPage<CommentsInfoItem> initPage;
private long total; private long total;
@ -33,11 +32,6 @@ public class PeertubeCommentsExtractor extends CommentsExtractor {
super(service, uiHandler); super(service, uiHandler);
} }
@Override
public String getName() throws ParsingException {
return "Comments";
}
@Override @Override
public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException {
super.fetchPage(); super.fetchPage();
@ -72,7 +66,7 @@ public class PeertubeCommentsExtractor extends CommentsExtractor {
public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Response response = getDownloader().get(pageUrl); Response response = getDownloader().get(pageUrl);
JsonObject json = null; JsonObject json = null;
if (null != response && !StringUtil.isBlank(response.responseBody())) { if (response != null && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); json = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (Exception e) {
@ -88,35 +82,11 @@ public class PeertubeCommentsExtractor extends CommentsExtractor {
} else { } else {
throw new ExtractionException("Unable to get peertube comments info"); throw new ExtractionException("Unable to get peertube comments info");
} }
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl)); return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
} }
@Override @Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
String pageUrl = getUrl() + "?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE; this.initPage = getPage(getUrl() + "?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
this.initPage = getPage(pageUrl);
} }
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
}
} }

View File

@ -45,13 +45,13 @@ public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtrac
} }
@Override @Override
public String getTextualPublishedTime() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
return JsonUtils.getString(item, "createdAt"); return JsonUtils.getString(item, "createdAt");
} }
@Override @Override
public DateWrapper getPublishedTime() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
String textualUploadDate = getTextualPublishedTime(); String textualUploadDate = getTextualUploadDate();
return new DateWrapper(PeertubeParsingHelper.parseDateFrom(textualUploadDate)); return new DateWrapper(PeertubeParsingHelper.parseDateFrom(textualUploadDate));
} }
@ -78,7 +78,7 @@ public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtrac
} }
@Override @Override
public String getAuthorThumbnail() throws ParsingException { public String getUploaderAvatarUrl() throws ParsingException {
String value; String value;
try { try {
value = JsonUtils.getString(item, "account.avatar.path"); value = JsonUtils.getString(item, "account.avatar.path");
@ -89,12 +89,12 @@ public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtrac
} }
@Override @Override
public String getAuthorName() 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");
} }
@Override @Override
public String getAuthorEndpoint() throws ParsingException { public String getUploaderUrl() throws ParsingException {
String name = JsonUtils.getString(item, "account.name"); String name = JsonUtils.getString(item, "account.name");
String host = JsonUtils.getString(item, "account.host"); String host = JsonUtils.getString(item, "account.host");
return ServiceList.PeerTube.getChannelLHFactory().fromId("accounts/" + name + "@" + host, baseUrl).getUrl(); return ServiceList.PeerTube.getChannelLHFactory().fromId("accounts/" + name + "@" + host, baseUrl).getUrl();

View File

@ -1,85 +1,135 @@
package org.schabi.newpipe.extractor.services.peertube.extractors; package org.schabi.newpipe.extractor.services.peertube.extractors;
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.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
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.utils.JsonUtils;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
public class PeertubePlaylistExtractor extends PlaylistExtractor { public class PeertubePlaylistExtractor extends PlaylistExtractor {
private JsonObject playlistInfo;
private JsonObject playlistVideos;
private String initialPageUrl;
private long total;
public PeertubePlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) { public PeertubePlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
// TODO Auto-generated constructor stub
} }
@Override @Override
public String getThumbnailUrl() throws ParsingException { public String getThumbnailUrl() throws ParsingException {
// TODO Auto-generated method stub return getBaseUrl() + playlistInfo.getString("thumbnailPath");
return null;
} }
@Override @Override
public String getBannerUrl() throws ParsingException { public String getBannerUrl() throws ParsingException {
// TODO Auto-generated method stub
return null; return null;
} }
@Override @Override
public String getUploaderUrl() throws ParsingException { public String getUploaderUrl() throws ParsingException {
// TODO Auto-generated method stub return playlistInfo.getObject("ownerAccount").getString("url");
return null;
} }
@Override @Override
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
// TODO Auto-generated method stub return playlistInfo.getObject("ownerAccount").getString("displayName");
return null;
} }
@Override @Override
public String getUploaderAvatarUrl() throws ParsingException { public String getUploaderAvatarUrl() throws ParsingException {
// TODO Auto-generated method stub return getBaseUrl() + playlistInfo.getObject("ownerAccount").getObject("avatar").getString("path");
return null;
} }
@Override @Override
public long getStreamCount() throws ParsingException { public long getStreamCount() throws ParsingException {
// TODO Auto-generated method stub return playlistInfo.getNumber("videosLength").longValue();
return 0;
} }
@Nonnull
@Override
public String getSubChannelName() throws ParsingException {
return playlistInfo.getObject("videoChannel").getString("displayName");
}
@Nonnull
@Override
public String getSubChannelUrl() throws ParsingException {
return playlistInfo.getObject("videoChannel").getString("url");
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() throws ParsingException {
return getBaseUrl() + playlistInfo.getObject("videoChannel").getObject("avatar").getString("path");
}
@Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
// TODO Auto-generated method stub return getPage(initialPageUrl);
return null;
} }
@Override @Override
public String getNextPageUrl() throws IOException, ExtractionException { public String getNextPageUrl() throws IOException, ExtractionException {
// TODO Auto-generated method stub return PeertubeParsingHelper.getNextPageUrl(initialPageUrl, total);
return null;
} }
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
// TODO Auto-generated method stub Response response = getDownloader().get(pageUrl);
return null; try {
playlistVideos = JsonParser.object().from(response.responseBody());
} catch (JsonParserException jpe) {
throw new ExtractionException("Could not parse json", jpe);
}
PeertubeParsingHelper.validate(playlistVideos);
this.total = JsonUtils.getNumber(playlistVideos, "total").longValue();
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
JsonArray videos = playlistVideos.getArray("data");
for (Object o : videos) {
JsonObject video = ((JsonObject) o).getObject("video");
collector.commit(new PeertubeStreamInfoItemExtractor(video, getBaseUrl()));
}
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
} }
@Override @Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
Response response = downloader.get(getUrl());
try {
playlistInfo = JsonParser.object().from(response.responseBody());
} catch (JsonParserException jpe) {
throw new ExtractionException("Could not parse json", jpe);
}
PeertubeParsingHelper.validate(playlistInfo);
initialPageUrl = getUrl() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
} }
@Nonnull
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
// TODO Auto-generated method stub return playlistInfo.getString("displayName");
return null;
} }
} }

View File

@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonArray; 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 org.jsoup.helper.StringUtil;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.InfoItemExtractor; import org.schabi.newpipe.extractor.InfoItemExtractor;
import org.schabi.newpipe.extractor.InfoItemsCollector; import org.schabi.newpipe.extractor.InfoItemsCollector;
@ -15,18 +15,18 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector; import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException; import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
public class PeertubeSearchExtractor extends SearchExtractor { import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start"; public class PeertubeSearchExtractor extends SearchExtractor {
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
private InfoItemsPage<InfoItem> initPage; private InfoItemsPage<InfoItem> initPage;
private long total; private long total;
@ -35,9 +35,15 @@ public class PeertubeSearchExtractor extends SearchExtractor {
super(service, linkHandler); super(service, linkHandler);
} }
@Nonnull
@Override @Override
public String getSearchSuggestion() throws ParsingException { public String getSearchSuggestion() throws ParsingException {
return null; return "";
}
@Override
public boolean isCorrectedSearch() {
return false;
} }
@Override @Override
@ -79,7 +85,7 @@ public class PeertubeSearchExtractor extends SearchExtractor {
public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Response response = getDownloader().get(pageUrl); Response response = getDownloader().get(pageUrl);
JsonObject json = null; JsonObject json = null;
if (null != response && !StringUtil.isBlank(response.responseBody())) { if (null != response && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); json = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (Exception e) {
@ -88,9 +94,8 @@ public class PeertubeSearchExtractor extends SearchExtractor {
} }
if (json != null) { if (json != null) {
Number number = JsonUtils.getNumber(json, "total"); total = JsonUtils.getNumber(json, "total").longValue();
if (number != null) this.total = number.longValue(); return new InfoItemsPage<>(collectStreamsFrom(json), PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
return new InfoItemsPage<>(collectStreamsFrom(json), getNextPageUrl(pageUrl));
} else { } else {
throw new ExtractionException("Unable to get peertube search info"); throw new ExtractionException("Unable to get peertube search info");
} }
@ -98,31 +103,6 @@ public class PeertubeSearchExtractor extends SearchExtractor {
@Override @Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
String pageUrl = getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE; initPage = getPage(getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
this.initPage = getPage(pageUrl);
} }
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
}
} }

View File

@ -4,7 +4,7 @@ import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.jsoup.helper.StringUtil;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
@ -17,10 +17,18 @@ 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.services.peertube.PeertubeParsingHelper; import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory; import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
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.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -29,6 +37,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.annotation.Nonnull;
public class PeertubeStreamExtractor extends StreamExtractor { public class PeertubeStreamExtractor extends StreamExtractor {
@ -147,6 +157,29 @@ public class PeertubeStreamExtractor extends StreamExtractor {
return baseUrl + value; return baseUrl + value;
} }
@Override
public String getSubChannelUrl() throws ParsingException {
return JsonUtils.getString(json, "channel.url");
}
@Nonnull
@Override
public String getSubChannelName() throws ParsingException {
return JsonUtils.getString(json, "channel.displayName");
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() throws ParsingException {
String value;
try {
value = JsonUtils.getString(json, "channel.avatar.path");
} catch (Exception e) {
value = "/client/assets/images/default-avatar.png";
}
return baseUrl + value;
}
@Override @Override
public String getDashMpdUrl() throws ParsingException { public String getDashMpdUrl() throws ParsingException {
return ""; return "";
@ -232,7 +265,7 @@ public class PeertubeStreamExtractor extends StreamExtractor {
} else { } else {
apiUrl = getUploaderUrl() + "/videos?start=0&count=8"; apiUrl = getUploaderUrl() + "/videos?start=0&count=8";
} }
if (!StringUtil.isBlank(apiUrl)) getStreamsFromApi(collector, apiUrl); if (!Utils.isBlank(apiUrl)) getStreamsFromApi(collector, apiUrl);
return collector; return collector;
} }
@ -269,7 +302,7 @@ public class PeertubeStreamExtractor extends StreamExtractor {
private void getStreamsFromApi(StreamInfoItemsCollector collector, String apiUrl) throws ReCaptchaException, IOException, ParsingException { private void getStreamsFromApi(StreamInfoItemsCollector collector, String apiUrl) throws ReCaptchaException, IOException, ParsingException {
Response response = getDownloader().get(apiUrl); Response response = getDownloader().get(apiUrl);
JsonObject relatedVideosJson = null; JsonObject relatedVideosJson = null;
if (null != response && !StringUtil.isBlank(response.responseBody())) { if (null != response && !Utils.isBlank(response.responseBody())) {
try { try {
relatedVideosJson = JsonParser.object().from(response.responseBody()); relatedVideosJson = JsonParser.object().from(response.responseBody());
} catch (JsonParserException e) { } catch (JsonParserException e) {

View File

@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonArray; 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 org.jsoup.helper.StringUtil;
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;
import org.schabi.newpipe.extractor.downloader.Response; import org.schabi.newpipe.extractor.downloader.Response;
@ -11,20 +11,17 @@ 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.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
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 org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Utils;
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException; import java.io.IOException;
public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> { import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start"; public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
private InfoItemsPage<StreamInfoItem> initPage; private InfoItemsPage<StreamInfoItem> initPage;
private long total; private long total;
@ -73,7 +70,7 @@ public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Response response = getDownloader().get(pageUrl); Response response = getDownloader().get(pageUrl);
JsonObject json = null; JsonObject json = null;
if (null != response && !StringUtil.isBlank(response.responseBody())) { if (response != null && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); json = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (Exception e) {
@ -89,35 +86,12 @@ public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
} else { } else {
throw new ExtractionException("Unable to get peertube kiosk info"); throw new ExtractionException("Unable to get peertube kiosk info");
} }
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl)); return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
} }
@Override @Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
String pageUrl = getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE; this.initPage = getPage(getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
this.initPage = getPage(pageUrl);
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
} }
} }

View File

@ -11,8 +11,7 @@ import java.util.List;
public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory { public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
private static final PeertubePlaylistLinkHandlerFactory instance = new PeertubePlaylistLinkHandlerFactory(); private static final PeertubePlaylistLinkHandlerFactory instance = new PeertubePlaylistLinkHandlerFactory();
private static final String ID_PATTERN = "/video-channels/([^/?&#]*)"; private static final String ID_PATTERN = "/videos/watch/playlist/([^/?&#]*)";
private static final String VIDEO_CHANNELS_ENDPOINT = "/api/v1/video-channels/";
public static PeertubePlaylistLinkHandlerFactory getInstance() { public static PeertubePlaylistLinkHandlerFactory getInstance() {
return instance; return instance;
@ -26,7 +25,7 @@ public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
@Override @Override
public String getUrl(String id, List<String> contentFilters, String sortFilter, String baseUrl) { public String getUrl(String id, List<String> contentFilters, String sortFilter, String baseUrl) {
return baseUrl + VIDEO_CHANNELS_ENDPOINT + id; return baseUrl + "/api/v1/video-playlists/" + id;
} }
@Override @Override
@ -34,9 +33,13 @@ public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
return Parser.matchGroup1(ID_PATTERN, url); return Parser.matchGroup1(ID_PATTERN, url);
} }
@Override @Override
public boolean onAcceptUrl(final String url) { public boolean onAcceptUrl(final String url) {
return url.contains("/video-channels/"); try {
getId(url);
return true;
} catch (ParsingException e) {
return false;
}
} }
} }

View File

@ -37,6 +37,12 @@ public class PeertubeStreamLinkHandlerFactory extends LinkHandlerFactory {
@Override @Override
public boolean onAcceptUrl(final String url) throws FoundAdException { public boolean onAcceptUrl(final String url) throws FoundAdException {
return url.contains("/videos/"); if (url.contains("/playlist/")) return false;
try {
getId(url);
return true;
} catch (ParsingException e) {
return false;
}
} }
} }

View File

@ -15,9 +15,13 @@ import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudStreamExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudStreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException; import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -29,6 +33,7 @@ import java.util.*;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps; import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
public class SoundcloudParsingHelper { public class SoundcloudParsingHelper {
@ -39,7 +44,7 @@ public class SoundcloudParsingHelper {
} }
public static String clientId() throws ExtractionException, IOException { public static String clientId() throws ExtractionException, IOException {
if (clientId != null && !clientId.isEmpty()) return clientId; if (!isNullOrEmpty(clientId)) return clientId;
Downloader dl = NewPipe.getDownloader(); Downloader dl = NewPipe.getDownloader();
clientId = HARDCODED_CLIENT_ID; clientId = HARDCODED_CLIENT_ID;
@ -61,7 +66,7 @@ public class SoundcloudParsingHelper {
for (Element element : possibleScripts) { for (Element element : possibleScripts) {
final String srcUrl = element.attr("src"); final String srcUrl = element.attr("src");
if (srcUrl != null && !srcUrl.isEmpty()) { if (!isNullOrEmpty(srcUrl)) {
try { try {
return clientId = Parser.matchGroup1(clientIdPattern, dl.get(srcUrl, headers).responseBody()); return clientId = Parser.matchGroup1(clientIdPattern, dl.get(srcUrl, headers).responseBody());
} catch (RegexException ignored) { } catch (RegexException ignored) {
@ -86,10 +91,12 @@ public class SoundcloudParsingHelper {
} }
} }
static Calendar parseDate(String textualUploadDate) throws ParsingException { public static Calendar parseDateFrom(String textualUploadDate) throws ParsingException {
Date date; Date date;
try { try {
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(textualUploadDate); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
date = sdf.parse(textualUploadDate);
} catch (ParseException e1) { } catch (ParseException e1) {
try { try {
date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(textualUploadDate); date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(textualUploadDate);
@ -256,13 +263,13 @@ public class SoundcloudParsingHelper {
} }
@Nonnull @Nonnull
static String getUploaderUrl(JsonObject object) { public static String getUploaderUrl(JsonObject object) {
String url = object.getObject("user").getString("permalink_url", EMPTY_STRING); String url = object.getObject("user").getString("permalink_url", EMPTY_STRING);
return replaceHttpWithHttps(url); return replaceHttpWithHttps(url);
} }
@Nonnull @Nonnull
static String getAvatarUrl(JsonObject object) { public static String getAvatarUrl(JsonObject object) {
String url = object.getObject("user").getString("avatar_url", EMPTY_STRING); String url = object.getObject("user").getString("avatar_url", EMPTY_STRING);
return replaceHttpWithHttps(url); return replaceHttpWithHttps(url);
} }

View File

@ -10,18 +10,21 @@ import org.schabi.newpipe.extractor.linkhandler.*;
import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.*;
import org.schabi.newpipe.extractor.services.soundcloud.linkHandler.*;
import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import java.util.List; import java.util.List;
import static java.util.Collections.singletonList; import static java.util.Arrays.asList;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
public class SoundcloudService extends StreamingService { public class SoundcloudService extends StreamingService {
public SoundcloudService(int id) { public SoundcloudService(int id) {
super(id, "SoundCloud", singletonList(AUDIO)); super(id, "SoundCloud", asList(AUDIO, COMMENTS));
} }
@Override @Override
@ -117,13 +120,13 @@ public class SoundcloudService extends StreamingService {
@Override @Override
public ListLinkHandlerFactory getCommentsLHFactory() { public ListLinkHandlerFactory getCommentsLHFactory() {
return null; return SoundcloudCommentsLinkHandlerFactory.getInstance();
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return null; return new SoundcloudCommentsExtractor(this, linkHandler);
} }
} }

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
@ -10,6 +10,7 @@ import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
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;
@ -17,6 +18,7 @@ import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class SoundcloudChannelExtractor extends ChannelExtractor { public class SoundcloudChannelExtractor extends ChannelExtractor {
@ -82,6 +84,21 @@ public class SoundcloudChannelExtractor extends ChannelExtractor {
return user.getString("description", EMPTY_STRING); return user.getString("description", EMPTY_STRING);
} }
@Override
public String getParentChannelName() throws ParsingException {
return "";
}
@Override
public String getParentChannelUrl() throws ParsingException {
return "";
}
@Override
public String getParentChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {
@ -116,7 +133,7 @@ public class SoundcloudChannelExtractor extends ChannelExtractor {
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(final String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(final String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor; import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;

View File

@ -1,10 +1,11 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
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;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
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;
@ -12,6 +13,7 @@ import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> { public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
private StreamInfoItemsCollector collector = null; private StreamInfoItemsCollector collector = null;
@ -35,7 +37,7 @@ public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }

View File

@ -0,0 +1,76 @@
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
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.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemsCollector;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import javax.annotation.Nonnull;
import java.io.IOException;
public class SoundcloudCommentsExtractor extends CommentsExtractor {
private JsonObject json;
public SoundcloudCommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
super(service, uiHandler);
}
@Nonnull
@Override
public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException {
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
collectStreamsFrom(collector, json.getArray("collection"));
return new InfoItemsPage<>(collector, getNextPageUrl());
}
@Override
public String getNextPageUrl() throws IOException, ExtractionException {
return json.getString("next_href");
}
@Override
public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
Downloader dl = NewPipe.getDownloader();
Response rp = dl.get(pageUrl);
try {
json = JsonParser.object().from(rp.responseBody());
} catch (JsonParserException e) {
throw new ParsingException("Could not parse json", e);
}
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
collectStreamsFrom(collector, json.getArray("collection"));
return new InfoItemsPage<>(collector, getNextPageUrl());
}
@Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
Response response = downloader.get(getUrl());
try {
json = JsonParser.object().from(response.responseBody());
} catch (JsonParserException e) {
throw new ParsingException("Could not parse json", e);
}
}
private void collectStreamsFrom(final CommentsInfoItemsCollector collector, final JsonArray entries) throws ParsingException {
final String url = getUrl();
for (Object comment : entries) {
collector.commit(new SoundcloudCommentsInfoItemExtractor((JsonObject) comment, url));
}
}
}

View File

@ -0,0 +1,76 @@
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import javax.annotation.Nullable;
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
private JsonObject json;
private String url;
public SoundcloudCommentsInfoItemExtractor(JsonObject json, String url) {
this.json = json;
this.url = url;
}
@Override
public String getCommentId() throws ParsingException {
return json.getNumber("id").toString();
}
@Override
public String getCommentText() throws ParsingException {
return json.getString("body");
}
@Override
public String getUploaderName() throws ParsingException {
return json.getObject("user").getString("username");
}
@Override
public String getUploaderAvatarUrl() throws ParsingException {
return json.getObject("user").getString("avatar_url");
}
@Override
public String getUploaderUrl() throws ParsingException {
return json.getObject("user").getString("permalink_url");
}
@Override
public String getTextualUploadDate() throws ParsingException {
return json.getString("created_at");
}
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(getTextualUploadDate()));
}
@Override
public int getLikeCount() throws ParsingException {
return -1;
}
@Override
public String getName() throws ParsingException {
return json.getObject("user").getString("permalink");
}
@Override
public String getUrl() throws ParsingException {
return url;
}
@Override
public String getThumbnailUrl() throws ParsingException {
return json.getObject("user").getString("avatar_url");
}
}

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
@ -12,6 +12,7 @@ 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.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
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;
@ -20,6 +21,8 @@ import java.io.IOException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class SoundcloudPlaylistExtractor extends PlaylistExtractor { public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
private static final int streamsPerRequestedPage = 15; private static final int streamsPerRequestedPage = 15;
@ -75,7 +78,7 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
for (StreamInfoItem item : infoItems.getItems()) { for (StreamInfoItem item : infoItems.getItems()) {
artworkUrl = item.getThumbnailUrl(); artworkUrl = item.getThumbnailUrl();
if (artworkUrl != null && !artworkUrl.isEmpty()) break; if (!isNullOrEmpty(artworkUrl)) break;
} }
} catch (Exception ignored) { } catch (Exception ignored) {
} }
@ -113,6 +116,24 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
return playlist.getNumber("track_count", 0).longValue(); return playlist.getNumber("track_count", 0).longValue();
} }
@Nonnull
@Override
public String getSubChannelName() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelUrl() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
@ -159,7 +180,7 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
@ -22,7 +22,7 @@ import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory.ITEMS_PER_PAGE; import static org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudSearchQueryHandlerFactory.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
public class SoundcloudSearchExtractor extends SearchExtractor { public class SoundcloudSearchExtractor extends SearchExtractor {
@ -33,9 +33,15 @@ public class SoundcloudSearchExtractor extends SearchExtractor {
super(service, linkHandler); super(service, linkHandler);
} }
@Nonnull
@Override @Override
public String getSearchSuggestion() { public String getSearchSuggestion() {
return null; return "";
}
@Override
public boolean isCorrectedSearch() {
return false;
} }
@Nonnull @Nonnull

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
@ -15,6 +15,7 @@ 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.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamExtractor;
@ -35,6 +36,7 @@ import java.util.Locale;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class SoundcloudStreamExtractor extends StreamExtractor { public class SoundcloudStreamExtractor extends StreamExtractor {
private JsonObject track; private JsonObject track;
@ -74,7 +76,7 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public DateWrapper getUploadDate() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(track.getString("created_at"))); return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(track.getString("created_at")));
} }
@Nonnull @Nonnull
@ -141,6 +143,24 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
return SoundcloudParsingHelper.getAvatarUrl(track); return SoundcloudParsingHelper.getAvatarUrl(track);
} }
@Nonnull
@Override
public String getSubChannelUrl() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelName() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull @Nonnull
@Override @Override
public String getDashMpdUrl() { public String getDashMpdUrl() {
@ -172,7 +192,7 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
JsonObject t = (JsonObject) transcoding; JsonObject t = (JsonObject) transcoding;
String url = t.getString("url"); String url = t.getString("url");
if (url != null && !url.isEmpty()) { if (!isNullOrEmpty(url)) {
// We can only play the mp3 format, but not handle m3u playlists / streams. // We can only play the mp3 format, but not handle m3u playlists / streams.
// what about Opus? // what about Opus?

View File

@ -1,8 +1,9 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException; 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.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
@ -49,7 +50,7 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
@Override @Override
public DateWrapper getUploadDate() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(getTextualUploadDate())); return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(getTextualUploadDate()));
} }
private String getCreatedAt() { private String getCreatedAt() {

View File

@ -1,8 +1,10 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector; import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudService;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem; import org.schabi.newpipe.extractor.subscription.SubscriptionItem;

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
@ -9,6 +9,7 @@ import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.io.IOException; import java.io.IOException;

View File

@ -1,7 +1,8 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;

View File

@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;

View File

@ -0,0 +1,47 @@
package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import java.io.IOException;
import java.util.List;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.clientId;
public class SoundcloudCommentsLinkHandlerFactory extends ListLinkHandlerFactory {
private static final SoundcloudCommentsLinkHandlerFactory instance = new SoundcloudCommentsLinkHandlerFactory();
public static SoundcloudCommentsLinkHandlerFactory getInstance() {
return instance;
}
@Override
public String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException {
try {
return "https://api-v2.soundcloud.com/tracks/" + id + "/comments" + "?client_id=" + clientId() +
"&threaded=0" + "&filter_replies=1"; // anything but 1 = sort by new
// + "&limit=NUMBER_OF_ITEMS_PER_REQUEST". We let the API control (default = 10)
// + "&offset=OFFSET". We let the API control (default = 0, then we use nextPageUrl)
} catch (ExtractionException | IOException e) {
throw new ParsingException("Could not get comments");
}
}
@Override
public String getId(String url) throws ParsingException {
// delagation to avoid duplicate code, as we need the same id
return SoundcloudStreamLinkHandlerFactory.getInstance().getId(url);
}
@Override
public boolean onAcceptUrl(String url) {
try {
getId(url);
return true;
} catch (ParsingException e) {
return false;
}
}
}

View File

@ -1,7 +1,8 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;

View File

@ -1,9 +1,10 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;

View File

@ -1,7 +1,8 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;

View File

@ -1,5 +1,4 @@
package org.schabi.newpipe.extractor.services.youtube.linkHandler; package org.schabi.newpipe.extractor.services.youtube;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
@ -28,8 +27,8 @@ import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import static org.schabi.newpipe.extractor.NewPipe.getDownloader; import static org.schabi.newpipe.extractor.NewPipe.getDownloader;
import static org.schabi.newpipe.extractor.utils.Utils.HTTP; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.HTTPS; import static org.schabi.newpipe.extractor.utils.Utils.*;
/* /*
* Created by Christian Schabesberger on 02.03.16. * Created by Christian Schabesberger on 02.03.16.
@ -111,9 +110,7 @@ public class YoutubeParsingHelper {
public static long parseDurationString(String input) public static long parseDurationString(String input)
throws ParsingException, NumberFormatException { throws ParsingException, NumberFormatException {
// If time separator : is not detected, try . instead // If time separator : is not detected, try . instead
final String[] splitInput = input.contains(":") final String[] splitInput = input.contains(":")
? input.split(":") ? input.split(":")
: input.split("\\."); : input.split("\\.");
@ -145,10 +142,10 @@ public class YoutubeParsingHelper {
default: default:
throw new ParsingException("Error duration string with unknown format: " + input); throw new ParsingException("Error duration string with unknown format: " + input);
} }
return ((((Long.parseLong(days) * 24) return ((((Long.parseLong(Utils.removeNonDigitCharacters(days)) * 24)
+ Long.parseLong(hours) * 60) + Long.parseLong(Utils.removeNonDigitCharacters(hours)) * 60)
+ Long.parseLong(minutes)) * 60) + Long.parseLong(Utils.removeNonDigitCharacters(minutes))) * 60)
+ Long.parseLong(seconds); + Long.parseLong(Utils.removeNonDigitCharacters(seconds));
} }
public static String getFeedUrlFrom(final String channelIdOrUser) { public static String getFeedUrlFrom(final String channelIdOrUser) {
@ -201,7 +198,7 @@ public class YoutubeParsingHelper {
* @throws ParsingException * @throws ParsingException
*/ */
public static String getClientVersion() throws IOException, ExtractionException { public static String getClientVersion() throws IOException, ExtractionException {
if (clientVersion != null && !clientVersion.isEmpty()) return clientVersion; if (!isNullOrEmpty(clientVersion)) return clientVersion;
if (isHardcodedClientVersionValid()) return clientVersion = HARDCODED_CLIENT_VERSION; if (isHardcodedClientVersionValid()) return clientVersion = HARDCODED_CLIENT_VERSION;
final String url = "https://www.youtube.com/results?search_query=test"; final String url = "https://www.youtube.com/results?search_query=test";
@ -244,7 +241,7 @@ public class YoutubeParsingHelper {
for (String pattern : patterns) { for (String pattern : patterns) {
try { try {
contextClientVersion = Parser.matchGroup1(pattern, html); contextClientVersion = Parser.matchGroup1(pattern, html);
if (contextClientVersion != null && !contextClientVersion.isEmpty()) { if (!isNullOrEmpty(contextClientVersion)) {
return clientVersion = contextClientVersion; return clientVersion = contextClientVersion;
} }
} catch (Exception ignored) { } catch (Exception ignored) {
@ -364,7 +361,7 @@ public class YoutubeParsingHelper {
return "https://www.youtube.com/channel/" + browseId; return "https://www.youtube.com/channel/" + browseId;
} }
if (canonicalBaseUrl != null && !canonicalBaseUrl.isEmpty()) { if (!isNullOrEmpty(canonicalBaseUrl)) {
return "https://www.youtube.com" + canonicalBaseUrl; return "https://www.youtube.com" + canonicalBaseUrl;
} }
@ -388,17 +385,21 @@ public class YoutubeParsingHelper {
* Get the text from a JSON object that has either a simpleText or a runs array. * Get the text from a JSON object that has either a simpleText or a runs array.
* @param textObject JSON object to get the text from * @param textObject JSON object to get the text from
* @param html whether to return HTML, by parsing the navigationEndpoint * @param html whether to return HTML, by parsing the navigationEndpoint
* @return text in the JSON object or an empty string * @return text in the JSON object or {@code null}
*/ */
public static String getTextFromObject(JsonObject textObject, boolean html) throws ParsingException { public static String getTextFromObject(JsonObject textObject, boolean html) throws ParsingException {
if (isNullOrEmpty(textObject)) return null;
if (textObject.has("simpleText")) return textObject.getString("simpleText"); if (textObject.has("simpleText")) return textObject.getString("simpleText");
if (textObject.getArray("runs").isEmpty()) return null;
StringBuilder textBuilder = new StringBuilder(); StringBuilder textBuilder = new StringBuilder();
for (Object textPart : textObject.getArray("runs")) { for (Object textPart : textObject.getArray("runs")) {
String text = ((JsonObject) textPart).getString("text"); String text = ((JsonObject) textPart).getString("text");
if (html && ((JsonObject) textPart).has("navigationEndpoint")) { if (html && ((JsonObject) textPart).has("navigationEndpoint")) {
String url = getUrlFromNavigationEndpoint(((JsonObject) textPart).getObject("navigationEndpoint")); String url = getUrlFromNavigationEndpoint(((JsonObject) textPart).getObject("navigationEndpoint"));
if (url != null && !url.isEmpty()) { if (!isNullOrEmpty(url)) {
textBuilder.append("<a href=\"").append(url).append("\">").append(text).append("</a>"); textBuilder.append("<a href=\"").append(url).append("\">").append(text).append("</a>");
continue; continue;
} }
@ -490,12 +491,12 @@ public class YoutubeParsingHelper {
* @param initialData the object which will be checked if an alert is present * @param initialData the object which will be checked if an alert is present
* @throws ContentNotAvailableException if an alert is detected * @throws ContentNotAvailableException if an alert is detected
*/ */
public static void defaultAlertsCheck(JsonObject initialData) throws ContentNotAvailableException { public static void defaultAlertsCheck(final JsonObject initialData) throws ParsingException {
final JsonArray alerts = initialData.getArray("alerts"); final JsonArray alerts = initialData.getArray("alerts");
if (!alerts.isEmpty()) { if (!isNullOrEmpty(alerts)) {
final JsonObject alertRenderer = alerts.getObject(0).getObject("alertRenderer"); final JsonObject alertRenderer = alerts.getObject(0).getObject("alertRenderer");
final String alertText = alertRenderer.getObject("text").getString("simpleText"); final String alertText = getTextFromObject(alertRenderer.getObject("text"));
final String alertType = alertRenderer.getString("type"); final String alertType = alertRenderer.getString("type", EMPTY_STRING);
if (alertType.equalsIgnoreCase("ERROR")) { if (alertType.equalsIgnoreCase("ERROR")) {
throw new ContentNotAvailableException("Got error: \"" + alertText + "\""); throw new ContentNotAvailableException("Got error: \"" + alertText + "\"");
} }

View File

@ -10,8 +10,8 @@ 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.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
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.services.youtube.linkHandler.YoutubeParsingHelper;
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 org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
@ -19,8 +19,9 @@ import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.*; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.JsonUtils.*; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/* /*
* Created by Christian Schabesberger on 25.07.16. * Created by Christian Schabesberger on 25.07.16.
@ -49,7 +50,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
/** /**
* Some channels have response redirects and the only way to reliably get the id is by saving it. * Some channels have response redirects and the only way to reliably get the id is by saving it.
*<p> * <p>
* "Movies & Shows": * "Movies & Shows":
* <pre> * <pre>
* UCuJcl0Ju-gPDoksRjK1ya-w * UCuJcl0Ju-gPDoksRjK1ya-w
@ -130,7 +131,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
if (!channelId.isEmpty()) { if (!channelId.isEmpty()) {
return channelId; return channelId;
} else if (redirectedChannelId != null && !redirectedChannelId.isEmpty()) { } else if (!isNullOrEmpty(redirectedChannelId)) {
return redirectedChannelId; return redirectedChannelId;
} else { } else {
throw new ParsingException("Could not get channel id"); throw new ParsingException("Could not get channel id");
@ -163,7 +164,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
public String getBannerUrl() throws ParsingException { public String getBannerUrl() throws ParsingException {
try { try {
String url = initialData.getObject("header").getObject("c4TabbedHeaderRenderer").getObject("banner") String url = initialData.getObject("header").getObject("c4TabbedHeaderRenderer").getObject("banner")
.getArray("thumbnails").getObject(0).getString("url"); .getArray("thumbnails").getObject(0).getString("url");
if (url == null || url.contains("s.ytimg.com") || url.contains("default_banner")) { if (url == null || url.contains("s.ytimg.com") || url.contains("default_banner")) {
return null; return null;
@ -212,6 +213,21 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
} }
} }
@Override
public String getParentChannelName() throws ParsingException {
return "";
}
@Override
public String getParentChannelUrl() throws ParsingException {
return "";
}
@Override
public String getParentChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {
@ -229,7 +245,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }
@ -250,7 +266,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
private String getNextPageUrlFrom(JsonArray continuations) { private String getNextPageUrlFrom(JsonArray continuations) {
if (continuations == null || continuations.isEmpty()) { if (isNullOrEmpty(continuations)) {
return ""; return "";
} }
@ -306,10 +322,12 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
throw new ContentNotSupportedException("This channel has no Videos tab"); throw new ContentNotSupportedException("This channel has no Videos tab");
} }
if (getTextFromObject(videoTab.getObject("content").getObject("sectionListRenderer") final String messageRendererText = getTextFromObject(videoTab.getObject("content")
.getArray("contents").getObject(0).getObject("itemSectionRenderer") .getObject("sectionListRenderer").getArray("contents").getObject(0)
.getArray("contents").getObject(0).getObject("messageRenderer") .getObject("itemSectionRenderer").getArray("contents").getObject(0)
.getObject("text")).equals("This channel has no videos.")) { .getObject("messageRenderer").getObject("text"));
if (messageRendererText != null
&& messageRendererText.equals("This channel has no videos.")) {
return null; return null;
} }

View File

@ -7,8 +7,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
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;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.fixThumbnailUrl; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.fixThumbnailUrl;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
/* /*
* Created by Christian Schabesberger on 12.02.17. * Created by Christian Schabesberger on 12.02.17.

View File

@ -27,6 +27,7 @@ import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class YoutubeCommentsExtractor extends CommentsExtractor { public class YoutubeCommentsExtractor extends CommentsExtractor {
@ -37,7 +38,6 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
private String ytClientVersion; private String ytClientVersion;
private String ytClientName; private String ytClientName;
private String title;
private InfoItemsPage<CommentsInfoItem> initPage; private InfoItemsPage<CommentsInfoItem> initPage;
public YoutubeCommentsExtractor(StreamingService service, ListLinkHandler uiHandler) { public YoutubeCommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
@ -92,7 +92,7 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
@Override @Override
public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }
String ajaxResponse = makeAjaxRequest(pageUrl); String ajaxResponse = makeAjaxRequest(pageUrl);
@ -116,7 +116,6 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
//no comments //no comments
return; return;
} }
fetchTitle(contents);
List<Object> comments; List<Object> comments;
try { try {
comments = JsonUtils.getValues(contents, "commentThreadRenderer.comment.commentRenderer"); comments = JsonUtils.getValues(contents, "commentThreadRenderer.comment.commentRenderer");
@ -132,16 +131,6 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
} }
} }
private void fetchTitle(JsonArray contents) {
if (title == null) {
try {
title = getYoutubeText(JsonUtils.getObject(contents.getObject(0), "commentThreadRenderer.commentTargetTitle"));
} catch (Exception e) {
title = "Youtube Comments";
}
}
}
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
final Map<String, List<String>> requestHeaders = new HashMap<>(); final Map<String, List<String>> requestHeaders = new HashMap<>();
@ -155,12 +144,6 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
initPage = getPage(getNextPageUrl(commentsToken)); initPage = getPage(getNextPageUrl(commentsToken));
} }
@Nonnull
@Override
public String getName() throws ParsingException {
return title;
}
private String makeAjaxRequest(String siteUrl) throws IOException, ReCaptchaException { private String makeAjaxRequest(String siteUrl) throws IOException, ReCaptchaException {
Map<String, List<String>> requestHeaders = new HashMap<>(); Map<String, List<String>> requestHeaders = new HashMap<>();

View File

@ -11,6 +11,8 @@ import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtractor { public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
private final JsonObject json; private final JsonObject json;
@ -48,7 +50,7 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
} }
@Override @Override
public String getTextualPublishedTime() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
try { try {
return YoutubeCommentsExtractor.getYoutubeText(JsonUtils.getObject(json, "publishedTimeText")); return YoutubeCommentsExtractor.getYoutubeText(JsonUtils.getObject(json, "publishedTimeText"));
} catch (Exception e) { } catch (Exception e) {
@ -58,8 +60,8 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
@Nullable @Nullable
@Override @Override
public DateWrapper getPublishedTime() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
String textualPublishedTime = getTextualPublishedTime(); String textualPublishedTime = getTextualUploadDate();
if (timeAgoParser != null && textualPublishedTime != null && !textualPublishedTime.isEmpty()) { if (timeAgoParser != null && textualPublishedTime != null && !textualPublishedTime.isEmpty()) {
return timeAgoParser.parse(textualPublishedTime); return timeAgoParser.parse(textualPublishedTime);
} else { } else {
@ -97,7 +99,7 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
} }
@Override @Override
public String getAuthorThumbnail() throws ParsingException { public String getUploaderAvatarUrl() throws ParsingException {
try { try {
JsonArray arr = JsonUtils.getArray(json, "authorThumbnail.thumbnails"); JsonArray arr = JsonUtils.getArray(json, "authorThumbnail.thumbnails");
return JsonUtils.getString(arr.getObject(2), "url"); return JsonUtils.getString(arr.getObject(2), "url");
@ -107,7 +109,7 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
} }
@Override @Override
public String getAuthorName() throws ParsingException { public String getUploaderName() throws ParsingException {
try { try {
return YoutubeCommentsExtractor.getYoutubeText(JsonUtils.getObject(json, "authorText")); return YoutubeCommentsExtractor.getYoutubeText(JsonUtils.getObject(json, "authorText"));
} catch (Exception e) { } catch (Exception e) {
@ -116,7 +118,7 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
} }
@Override @Override
public String getAuthorEndpoint() throws ParsingException { public String getUploaderUrl() throws ParsingException {
try { try {
return "https://youtube.com/channel/" + JsonUtils.getString(json, "authorEndpoint.browseEndpoint.browseId"); return "https://youtube.com/channel/" + JsonUtils.getString(json, "authorEndpoint.browseEndpoint.browseId");
} catch (Exception e) { } catch (Exception e) {

View File

@ -11,7 +11,7 @@ import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.feed.FeedExtractor; import org.schabi.newpipe.extractor.feed.FeedExtractor;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
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;

View File

@ -5,6 +5,7 @@ import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import com.grack.nanojson.JsonWriter; import com.grack.nanojson.JsonWriter;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
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;
@ -16,26 +17,26 @@ import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector; import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.utils.JsonUtils;
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 java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.fixThumbnailUrl; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getUrlFromNavigationEndpoint; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getValidJsonResponseBody;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ALBUMS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ALBUMS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ARTISTS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ARTISTS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_PLAYLISTS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_PLAYLISTS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_SONGS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_SONGS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_VIDEOS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_VIDEOS;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class YoutubeMusicSearchExtractor extends SearchExtractor { public class YoutubeMusicSearchExtractor extends SearchExtractor {
private JsonObject initialData; private JsonObject initialData;
@ -125,15 +126,40 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
return super.getUrl(); return super.getUrl();
} }
@Nonnull
@Override @Override
public String getSearchSuggestion() throws ParsingException { public String getSearchSuggestion() throws ParsingException {
final JsonObject didYouMeanRenderer = initialData.getObject("contents").getObject("sectionListRenderer") final JsonObject itemSectionRenderer = initialData.getObject("contents").getObject("sectionListRenderer")
.getArray("contents").getObject(0).getObject("itemSectionRenderer") .getArray("contents").getObject(0).getObject("itemSectionRenderer");
.getArray("contents").getObject(0).getObject("didYouMeanRenderer"); if (itemSectionRenderer.isEmpty()) {
if (!didYouMeanRenderer.has("correctedQuery")) {
return ""; return "";
} }
return getTextFromObject(didYouMeanRenderer.getObject("correctedQuery"));
final JsonObject didYouMeanRenderer = itemSectionRenderer.getArray("contents")
.getObject(0).getObject("didYouMeanRenderer");
final JsonObject showingResultsForRenderer = itemSectionRenderer.getArray("contents").getObject(0)
.getObject("showingResultsForRenderer");
if (!didYouMeanRenderer.isEmpty()) {
return getTextFromObject(didYouMeanRenderer.getObject("correctedQuery"));
} else if (!showingResultsForRenderer.isEmpty()) {
return JsonUtils.getString(showingResultsForRenderer, "correctedQueryEndpoint.searchEndpoint.query");
} else {
return "";
}
}
@Override
public boolean isCorrectedSearch() {
final JsonObject itemSectionRenderer = initialData.getObject("contents").getObject("sectionListRenderer")
.getArray("contents").getObject(0).getObject("itemSectionRenderer");
if (itemSectionRenderer.isEmpty()) {
return false;
}
final JsonObject showingResultsForRenderer = itemSectionRenderer.getArray("contents").getObject(0)
.getObject("showingResultsForRenderer");
return !showingResultsForRenderer.isEmpty();
} }
@Nonnull @Nonnull
@ -167,7 +193,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
@Override @Override
public InfoItemsPage<InfoItem> getPage(final String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<InfoItem> getPage(final String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }
@ -239,7 +265,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
final String url = getUrlFromNavigationEndpoint(info.getObject("doubleTapCommand")); final String url = getUrlFromNavigationEndpoint(info.getObject("doubleTapCommand"));
if (url != null && !url.isEmpty()) { if (!isNullOrEmpty(url)) {
return url; return url;
} }
throw new ParsingException("Could not get url"); throw new ParsingException("Could not get url");
@ -249,7 +275,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
public String getName() throws ParsingException { public String getName() throws ParsingException {
final String name = getTextFromObject(info.getArray("flexColumns").getObject(0) final String name = getTextFromObject(info.getArray("flexColumns").getObject(0)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!name.isEmpty()) { if (!isNullOrEmpty(name)) {
return name; return name;
} }
throw new ParsingException("Could not get name"); throw new ParsingException("Could not get name");
@ -259,7 +285,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
public long getDuration() throws ParsingException { public long getDuration() throws ParsingException {
final String duration = getTextFromObject(info.getArray("flexColumns").getObject(3) final String duration = getTextFromObject(info.getArray("flexColumns").getObject(3)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!duration.isEmpty()) { if (!isNullOrEmpty(duration)) {
return YoutubeParsingHelper.parseDurationString(duration); return YoutubeParsingHelper.parseDurationString(duration);
} }
throw new ParsingException("Could not get duration"); throw new ParsingException("Could not get duration");
@ -269,7 +295,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
final String name = getTextFromObject(info.getArray("flexColumns").getObject(1) final String name = getTextFromObject(info.getArray("flexColumns").getObject(1)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!name.isEmpty()) { if (!isNullOrEmpty(name)) {
return name; return name;
} }
throw new ParsingException("Could not get uploader name"); throw new ParsingException("Could not get uploader name");
@ -288,13 +314,15 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
return null; return null;
} else { } else {
final JsonObject navigationEndpoint = info.getArray("flexColumns") final JsonObject navigationEndpointHolder = info.getArray("flexColumns")
.getObject(1).getObject("musicResponsiveListItemFlexColumnRenderer") .getObject(1).getObject("musicResponsiveListItemFlexColumnRenderer")
.getObject("text").getArray("runs").getObject(0).getObject("navigationEndpoint"); .getObject("text").getArray("runs").getObject(0);
final String url = getUrlFromNavigationEndpoint(navigationEndpoint); if (!navigationEndpointHolder.has("navigationEndpoint")) return null;
if (url != null && !url.isEmpty()) { final String url = getUrlFromNavigationEndpoint(navigationEndpointHolder.getObject("navigationEndpoint"));
if (!isNullOrEmpty(url)) {
return url; return url;
} }
@ -319,7 +347,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
} }
final String viewCount = getTextFromObject(info.getArray("flexColumns").getObject(2) final String viewCount = getTextFromObject(info.getArray("flexColumns").getObject(2)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!viewCount.isEmpty()) { if (!isNullOrEmpty(viewCount)) {
return Utils.mixedNumberWordToLong(viewCount); return Utils.mixedNumberWordToLong(viewCount);
} }
throw new ParsingException("Could not get view count"); throw new ParsingException("Could not get view count");
@ -359,7 +387,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
public String getName() throws ParsingException { public String getName() throws ParsingException {
final String name = getTextFromObject(info.getArray("flexColumns").getObject(0) final String name = getTextFromObject(info.getArray("flexColumns").getObject(0)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!name.isEmpty()) { if (!isNullOrEmpty(name)) {
return name; return name;
} }
throw new ParsingException("Could not get name"); throw new ParsingException("Could not get name");
@ -368,7 +396,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
final String url = getUrlFromNavigationEndpoint(info.getObject("navigationEndpoint")); final String url = getUrlFromNavigationEndpoint(info.getObject("navigationEndpoint"));
if (url != null && !url.isEmpty()) { if (!isNullOrEmpty(url)) {
return url; return url;
} }
throw new ParsingException("Could not get url"); throw new ParsingException("Could not get url");
@ -378,7 +406,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
public long getSubscriberCount() throws ParsingException { public long getSubscriberCount() throws ParsingException {
final String viewCount = getTextFromObject(info.getArray("flexColumns").getObject(2) final String viewCount = getTextFromObject(info.getArray("flexColumns").getObject(2)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!viewCount.isEmpty()) { if (!isNullOrEmpty(viewCount)) {
return Utils.mixedNumberWordToLong(viewCount); return Utils.mixedNumberWordToLong(viewCount);
} }
throw new ParsingException("Could not get subscriber count"); throw new ParsingException("Could not get subscriber count");
@ -414,7 +442,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
public String getName() throws ParsingException { public String getName() throws ParsingException {
final String name = getTextFromObject(info.getArray("flexColumns").getObject(0) final String name = getTextFromObject(info.getArray("flexColumns").getObject(0)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!name.isEmpty()) { if (!isNullOrEmpty(name)) {
return name; return name;
} }
throw new ParsingException("Could not get name"); throw new ParsingException("Could not get name");
@ -423,7 +451,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
final String url = getUrlFromNavigationEndpoint(info.getObject("doubleTapCommand")); final String url = getUrlFromNavigationEndpoint(info.getObject("doubleTapCommand"));
if (url != null && !url.isEmpty()) { if (!isNullOrEmpty(url)) {
return url; return url;
} }
throw new ParsingException("Could not get url"); throw new ParsingException("Could not get url");
@ -439,7 +467,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
name = getTextFromObject(info.getArray("flexColumns").getObject(1) name = getTextFromObject(info.getArray("flexColumns").getObject(1)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
} }
if (!name.isEmpty()) { if (!isNullOrEmpty(name)) {
return name; return name;
} }
throw new ParsingException("Could not get uploader name"); throw new ParsingException("Could not get uploader name");
@ -452,7 +480,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
} }
final String count = getTextFromObject(info.getArray("flexColumns").getObject(2) final String count = getTextFromObject(info.getArray("flexColumns").getObject(2)
.getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text")); .getObject("musicResponsiveListItemFlexColumnRenderer").getObject("text"));
if (!count.isEmpty()) { if (!isNullOrEmpty(count)) {
if (count.contains("100+")) { if (count.contains("100+")) {
return ITEM_COUNT_MORE_THAN_100; return ITEM_COUNT_MORE_THAN_100;
} else { } else {
@ -468,7 +496,7 @@ public class YoutubeMusicSearchExtractor extends SearchExtractor {
} }
private String getNextPageUrlFrom(final JsonArray continuations) throws ParsingException, IOException, ReCaptchaException { private String getNextPageUrlFrom(final JsonArray continuations) throws ParsingException, IOException, ReCaptchaException {
if (continuations == null || continuations.isEmpty()) { if (isNullOrEmpty(continuations)) {
return ""; return "";
} }

View File

@ -2,28 +2,37 @@ 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.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamType;
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 static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.fixThumbnailUrl; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getJsonResponse; import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getUrlFromNavigationEndpoint; 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.utils.Utils.isNullOrEmpty;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class YoutubePlaylistExtractor extends PlaylistExtractor { public class YoutubePlaylistExtractor extends PlaylistExtractor {
private JsonArray initialAjaxJson;
private JsonObject initialData; private JsonObject initialData;
private JsonObject playlistInfo; private JsonObject playlistInfo;
@ -35,9 +44,9 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
final String url = getUrl() + "&pbj=1"; final String url = getUrl() + "&pbj=1";
final JsonArray ajaxJson = getJsonResponse(url, getExtractorLocalization()); initialAjaxJson = getJsonResponse(url, getExtractorLocalization());
initialData = ajaxJson.getObject(1).getObject("response"); initialData = initialAjaxJson.getObject(1).getObject("response");
YoutubeParsingHelper.defaultAlertsCheck(initialData); YoutubeParsingHelper.defaultAlertsCheck(initialData);
playlistInfo = getPlaylistInfo(); playlistInfo = getPlaylistInfo();
@ -81,7 +90,7 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
String name = getTextFromObject(playlistInfo.getObject("title")); String name = getTextFromObject(playlistInfo.getObject("title"));
if (!name.isEmpty()) return name; if (name != null && !name.isEmpty()) return name;
return initialData.getObject("microformat").getObject("microformatDataRenderer").getString("title"); return initialData.getObject("microformat").getObject("microformatDataRenderer").getString("title");
} }
@ -91,11 +100,11 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
String url = playlistInfo.getObject("thumbnailRenderer").getObject("playlistVideoThumbnailRenderer") String url = playlistInfo.getObject("thumbnailRenderer").getObject("playlistVideoThumbnailRenderer")
.getObject("thumbnail").getArray("thumbnails").getObject(0).getString("url"); .getObject("thumbnail").getArray("thumbnails").getObject(0).getString("url");
if (url == null || url.isEmpty()) { if (isNullOrEmpty(url)) {
url = initialData.getObject("microformat").getObject("microformatDataRenderer").getObject("thumbnail") url = initialData.getObject("microformat").getObject("microformatDataRenderer").getObject("thumbnail")
.getArray("thumbnails").getObject(0).getString("url"); .getArray("thumbnails").getObject(0).getString("url");
if (url == null || url.isEmpty()) throw new ParsingException("Could not get playlist thumbnail"); if (isNullOrEmpty(url)) throw new ParsingException("Could not get playlist thumbnail");
} }
return fixThumbnailUrl(url); return fixThumbnailUrl(url);
@ -149,29 +158,60 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() { public String getSubChannelName() {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); return "";
}
JsonArray videos = initialData.getObject("contents").getObject("twoColumnBrowseResultsRenderer") @Nonnull
@Override
public String getSubChannelUrl() {
return "";
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() {
return "";
}
@Nonnull
@Override
public InfoItemsPage<StreamInfoItem> getInitialPage() {
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
final JsonArray contents = initialData.getObject("contents").getObject("twoColumnBrowseResultsRenderer")
.getArray("tabs").getObject(0).getObject("tabRenderer").getObject("content") .getArray("tabs").getObject(0).getObject("tabRenderer").getObject("content")
.getObject("sectionListRenderer").getArray("contents").getObject(0) .getObject("sectionListRenderer").getArray("contents").getObject(0)
.getObject("itemSectionRenderer").getArray("contents").getObject(0) .getObject("itemSectionRenderer").getArray("contents");
.getObject("playlistVideoListRenderer").getArray("contents");
if (contents.getObject(0).has("playlistSegmentRenderer")) {
for (final Object segment : contents) {
if (((JsonObject) segment).getObject("playlistSegmentRenderer").has("trailer")) {
collectTrailerFrom(collector, ((JsonObject) segment));
} else if (((JsonObject) segment).getObject("playlistSegmentRenderer").has("videoList")) {
collectStreamsFrom(collector, ((JsonObject) segment).getObject("playlistSegmentRenderer")
.getObject("videoList").getObject("playlistVideoListRenderer").getArray("contents"));
}
}
} else if (contents.getObject(0).has("playlistVideoListRenderer")) {
final JsonArray videos = contents.getObject(0)
.getObject("playlistVideoListRenderer").getArray("contents");
collectStreamsFrom(collector, videos);
}
collectStreamsFrom(collector, videos);
return new InfoItemsPage<>(collector, getNextPageUrl()); return new InfoItemsPage<>(collector, getNextPageUrl());
} }
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(final String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(final String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
final JsonArray ajaxJson = getJsonResponse(pageUrl, getExtractorLocalization()); final JsonArray ajaxJson = getJsonResponse(pageUrl, getExtractorLocalization());
JsonObject sectionListContinuation = ajaxJson.getObject(1).getObject("response") final JsonObject sectionListContinuation = ajaxJson.getObject(1).getObject("response")
.getObject("continuationContents").getObject("playlistVideoListContinuation"); .getObject("continuationContents").getObject("playlistVideoListContinuation");
collectStreamsFrom(collector, sectionListContinuation.getArray("contents")); collectStreamsFrom(collector, sectionListContinuation.getArray("contents"));
@ -179,8 +219,8 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
return new InfoItemsPage<>(collector, getNextPageUrlFrom(sectionListContinuation.getArray("continuations"))); return new InfoItemsPage<>(collector, getNextPageUrlFrom(sectionListContinuation.getArray("continuations")));
} }
private String getNextPageUrlFrom(JsonArray continuations) { private String getNextPageUrlFrom(final JsonArray continuations) {
if (continuations == null || continuations.isEmpty()) { if (isNullOrEmpty(continuations)) {
return ""; return "";
} }
@ -191,9 +231,7 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
+ "&itct=" + clickTrackingParams; + "&itct=" + clickTrackingParams;
} }
private void collectStreamsFrom(StreamInfoItemsCollector collector, JsonArray videos) { private void collectStreamsFrom(final StreamInfoItemsCollector collector, final JsonArray videos) {
collector.reset();
final TimeAgoParser timeAgoParser = getTimeAgoParser(); final TimeAgoParser timeAgoParser = getTimeAgoParser();
for (Object video : videos) { for (Object video : videos) {
@ -207,4 +245,76 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
} }
} }
} }
private void collectTrailerFrom(final StreamInfoItemsCollector collector,
final JsonObject segment) {
collector.commit(new StreamInfoItemExtractor() {
@Override
public String getName() throws ParsingException {
return getTextFromObject(segment.getObject("playlistSegmentRenderer")
.getObject("title"));
}
@Override
public String getUrl() throws ParsingException {
return YoutubeStreamLinkHandlerFactory.getInstance()
.fromId(segment.getObject("playlistSegmentRenderer").getObject("trailer")
.getObject("playlistVideoPlayerRenderer").getString("videoId"))
.getUrl();
}
@Override
public String getThumbnailUrl() {
final JsonArray thumbnails = initialAjaxJson.getObject(1).getObject("playerResponse")
.getObject("videoDetails").getObject("thumbnail").getArray("thumbnails");
// the last thumbnail is the one with the highest resolution
final String url = thumbnails.getObject(thumbnails.size() - 1).getString("url");
return fixThumbnailUrl(url);
}
@Override
public StreamType getStreamType() {
return StreamType.VIDEO_STREAM;
}
@Override
public boolean isAd() {
return false;
}
@Override
public long getDuration() throws ParsingException {
return YoutubeParsingHelper.parseDurationString(
getTextFromObject(segment.getObject("playlistSegmentRenderer")
.getObject("segmentAnnotation")).split("")[0]);
}
@Override
public long getViewCount() {
return -1;
}
@Override
public String getUploaderName() throws ParsingException {
return YoutubePlaylistExtractor.this.getUploaderName();
}
@Override
public String getUploaderUrl() throws ParsingException {
return YoutubePlaylistExtractor.this.getUploaderUrl();
}
@Nullable
@Override
public String getTextualUploadDate() {
return null;
}
@Nullable
@Override
public DateWrapper getUploadDate() {
return null;
}
});
}
} }

View File

@ -7,8 +7,8 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.fixThumbnailUrl; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.fixThumbnailUrl;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
public class YoutubePlaylistInfoItemExtractor implements PlaylistInfoItemExtractor { public class YoutubePlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
private JsonObject playlistInfoItem; private JsonObject playlistInfoItem;

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.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
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;
@ -12,13 +11,14 @@ import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector; import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import java.io.IOException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getJsonResponse; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonResponse;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/* /*
* Created by Christian Schabesberger on 22.07.2018 * Created by Christian Schabesberger on 22.07.2018
@ -62,17 +62,35 @@ public class YoutubeSearchExtractor extends SearchExtractor {
return super.getUrl() + "&gl=" + getExtractorContentCountry().getCountryCode(); return super.getUrl() + "&gl=" + getExtractorContentCountry().getCountryCode();
} }
@Nonnull
@Override @Override
public String getSearchSuggestion() throws ParsingException { public String getSearchSuggestion() throws ParsingException {
final JsonObject itemSectionRenderer = initialData.getObject("contents")
.getObject("twoColumnSearchResultsRenderer").getObject("primaryContents")
.getObject("sectionListRenderer").getArray("contents").getObject(0)
.getObject("itemSectionRenderer");
final JsonObject didYouMeanRenderer = itemSectionRenderer.getArray("contents").getObject(0)
.getObject("didYouMeanRenderer");
final JsonObject showingResultsForRenderer = itemSectionRenderer.getArray("contents").getObject(0)
.getObject("showingResultsForRenderer");
if (!didYouMeanRenderer.isEmpty()) {
return JsonUtils.getString(didYouMeanRenderer, "correctedQueryEndpoint.searchEndpoint.query");
} else if (showingResultsForRenderer != null) {
return getTextFromObject(showingResultsForRenderer.getObject("correctedQuery"));
} else {
return "";
}
}
@Override
public boolean isCorrectedSearch() {
final JsonObject showingResultsForRenderer = initialData.getObject("contents") final JsonObject showingResultsForRenderer = initialData.getObject("contents")
.getObject("twoColumnSearchResultsRenderer").getObject("primaryContents") .getObject("twoColumnSearchResultsRenderer").getObject("primaryContents")
.getObject("sectionListRenderer").getArray("contents").getObject(0) .getObject("sectionListRenderer").getArray("contents").getObject(0)
.getObject("itemSectionRenderer").getArray("contents").getObject(0) .getObject("itemSectionRenderer").getArray("contents").getObject(0)
.getObject("showingResultsForRenderer"); .getObject("showingResultsForRenderer");
if (!showingResultsForRenderer.has("correctedQuery")) { return !showingResultsForRenderer.isEmpty();
return "";
}
return getTextFromObject(showingResultsForRenderer.getObject("correctedQuery"));
} }
@Nonnull @Nonnull
@ -99,7 +117,7 @@ public class YoutubeSearchExtractor extends SearchExtractor {
@Override @Override
public InfoItemsPage<InfoItem> getPage(final String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<InfoItem> getPage(final String pageUrl) throws IOException, ExtractionException {
if (pageUrl == null || pageUrl.isEmpty()) { if (isNullOrEmpty(pageUrl)) {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }
@ -133,7 +151,7 @@ public class YoutubeSearchExtractor extends SearchExtractor {
} }
private String getNextPageUrlFrom(final JsonArray continuations) throws ParsingException { private String getNextPageUrlFrom(final JsonArray continuations) throws ParsingException {
if (continuations == null || continuations.isEmpty()) { if (isNullOrEmpty(continuations)) {
return ""; return "";
} }

View File

@ -3,6 +3,7 @@ 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 com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function; import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
@ -21,7 +22,7 @@ import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager; import org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager;
import org.schabi.newpipe.extractor.services.youtube.ItagItem; import org.schabi.newpipe.extractor.services.youtube.ItagItem;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.Frameset; import org.schabi.newpipe.extractor.stream.Frameset;
@ -35,8 +36,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -50,11 +49,12 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.fixThumbnailUrl; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getJsonResponse; import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getUrlFromNavigationEndpoint; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/* /*
* Created by Christian Schabesberger on 06.08.15. * Created by Christian Schabesberger on 06.08.15.
@ -117,10 +117,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
assertPageFetched(); assertPageFetched();
String title = getTextFromObject(getVideoPrimaryInfoRenderer().getObject("title")); String title = getTextFromObject(getVideoPrimaryInfoRenderer().getObject("title"));
if (title.isEmpty()) { if (isNullOrEmpty(title)) {
title = playerResponse.getObject("videoDetails").getString("title"); title = playerResponse.getObject("videoDetails").getString("title");
if (title == null || title.isEmpty()) throw new ParsingException("Could not get name"); if (isNullOrEmpty(title)) throw new ParsingException("Could not get name");
} }
return title; return title;
@ -168,7 +168,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
public DateWrapper getUploadDate() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
final String textualUploadDate = getTextualUploadDate(); final String textualUploadDate = getTextualUploadDate();
if (textualUploadDate == null || textualUploadDate.isEmpty()) { if (isNullOrEmpty(textualUploadDate)) {
return null; return null;
} }
@ -197,7 +197,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
assertPageFetched(); assertPageFetched();
// description with more info on links // description with more info on links
String description = getTextFromObject(getVideoSecondaryInfoRenderer().getObject("description"), true); String description = getTextFromObject(getVideoSecondaryInfoRenderer().getObject("description"), true);
if (!description.isEmpty()) return new Description(description, Description.HTML); if (description != null && !description.isEmpty()) return new Description(description, Description.HTML);
// raw non-html description // raw non-html description
return new Description(playerResponse.getObject("videoDetails").getString("shortDescription"), Description.PLAIN_TEXT); return new Description(playerResponse.getObject("videoDetails").getString("shortDescription"), Description.PLAIN_TEXT);
@ -205,7 +205,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
@Override @Override
public int getAgeLimit() { public int getAgeLimit() {
if (initialData == null || initialData.isEmpty()) throw new IllegalStateException("initialData is not parsed yet"); if (isNullOrEmpty(initialData)) throw new IllegalStateException("initialData is not parsed yet");
return ageLimit; return ageLimit;
} }
@ -249,10 +249,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
String views = getTextFromObject(getVideoPrimaryInfoRenderer().getObject("viewCount") String views = getTextFromObject(getVideoPrimaryInfoRenderer().getObject("viewCount")
.getObject("videoViewCountRenderer").getObject("viewCount")); .getObject("videoViewCountRenderer").getObject("viewCount"));
if (views.isEmpty()) { if (isNullOrEmpty(views)) {
views = playerResponse.getObject("videoDetails").getString("viewCount"); views = playerResponse.getObject("videoDetails").getString("viewCount");
if (views == null || views.isEmpty()) throw new ParsingException("Could not get view count"); if (isNullOrEmpty(views)) throw new ParsingException("Could not get view count");
} }
if (views.toLowerCase().contains("no views")) return 0; if (views.toLowerCase().contains("no views")) return 0;
@ -330,10 +330,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
String uploaderName = getTextFromObject(getVideoSecondaryInfoRenderer().getObject("owner") String uploaderName = getTextFromObject(getVideoSecondaryInfoRenderer().getObject("owner")
.getObject("videoOwnerRenderer").getObject("title")); .getObject("videoOwnerRenderer").getObject("title"));
if (uploaderName.isEmpty()) { if (isNullOrEmpty(uploaderName)) {
uploaderName = playerResponse.getObject("videoDetails").getString("author"); uploaderName = playerResponse.getObject("videoDetails").getString("author");
if (uploaderName == null || uploaderName.isEmpty()) throw new ParsingException("Could not get uploader name"); if (isNullOrEmpty(uploaderName)) throw new ParsingException("Could not get uploader name");
} }
return uploaderName; return uploaderName;
@ -353,13 +353,33 @@ public class YoutubeStreamExtractor extends StreamExtractor {
} }
} }
@Nonnull
@Override
public String getSubChannelUrl() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelName() throws ParsingException {
return "";
}
@Nonnull
@Override
public String getSubChannelAvatarUrl() throws ParsingException {
return "";
}
@Nonnull @Nonnull
@Override @Override
public String getDashMpdUrl() throws ParsingException { public String getDashMpdUrl() throws ParsingException {
assertPageFetched(); assertPageFetched();
try { try {
String dashManifestUrl; String dashManifestUrl;
if (videoInfoPage.containsKey("dashmpd")) { if (playerResponse.getObject("streamingData").isString("dashManifestUrl")) {
return playerResponse.getObject("streamingData").getString("dashManifestUrl");
} else if (videoInfoPage.containsKey("dashmpd")) {
dashManifestUrl = videoInfoPage.get("dashmpd"); dashManifestUrl = videoInfoPage.get("dashmpd");
} else if (playerArgs != null && playerArgs.isString("dashmpd")) { } else if (playerArgs != null && playerArgs.isString("dashmpd")) {
dashManifestUrl = playerArgs.getString("dashmpd", EMPTY_STRING); dashManifestUrl = playerArgs.getString("dashmpd", EMPTY_STRING);
@ -910,8 +930,12 @@ public class YoutubeStreamExtractor extends StreamExtractor {
streamUrl = formatData.getString("url"); streamUrl = formatData.getString("url");
} else { } else {
// this url has an encrypted signature // this url has an encrypted signature
Map<String, String> cipher = Parser.compatParseMap(formatData.getString("cipher")); final String cipherString = formatData.has("cipher")
streamUrl = cipher.get("url") + "&" + cipher.get("sp") + "=" + decryptSignature(cipher.get("s"), decryptionCode); ? formatData.getString("cipher")
: formatData.getString("signatureCipher");
final Map<String, String> cipher = Parser.compatParseMap(cipherString);
streamUrl = cipher.get("url") + "&" + cipher.get("sp") + "="
+ decryptSignature(cipher.get("s"), decryptionCode);
} }
urlAndItags.put(streamUrl, itagItem); urlAndItags.put(streamUrl, itagItem);

View File

@ -5,7 +5,7 @@ import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException; 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.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
@ -16,8 +16,9 @@ import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.*; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*;
import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING; import static org.schabi.newpipe.extractor.utils.JsonUtils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/* /*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
@ -93,7 +94,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
String name = getTextFromObject(videoInfo.getObject("title")); String name = getTextFromObject(videoInfo.getObject("title"));
if (!name.isEmpty()) return name; if (!isNullOrEmpty(name)) return name;
throw new ParsingException("Could not get name"); throw new ParsingException("Could not get name");
} }
@ -105,7 +106,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
String duration = getTextFromObject(videoInfo.getObject("lengthText")); String duration = getTextFromObject(videoInfo.getObject("lengthText"));
if (duration.isEmpty()) { if (isNullOrEmpty(duration)) {
for (Object thumbnailOverlay : videoInfo.getArray("thumbnailOverlays")) { for (Object thumbnailOverlay : videoInfo.getArray("thumbnailOverlays")) {
if (((JsonObject) thumbnailOverlay).has("thumbnailOverlayTimeStatusRenderer")) { if (((JsonObject) thumbnailOverlay).has("thumbnailOverlayTimeStatusRenderer")) {
duration = getTextFromObject(((JsonObject) thumbnailOverlay) duration = getTextFromObject(((JsonObject) thumbnailOverlay)
@ -113,7 +114,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
} }
} }
if (duration.isEmpty()) throw new ParsingException("Could not get duration"); if (isNullOrEmpty(duration)) throw new ParsingException("Could not get duration");
} }
return YoutubeParsingHelper.parseDurationString(duration); return YoutubeParsingHelper.parseDurationString(duration);
@ -123,13 +124,13 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
String name = getTextFromObject(videoInfo.getObject("longBylineText")); String name = getTextFromObject(videoInfo.getObject("longBylineText"));
if (name.isEmpty()) { if (isNullOrEmpty(name)) {
name = getTextFromObject(videoInfo.getObject("ownerText")); name = getTextFromObject(videoInfo.getObject("ownerText"));
if (name.isEmpty()) { if (isNullOrEmpty(name)) {
name = getTextFromObject(videoInfo.getObject("shortBylineText")); name = getTextFromObject(videoInfo.getObject("shortBylineText"));
if (name.isEmpty()) throw new ParsingException("Could not get uploader name"); if (isNullOrEmpty(name)) throw new ParsingException("Could not get uploader name");
} }
} }
@ -141,15 +142,15 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
String url = getUrlFromNavigationEndpoint(videoInfo.getObject("longBylineText") String url = getUrlFromNavigationEndpoint(videoInfo.getObject("longBylineText")
.getArray("runs").getObject(0).getObject("navigationEndpoint")); .getArray("runs").getObject(0).getObject("navigationEndpoint"));
if (url == null || url.isEmpty()) { if (isNullOrEmpty(url)) {
url = getUrlFromNavigationEndpoint(videoInfo.getObject("ownerText") url = getUrlFromNavigationEndpoint(videoInfo.getObject("ownerText")
.getArray("runs").getObject(0).getObject("navigationEndpoint")); .getArray("runs").getObject(0).getObject("navigationEndpoint"));
if (url == null || url.isEmpty()) { if (isNullOrEmpty(url)) {
url = getUrlFromNavigationEndpoint(videoInfo.getObject("shortBylineText") url = getUrlFromNavigationEndpoint(videoInfo.getObject("shortBylineText")
.getArray("runs").getObject(0).getObject("navigationEndpoint")); .getArray("runs").getObject(0).getObject("navigationEndpoint"));
if (url == null || url.isEmpty()) throw new ParsingException("Could not get uploader url"); if (isNullOrEmpty(url)) throw new ParsingException("Could not get uploader url");
} }
} }
@ -169,7 +170,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
} }
final String publishedTimeText = getTextFromObject(videoInfo.getObject("publishedTimeText")); final String publishedTimeText = getTextFromObject(videoInfo.getObject("publishedTimeText"));
if (!publishedTimeText.isEmpty()) return publishedTimeText; if (publishedTimeText != null && !publishedTimeText.isEmpty()) return publishedTimeText;
return null; return null;
} }
@ -186,7 +187,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
} }
final String textualUploadDate = getTextualUploadDate(); final String textualUploadDate = getTextualUploadDate();
if (timeAgoParser != null && textualUploadDate != null && !textualUploadDate.isEmpty()) { if (timeAgoParser != null && !isNullOrEmpty(textualUploadDate)) {
try { try {
return timeAgoParser.parse(textualUploadDate); return timeAgoParser.parse(textualUploadDate);
} catch (ParsingException e) { } catch (ParsingException e) {

View File

@ -22,6 +22,7 @@ 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.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@ -32,11 +33,13 @@ import org.schabi.newpipe.extractor.localization.TimeAgoParser;
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 java.io.IOException; import java.io.IOException;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getJsonResponse; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.getTextFromObject;
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.utils.Utils.isNullOrEmpty;
public class YoutubeTrendingExtractor extends KioskExtractor<StreamInfoItem> { public class YoutubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
private JsonObject initialData; private JsonObject initialData;
@ -71,7 +74,7 @@ public class YoutubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
String name = getTextFromObject(initialData.getObject("header").getObject("feedTabbedHeaderRenderer").getObject("title")); String name = getTextFromObject(initialData.getObject("header").getObject("feedTabbedHeaderRenderer").getObject("title"));
if (!name.isEmpty()) { if (!isNullOrEmpty(name)) {
return name; return name;
} }
throw new ParsingException("Could not get Trending name"); throw new ParsingException("Could not get Trending name");

View File

@ -2,6 +2,7 @@ package org.schabi.newpipe.extractor.services.youtube.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import java.net.URL; import java.net.URL;

View File

@ -1,6 +1,6 @@
package org.schabi.newpipe.extractor.services.youtube.linkHandler; package org.schabi.newpipe.extractor.services.youtube.linkHandler;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.BASE_YOUTUBE_INTENT_URL; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.BASE_YOUTUBE_INTENT_URL;
import org.schabi.newpipe.extractor.exceptions.FoundAdException; import org.schabi.newpipe.extractor.exceptions.FoundAdException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;

View File

@ -3,6 +3,7 @@ package org.schabi.newpipe.extractor.services.youtube.linkHandler;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import java.net.URL; import java.net.URL;

View File

@ -1,11 +1,12 @@
package org.schabi.newpipe.extractor.services.youtube.linkHandler; package org.schabi.newpipe.extractor.services.youtube.linkHandler;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.BASE_YOUTUBE_INTENT_URL; import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.BASE_YOUTUBE_INTENT_URL;
import org.schabi.newpipe.extractor.exceptions.FoundAdException; import org.schabi.newpipe.extractor.exceptions.FoundAdException;
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.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@ -13,6 +14,8 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import javax.annotation.Nullable;
/* /*
* Created by Christian Schabesberger on 02.02.16. * Created by Christian Schabesberger on 02.02.16.
* *
@ -44,12 +47,16 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
return instance; return instance;
} }
private static String assertIsID(String id) throws ParsingException { private static boolean isId(@Nullable String id) {
if (id == null || !id.matches("[a-zA-Z0-9_-]{11}")) { return id != null && id.matches("[a-zA-Z0-9_-]{11}");
}
private static String assertIsId(@Nullable String id) throws ParsingException {
if (isId(id)) {
return id;
} else {
throw new ParsingException("The given string is not a Youtube-Video-ID"); throw new ParsingException("The given string is not a Youtube-Video-ID");
} }
return id;
} }
@Override @Override
@ -75,9 +82,14 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
if (scheme != null && (scheme.equals("vnd.youtube") || scheme.equals("vnd.youtube.launch"))) { if (scheme != null && (scheme.equals("vnd.youtube") || scheme.equals("vnd.youtube.launch"))) {
String schemeSpecificPart = uri.getSchemeSpecificPart(); String schemeSpecificPart = uri.getSchemeSpecificPart();
if (schemeSpecificPart.startsWith("//")) { if (schemeSpecificPart.startsWith("//")) {
final String possiblyId = schemeSpecificPart.substring(2);
if (isId(possiblyId)) {
return possiblyId;
}
urlString = "https:" + schemeSpecificPart; urlString = "https:" + schemeSpecificPart;
} else { } else {
return assertIsID(schemeSpecificPart); return assertIsId(schemeSpecificPart);
} }
} }
} catch (URISyntaxException ignored) { } catch (URISyntaxException ignored) {
@ -118,7 +130,7 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
if (path.startsWith("embed/")) { if (path.startsWith("embed/")) {
String id = path.split("/")[1]; String id = path.split("/")[1];
return assertIsID(id); return assertIsId(id);
} }
break; break;
@ -139,38 +151,38 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
} }
String viewQueryValue = Utils.getQueryValue(decodedURL, "v"); String viewQueryValue = Utils.getQueryValue(decodedURL, "v");
return assertIsID(viewQueryValue); return assertIsId(viewQueryValue);
} }
if (path.startsWith("embed/")) { if (path.startsWith("embed/")) {
String id = path.split("/")[1]; String id = path.split("/")[1];
return assertIsID(id); return assertIsId(id);
} }
String viewQueryValue = Utils.getQueryValue(url, "v"); String viewQueryValue = Utils.getQueryValue(url, "v");
return assertIsID(viewQueryValue); return assertIsId(viewQueryValue);
} }
case "YOUTU.BE": { case "YOUTU.BE": {
String viewQueryValue = Utils.getQueryValue(url, "v"); String viewQueryValue = Utils.getQueryValue(url, "v");
if (viewQueryValue != null) { if (viewQueryValue != null) {
return assertIsID(viewQueryValue); return assertIsId(viewQueryValue);
} }
return assertIsID(path); return assertIsId(path);
} }
case "HOOKTUBE.COM": { case "HOOKTUBE.COM": {
if (path.startsWith("v/")) { if (path.startsWith("v/")) {
String id = path.substring("v/".length()); String id = path.substring("v/".length());
return assertIsID(id); return assertIsId(id);
} }
if (path.startsWith("watch/")) { if (path.startsWith("watch/")) {
String id = path.substring("watch/".length()); String id = path.substring("watch/".length());
return assertIsID(id); return assertIsId(id);
} }
// there is no break-statement here on purpose so the next code-block gets also run for hooktube // there is no break-statement here on purpose so the next code-block gets also run for hooktube
} }
@ -193,21 +205,21 @@ public class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
if (path.equals("watch")) { if (path.equals("watch")) {
String viewQueryValue = Utils.getQueryValue(url, "v"); String viewQueryValue = Utils.getQueryValue(url, "v");
if (viewQueryValue != null) { if (viewQueryValue != null) {
return assertIsID(viewQueryValue); return assertIsId(viewQueryValue);
} }
} }
if (path.startsWith("embed/")) { if (path.startsWith("embed/")) {
String id = path.substring("embed/".length()); String id = path.substring("embed/".length());
return assertIsID(id); return assertIsId(id);
} }
String viewQueryValue = Utils.getQueryValue(url, "v"); String viewQueryValue = Utils.getQueryValue(url, "v");
if (viewQueryValue != null) { if (viewQueryValue != null) {
return assertIsID(viewQueryValue); return assertIsId(viewQueryValue);
} }
return assertIsID(path); return assertIsId(path);
} }
} }

View File

@ -21,6 +21,7 @@ package org.schabi.newpipe.extractor.services.youtube.linkHandler;
*/ */
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import java.net.MalformedURLException; import java.net.MalformedURLException;

View File

@ -5,6 +5,8 @@ import org.schabi.newpipe.extractor.MediaFormat;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/** /**
* Creates a stream object from url, format and optional torrent url * Creates a stream object from url, format and optional torrent url
*/ */
@ -61,7 +63,7 @@ public abstract class Stream implements Serializable {
* Check if the list already contains one stream with equals stats * Check if the list already contains one stream with equals stats
*/ */
public static boolean containSimilarStream(Stream stream, List<? extends Stream> streamList) { public static boolean containSimilarStream(Stream stream, List<? extends Stream> streamList) {
if (stream == null || streamList == null) return false; if (isNullOrEmpty(streamList)) return false;
for (Stream cmpStream : streamList) { for (Stream cmpStream : streamList) {
if (stream.equalStats(cmpStream)) return true; if (stream.equalStats(cmpStream)) return true;
} }

View File

@ -23,6 +23,7 @@ package org.schabi.newpipe.extractor.stream;
import org.schabi.newpipe.extractor.Extractor; import org.schabi.newpipe.extractor.Extractor;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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;
@ -148,7 +149,7 @@ public abstract class StreamExtractor extends Extractor {
/** /**
* The Url to the page of the creator/uploader of the stream. This must not be a homepage, * The Url to the page of the creator/uploader of the stream. This must not be a homepage,
* but the page offered by the service the extractor handles. This url will be handled by the * but the page offered by the service the extractor handles. This url will be handled by the
* <a href="https://teamnewpipe.github.io/documentation/03_Implement_a_service/#channel">ChannelExtractor</a>, * {@link ChannelExtractor},
* so be sure to implement that one before you return a value here, otherwise NewPipe will crash if one selects * so be sure to implement that one before you return a value here, otherwise NewPipe will crash if one selects
* this url. * this url.
* *
@ -178,6 +179,39 @@ public abstract class StreamExtractor extends Extractor {
@Nonnull @Nonnull
public abstract String getUploaderAvatarUrl() throws ParsingException; public abstract String getUploaderAvatarUrl() throws ParsingException;
/**
* The Url to the page of the sub-channel of the stream. This must not be a homepage,
* but the page offered by the service the extractor handles. This url will be handled by the
* {@link ChannelExtractor},
* so be sure to implement that one before you return a value here, otherwise NewPipe will crash if one selects
* this url.
*
* @return the url to the page of the sub-channel of the stream or an empty String
* @throws ParsingException
*/
@Nonnull
public abstract String getSubChannelUrl() throws ParsingException;
/**
* The name of the sub-channel of the stream.
* If the name is not available you can simply return an empty string.
*
* @return the name of the sub-channel of the stream or an empty String
* @throws ParsingException
*/
@Nonnull
public abstract String getSubChannelName() throws ParsingException;
/**
* The url to the image file/profile picture/avatar of the sub-channel of the stream.
* If the url is not available you can return an empty String.
*
* @return The url of the image file of the sub-channel or an empty String
* @throws ParsingException
*/
@Nonnull
public abstract String getSubChannelAvatarUrl() throws ParsingException;
/** /**
* Get the dash mpd url. If you don't know what a dash MPD is you can read about it * Get the dash mpd url. If you don't know what a dash MPD is you can read about it
* <a href="https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html">here</a>. * <a href="https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html">here</a>.

View File

@ -16,6 +16,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
/* /*
* Created by Christian Schabesberger on 26.08.15. * Created by Christian Schabesberger on 26.08.15.
* *
@ -103,7 +105,7 @@ public class StreamInfo extends Info {
String name = extractor.getName(); String name = extractor.getName();
int ageLimit = extractor.getAgeLimit(); int ageLimit = extractor.getAgeLimit();
if ((streamType == StreamType.NONE) || (url == null || url.isEmpty()) || (id == null || id.isEmpty()) if ((streamType == StreamType.NONE) || isNullOrEmpty(url) || (isNullOrEmpty(id))
|| (name == null /* streamInfo.title can be empty of course */) || (ageLimit == -1)) { || (name == null /* streamInfo.title can be empty of course */) || (ageLimit == -1)) {
throw new ExtractionException("Some important stream information was not given."); throw new ExtractionException("Some important stream information was not given.");
} }
@ -159,7 +161,7 @@ public class StreamInfo extends Info {
streamInfo.setAudioStreams(new ArrayList<AudioStream>()); streamInfo.setAudioStreams(new ArrayList<AudioStream>());
Exception dashMpdError = null; Exception dashMpdError = null;
if (streamInfo.getDashMpdUrl() != null && !streamInfo.getDashMpdUrl().isEmpty()) { if (!isNullOrEmpty(streamInfo.getDashMpdUrl())) {
try { try {
DashMpdParser.ParserResult result = DashMpdParser.getStreams(streamInfo); DashMpdParser.ParserResult result = DashMpdParser.getStreams(streamInfo);
streamInfo.getVideoOnlyStreams().addAll(result.getVideoOnlyStreams()); streamInfo.getVideoOnlyStreams().addAll(result.getVideoOnlyStreams());
@ -223,6 +225,28 @@ public class StreamInfo extends Info {
} catch (Exception e) { } catch (Exception e) {
streamInfo.addError(e); streamInfo.addError(e);
} }
try {
streamInfo.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
} catch (Exception e) {
streamInfo.addError(e);
}
try {
streamInfo.setSubChannelName(extractor.getSubChannelName());
} catch (Exception e) {
streamInfo.addError(e);
}
try {
streamInfo.setSubChannelUrl(extractor.getSubChannelUrl());
} catch (Exception e) {
streamInfo.addError(e);
}
try {
streamInfo.setSubChannelAvatarUrl(extractor.getSubChannelAvatarUrl());
} catch (Exception e) {
streamInfo.addError(e);
}
try { try {
streamInfo.setDescription(extractor.getDescription()); streamInfo.setDescription(extractor.getDescription());
} catch (Exception e) { } catch (Exception e) {
@ -243,11 +267,6 @@ public class StreamInfo extends Info {
} catch (Exception e) { } catch (Exception e) {
streamInfo.addError(e); streamInfo.addError(e);
} }
try {
streamInfo.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
} catch (Exception e) {
streamInfo.addError(e);
}
try { try {
streamInfo.setStartPosition(extractor.getTimeStamp()); streamInfo.setStartPosition(extractor.getTimeStamp());
} catch (Exception e) { } catch (Exception e) {
@ -332,6 +351,10 @@ public class StreamInfo extends Info {
private String uploaderUrl = ""; private String uploaderUrl = "";
private String uploaderAvatarUrl = ""; private String uploaderAvatarUrl = "";
private String subChannelName = "";
private String subChannelUrl = "";
private String subChannelAvatarUrl = "";
private List<VideoStream> videoStreams = new ArrayList<>(); private List<VideoStream> videoStreams = new ArrayList<>();
private List<AudioStream> audioStreams = new ArrayList<>(); private List<AudioStream> audioStreams = new ArrayList<>();
private List<VideoStream> videoOnlyStreams = new ArrayList<>(); private List<VideoStream> videoOnlyStreams = new ArrayList<>();
@ -486,6 +509,30 @@ public class StreamInfo extends Info {
this.uploaderAvatarUrl = uploaderAvatarUrl; this.uploaderAvatarUrl = uploaderAvatarUrl;
} }
public String getSubChannelName() {
return subChannelName;
}
public void setSubChannelName(String subChannelName) {
this.subChannelName = subChannelName;
}
public String getSubChannelUrl() {
return subChannelUrl;
}
public void setSubChannelUrl(String subChannelUrl) {
this.subChannelUrl = subChannelUrl;
}
public String getSubChannelAvatarUrl() {
return subChannelAvatarUrl;
}
public void setSubChannelAvatarUrl(String subChannelAvatarUrl) {
this.subChannelAvatarUrl = subChannelAvatarUrl;
}
public List<VideoStream> getVideoStreams() { public List<VideoStream> getVideoStreams() {
return videoStreams; return videoStreams;
} }

View File

@ -6,7 +6,9 @@ import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
public class Utils { public class Utils {
@ -48,7 +50,8 @@ public class Utils {
String multiplier = ""; String multiplier = "";
try { try {
multiplier = Parser.matchGroup("[\\d]+([\\.,][\\d]+)?([KMBkmb])+", numberWord, 2); multiplier = Parser.matchGroup("[\\d]+([\\.,][\\d]+)?([KMBkmb])+", numberWord, 2);
} catch(ParsingException ignored) {} } catch (ParsingException ignored) {
}
double count = Double.parseDouble(Parser.matchGroup1("([\\d]+([\\.,][\\d]+)?)", numberWord) double count = Double.parseDouble(Parser.matchGroup1("([\\d]+([\\.,][\\d]+)?)", numberWord)
.replace(",", ".")); .replace(",", "."));
switch (multiplier.toUpperCase()) { switch (multiplier.toUpperCase()) {
@ -70,7 +73,7 @@ public class Utils {
* @param url the url to be tested * @param url the url to be tested
*/ */
public static void checkUrl(String pattern, String url) throws ParsingException { public static void checkUrl(String pattern, String url) throws ParsingException {
if (url == null || url.isEmpty()) { if (isNullOrEmpty(url)) {
throw new IllegalArgumentException("Url can't be null or empty"); throw new IllegalArgumentException("Url can't be null or empty");
} }
@ -186,4 +189,37 @@ public class Utils {
} }
return uri.getProtocol() + "://" + uri.getAuthority(); return uri.getProtocol() + "://" + uri.getAuthority();
} }
}
public static boolean isNullOrEmpty(final String str) {
return str == null || str.isEmpty();
}
// can be used for JsonArrays
public static boolean isNullOrEmpty(final Collection<?> collection) {
return collection == null || collection.isEmpty();
}
// can be used for JsonObjects
public static boolean isNullOrEmpty(final Map map) {
return map == null || map.isEmpty();
}
public static boolean isWhitespace(final int c){
return c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r';
}
public static boolean isBlank(final String string) {
if (string == null || string.isEmpty()) {
return true;
}
final int length = string.length();
for (int i = 0; i < length; i++) {
if (!isWhitespace(string.codePointAt(i))) {
return false;
}
}
return true;
}
}

View File

@ -4,4 +4,5 @@ package org.schabi.newpipe.extractor.services;
public interface BaseSearchExtractorTest extends BaseListExtractorTest { public interface BaseSearchExtractorTest extends BaseListExtractorTest {
void testSearchString() throws Exception; void testSearchString() throws Exception;
void testSearchSuggestion() throws Exception; void testSearchSuggestion() throws Exception;
void testSearchCorrected() throws Exception;
} }

View File

@ -8,6 +8,7 @@ import javax.annotation.Nullable;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmpty; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmpty;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public abstract class DefaultSearchExtractorTest extends DefaultListExtractorTest<SearchExtractor> public abstract class DefaultSearchExtractorTest extends DefaultListExtractorTest<SearchExtractor>
implements BaseSearchExtractorTest { implements BaseSearchExtractorTest {
@ -15,6 +16,10 @@ public abstract class DefaultSearchExtractorTest extends DefaultListExtractorTes
public abstract String expectedSearchString(); public abstract String expectedSearchString();
@Nullable public abstract String expectedSearchSuggestion(); @Nullable public abstract String expectedSearchSuggestion();
public boolean isCorrectedSearch() {
return false;
}
@Test @Test
@Override @Override
public void testSearchString() throws Exception { public void testSearchString() throws Exception {
@ -25,10 +30,15 @@ public abstract class DefaultSearchExtractorTest extends DefaultListExtractorTes
@Override @Override
public void testSearchSuggestion() throws Exception { public void testSearchSuggestion() throws Exception {
final String expectedSearchSuggestion = expectedSearchSuggestion(); final String expectedSearchSuggestion = expectedSearchSuggestion();
if (expectedSearchSuggestion == null || expectedSearchSuggestion.isEmpty()) { if (isNullOrEmpty(expectedSearchSuggestion)) {
assertEmpty("Suggestion was expected to be empty", extractor().getSearchSuggestion()); assertEmpty("Suggestion was expected to be empty", extractor().getSearchSuggestion());
} else { } else {
assertEquals(expectedSearchSuggestion, extractor().getSearchSuggestion()); assertEquals(expectedSearchSuggestion, extractor().getSearchSuggestion());
} }
} }
@Test
public void testSearchCorrected() throws Exception {
assertEquals(isCorrectedSearch(), extractor().isCorrectedSearch());
}
} }

View File

@ -18,6 +18,7 @@ import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.*; import static org.schabi.newpipe.extractor.ExtractorAsserts.*;
import static org.schabi.newpipe.extractor.StreamingService.LinkType; import static org.schabi.newpipe.extractor.StreamingService.LinkType;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public final class DefaultTests { public final class DefaultTests {
public static void defaultTestListOfItems(StreamingService expectedService, List<? extends InfoItem> itemsList, List<Throwable> errors) throws ParsingException { public static void defaultTestListOfItems(StreamingService expectedService, List<? extends InfoItem> itemsList, List<Throwable> errors) throws ParsingException {
@ -27,8 +28,10 @@ public final class DefaultTests {
for (InfoItem item : itemsList) { for (InfoItem item : itemsList) {
assertIsSecureUrl(item.getUrl()); assertIsSecureUrl(item.getUrl());
if (item.getThumbnailUrl() != null && !item.getThumbnailUrl().isEmpty()) {
assertIsSecureUrl(item.getThumbnailUrl()); final String thumbnailUrl = item.getThumbnailUrl();
if (!isNullOrEmpty(thumbnailUrl)) {
assertIsSecureUrl(thumbnailUrl);
} }
assertNotNull("InfoItem type not set: " + item, item.getInfoType()); assertNotNull("InfoItem type not set: " + item, item.getInfoType());
assertEquals("Unexpected item service id", expectedService.getServiceId(), item.getServiceId()); assertEquals("Unexpected item service id", expectedService.getServiceId(), item.getServiceId());
@ -39,15 +42,15 @@ public final class DefaultTests {
assertNotEmpty("Uploader name not set: " + item, streamInfoItem.getUploaderName()); assertNotEmpty("Uploader name not set: " + item, streamInfoItem.getUploaderName());
// assertNotEmpty("Uploader url not set: " + item, streamInfoItem.getUploaderUrl()); // assertNotEmpty("Uploader url not set: " + item, streamInfoItem.getUploaderUrl());
if (streamInfoItem.getUploaderUrl() != null && !streamInfoItem.getUploaderUrl().isEmpty()) { final String uploaderUrl = streamInfoItem.getUploaderUrl();
assertIsSecureUrl(streamInfoItem.getUploaderUrl()); if (!isNullOrEmpty(uploaderUrl)) {
assertExpectedLinkType(expectedService, streamInfoItem.getUploaderUrl(), LinkType.CHANNEL); assertIsSecureUrl(uploaderUrl);
assertExpectedLinkType(expectedService, uploaderUrl, LinkType.CHANNEL);
} }
assertExpectedLinkType(expectedService, streamInfoItem.getUrl(), LinkType.STREAM); assertExpectedLinkType(expectedService, streamInfoItem.getUrl(), LinkType.STREAM);
final String textualUploadDate = streamInfoItem.getTextualUploadDate(); if (!isNullOrEmpty(streamInfoItem.getTextualUploadDate())) {
if (textualUploadDate != null && !textualUploadDate.isEmpty()) {
final DateWrapper uploadDate = streamInfoItem.getUploadDate(); final DateWrapper uploadDate = streamInfoItem.getUploadDate();
assertNotNull("No parsed upload date", uploadDate); assertNotNull("No parsed upload date", uploadDate);
assertTrue("Upload date not in the past", uploadDate.date().before(Calendar.getInstance())); assertTrue("Upload date not in the past", uploadDate.date().before(Calendar.getInstance()));
@ -83,7 +86,7 @@ public final class DefaultTests {
public static <T extends InfoItem> void assertNoMoreItems(ListExtractor<T> extractor) throws Exception { public static <T extends InfoItem> void assertNoMoreItems(ListExtractor<T> extractor) throws Exception {
assertFalse("More items available when it shouldn't", extractor.hasNextPage()); assertFalse("More items available when it shouldn't", extractor.hasNextPage());
final String nextPageUrl = extractor.getNextPageUrl(); final String nextPageUrl = extractor.getNextPageUrl();
assertTrue("Next page is not empty or null", nextPageUrl == null || nextPageUrl.isEmpty()); assertTrue("Next page is not empty or null", isNullOrEmpty(nextPageUrl));
} }
public static void assertNoDuplicatedItems(StreamingService expectedService, public static void assertNoDuplicatedItems(StreamingService expectedService,

View File

@ -76,7 +76,7 @@ public class MediaCCCStreamExtractorTest {
@Test @Test
public void testUploaderUrl() throws Exception { public void testUploaderUrl() throws Exception {
assertIsSecureUrl(extractor.getUploaderUrl()); assertIsSecureUrl(extractor.getUploaderUrl());
assertEquals("https://api.media.ccc.de/public/conferences/gpn18", extractor.getUploaderUrl()); assertEquals("https://media.ccc.de/public/conferences/gpn18", extractor.getUploaderUrl());
} }
@Test @Test

View File

@ -84,6 +84,21 @@ public class PeertubeChannelExtractorTest {
assertNotNull(extractor.getDescription()); assertNotNull(extractor.getDescription());
} }
@Test
public void testParentChannelName() throws ParsingException {
assertEquals("libux", extractor.getParentChannelName());
}
@Test
public void testParentChannelUrl() throws ParsingException {
assertEquals("https://peertube.mastodon.host/accounts/libux", extractor.getParentChannelUrl());
}
@Test
public void testParentChannelAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getParentChannelAvatarUrl());
}
@Test @Test
public void testAvatarUrl() throws ParsingException { public void testAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getAvatarUrl()); assertIsSecureUrl(extractor.getAvatarUrl());
@ -181,6 +196,21 @@ public class PeertubeChannelExtractorTest {
assertNotNull(extractor.getDescription()); assertNotNull(extractor.getDescription());
} }
@Test
public void testParentChannelName() throws ParsingException {
assertEquals("booteille", extractor.getParentChannelName());
}
@Test
public void testParentChannelUrl() throws ParsingException {
assertEquals("https://peertube.mastodon.host/accounts/booteille", extractor.getParentChannelUrl());
}
@Test
public void testParentChannelAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getParentChannelAvatarUrl());
}
@Test @Test
public void testAvatarUrl() throws ParsingException { public void testAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getAvatarUrl()); assertIsSecureUrl(extractor.getAvatarUrl());

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.extractor.services.peertube; package org.schabi.newpipe.extractor.services.peertube;
import org.jsoup.helper.StringUtil;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
@ -10,12 +9,12 @@ import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeCommentsExtractor; import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeCommentsExtractor;
import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.PeerTube; import static org.schabi.newpipe.extractor.ServiceList.PeerTube;
public class PeertubeCommentsExtractorTest { public class PeertubeCommentsExtractorTest {
@ -47,11 +46,11 @@ public class PeertubeCommentsExtractorTest {
public void testGetCommentsFromCommentsInfo() throws IOException, ExtractionException { public void testGetCommentsFromCommentsInfo() throws IOException, ExtractionException {
boolean result = false; boolean result = false;
CommentsInfo commentsInfo = CommentsInfo.getInfo("https://framatube.org/videos/watch/a8ea95b8-0396-49a6-8f30-e25e25fb2828"); CommentsInfo commentsInfo = CommentsInfo.getInfo("https://framatube.org/videos/watch/a8ea95b8-0396-49a6-8f30-e25e25fb2828");
assertTrue("Comments".equals(commentsInfo.getName())); assertEquals("Comments", commentsInfo.getName());
result = findInComments(commentsInfo.getRelatedItems(), "Loved it!!!"); result = findInComments(commentsInfo.getRelatedItems(), "Loved it!!!");
String nextPage = commentsInfo.getNextPageUrl(); String nextPage = commentsInfo.getNextPageUrl();
while (!StringUtil.isBlank(nextPage) && !result) { while (!Utils.isBlank(nextPage) && !result) {
InfoItemsPage<CommentsInfoItem> moreItems = CommentsInfo.getMoreItems(PeerTube, commentsInfo, nextPage); InfoItemsPage<CommentsInfoItem> moreItems = CommentsInfo.getMoreItems(PeerTube, commentsInfo, nextPage);
result = findInComments(moreItems.getItems(), "Loved it!!!"); result = findInComments(moreItems.getItems(), "Loved it!!!");
nextPage = moreItems.getNextPageUrl(); nextPage = moreItems.getNextPageUrl();
@ -64,15 +63,15 @@ public class PeertubeCommentsExtractorTest {
public void testGetCommentsAllData() throws IOException, ExtractionException { public void testGetCommentsAllData() throws IOException, ExtractionException {
InfoItemsPage<CommentsInfoItem> comments = extractor.getInitialPage(); InfoItemsPage<CommentsInfoItem> comments = extractor.getInitialPage();
for (CommentsInfoItem c : comments.getItems()) { for (CommentsInfoItem c : comments.getItems()) {
assertFalse(StringUtil.isBlank(c.getAuthorEndpoint())); assertFalse(Utils.isBlank(c.getUploaderUrl()));
assertFalse(StringUtil.isBlank(c.getAuthorName())); assertFalse(Utils.isBlank(c.getUploaderName()));
assertFalse(StringUtil.isBlank(c.getAuthorThumbnail())); assertFalse(Utils.isBlank(c.getUploaderAvatarUrl()));
assertFalse(StringUtil.isBlank(c.getCommentId())); assertFalse(Utils.isBlank(c.getCommentId()));
assertFalse(StringUtil.isBlank(c.getCommentText())); assertFalse(Utils.isBlank(c.getCommentText()));
assertFalse(StringUtil.isBlank(c.getName())); assertFalse(Utils.isBlank(c.getName()));
assertFalse(StringUtil.isBlank(c.getTextualPublishedTime())); assertFalse(Utils.isBlank(c.getTextualUploadDate()));
assertFalse(StringUtil.isBlank(c.getThumbnailUrl())); assertFalse(Utils.isBlank(c.getThumbnailUrl()));
assertFalse(StringUtil.isBlank(c.getUrl())); assertFalse(Utils.isBlank(c.getUrl()));
assertFalse(c.getLikeCount() != -1); assertFalse(c.getLikeCount() != -1);
} }
} }

View File

@ -0,0 +1,72 @@
package org.schabi.newpipe.extractor.services.peertube;
import org.junit.BeforeClass;
import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubePlaylistExtractor;
import static org.junit.Assert.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.PeerTube;
public class PeertubePlaylistExtractorTest {
public static class Shocking {
private static PeertubePlaylistExtractor extractor;
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (PeertubePlaylistExtractor) PeerTube
.getPlaylistExtractor("https://framatube.org/videos/watch/playlist/96b0ee2b-a5a7-4794-8769-58d8ccb79ab7");
extractor.fetchPage();
}
@Test
public void testGetName() throws ParsingException {
assertEquals("Shocking !", extractor.getName());
}
@Test
public void testGetThumbnailUrl() throws ParsingException {
assertEquals("https://framatube.org/static/thumbnails/playlist-96b0ee2b-a5a7-4794-8769-58d8ccb79ab7.jpg", extractor.getThumbnailUrl());
}
@Test
public void testGetUploaderUrl() throws ParsingException {
assertEquals("https://skeptikon.fr/accounts/metadechoc", extractor.getUploaderUrl());
}
@Test
public void testGetUploaderAvatarUrl() throws ParsingException {
assertEquals("https://framatube.org/lazy-static/avatars/cd0f781d-0287-4be2-94f1-24cd732337b2.jpg", extractor.getUploaderAvatarUrl());
}
@Test
public void testGetUploaderName() throws ParsingException {
assertEquals("Méta de Choc", extractor.getUploaderName());
}
@Test
public void testGetStreamCount() throws ParsingException {
assertEquals(35, extractor.getStreamCount());
}
@Test
public void testGetSubChannelUrl() throws ParsingException {
assertEquals("https://skeptikon.fr/video-channels/metadechoc_channel", extractor.getSubChannelUrl());
}
@Test
public void testGetSubChannelName() throws ParsingException {
assertEquals("SHOCKING !", extractor.getSubChannelName());
}
@Test
public void testGetSubChannelAvatarUrl() throws ParsingException {
assertEquals("https://framatube.org/lazy-static/avatars/f1dcd0e8-e651-42ed-ae81-bb3bd4aff2bc.png",
extractor.getSubChannelAvatarUrl());
}
}
}

View File

@ -25,12 +25,17 @@ public class PeertubePlaylistLinkHandlerFactoryTest {
@Test @Test
public void acceptUrlTest() throws ParsingException { public void acceptUrlTest() throws ParsingException {
assertTrue(linkHandler.acceptUrl("https://peertube.mastodon.host/video-channels/b45e84fb-c47f-475b-94f2-718126154d33/videos")); assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/d8ca79f9-e4c7-4269-8183-d78ed269c909"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/d8ca79f9-e4c7-4269-8183-d78ed269c909/videos"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/dacdc4ef-5160-4846-9b70-a655880da667"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/96b0ee2b-a5a7-4794-8769-58d8ccb79ab7"));
} }
@Test @Test
public void getIdFromUrl() throws ParsingException { public void getIdFromUrl() throws ParsingException {
assertEquals("b45e84fb-c47f-475b-94f2-718126154d33", linkHandler.fromUrl("https://peertube.mastodon.host/video-channels/b45e84fb-c47f-475b-94f2-718126154d33").getId()); assertEquals("d8ca79f9-e4c7-4269-8183-d78ed269c909", linkHandler.getId("https://framatube.org/videos/watch/playlist/d8ca79f9-e4c7-4269-8183-d78ed269c909"));
assertEquals("b45e84fb-c47f-475b-94f2-718126154d33", linkHandler.fromUrl("https://peertube.mastodon.host/video-channels/b45e84fb-c47f-475b-94f2-718126154d33/videos").getId()); assertEquals("dacdc4ef-5160-4846-9b70-a655880da667", linkHandler.getId("https://framatube.org/videos/watch/playlist/dacdc4ef-5160-4846-9b70-a655880da667"));
assertEquals("bfc145f5-1be7-48a6-9b9e-4f1967199dad", linkHandler.getId("https://framatube.org/videos/watch/playlist/bfc145f5-1be7-48a6-9b9e-4f1967199dad"));
assertEquals("96b0ee2b-a5a7-4794-8769-58d8ccb79ab7", linkHandler.getId("https://framatube.org/videos/watch/playlist/96b0ee2b-a5a7-4794-8769-58d8ccb79ab7"));
} }
} }

View File

@ -87,6 +87,33 @@ public class PeertubeStreamExtractorDefaultTest {
assertEquals("Framasoft", extractor.getUploaderName()); assertEquals("Framasoft", extractor.getUploaderName());
} }
@Test
public void testGetUploaderUrl() throws ParsingException {
assertIsSecureUrl(extractor.getUploaderUrl());
assertEquals("https://framatube.org/api/v1/accounts/framasoft@framatube.org", extractor.getUploaderUrl());
}
@Test
public void testGetUploaderAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getUploaderAvatarUrl());
}
@Test
public void testGetSubChannelName() throws ParsingException {
assertEquals("Les vidéos de Framasoft", extractor.getSubChannelName());
}
@Test
public void testGetSubChannelUrl() throws ParsingException {
assertIsSecureUrl(extractor.getSubChannelUrl());
assertEquals("https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8", extractor.getSubChannelUrl());
}
@Test
public void testGetSubChannelAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getSubChannelAvatarUrl());
}
@Test @Test
public void testGetLength() throws ParsingException { public void testGetLength() throws ParsingException {
assertEquals(113, extractor.getLength()); assertEquals(113, extractor.getLength());
@ -98,22 +125,11 @@ public class PeertubeStreamExtractorDefaultTest {
extractor.getViewCount() > 10); extractor.getViewCount() > 10);
} }
@Test
public void testGetUploaderUrl() throws ParsingException {
assertIsSecureUrl(extractor.getUploaderUrl());
assertEquals("https://framatube.org/api/v1/accounts/framasoft@framatube.org", extractor.getUploaderUrl());
}
@Test @Test
public void testGetThumbnailUrl() throws ParsingException { public void testGetThumbnailUrl() throws ParsingException {
assertIsSecureUrl(extractor.getThumbnailUrl()); assertIsSecureUrl(extractor.getThumbnailUrl());
} }
@Test
public void testGetUploaderAvatarUrl() throws ParsingException {
assertIsSecureUrl(extractor.getUploaderAvatarUrl());
}
@Test @Test
public void testGetVideoStreams() throws IOException, ExtractionException { public void testGetVideoStreams() throws IOException, ExtractionException {
assertFalse(extractor.getVideoStreams().isEmpty()); assertFalse(extractor.getVideoStreams().isEmpty());

View File

@ -3,17 +3,10 @@ package org.schabi.newpipe.extractor.services.peertube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.services.BaseListExtractorTest; import org.schabi.newpipe.extractor.services.BaseListExtractorTest;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeTrendingExtractor; import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeTrendingExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudChartsExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeTrendingExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.util.List;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ServiceList.*; import static org.schabi.newpipe.extractor.ServiceList.*;

View File

@ -7,6 +7,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest; import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudChannelExtractor;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmpty; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmpty;

View File

@ -3,19 +3,13 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.services.BaseListExtractorTest; import org.schabi.newpipe.extractor.services.BaseListExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeTrendingExtractor; import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudChartsExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.util.List;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
import static org.schabi.newpipe.extractor.services.DefaultTests.*; import static org.schabi.newpipe.extractor.services.DefaultTests.*;
public class SoundcloudChartsExtractorTest { public class SoundcloudChartsExtractorTest {

View File

@ -5,6 +5,7 @@ import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudChartsLinkHandlerFactory;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@ -7,6 +7,7 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
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.soundcloud.extractors.SoundcloudPlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;

View File

@ -8,6 +8,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; 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.services.soundcloud.extractors.SoundcloudStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
@ -16,6 +17,7 @@ import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.TimeZone;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -83,7 +85,9 @@ public class SoundcloudStreamExtractorDefaultTest {
@Test @Test
public void testGetUploadDate() throws ParsingException, ParseException { public void testGetUploadDate() throws ParsingException, ParseException {
final Calendar instance = Calendar.getInstance(); final Calendar instance = Calendar.getInstance();
instance.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse("2016/07/31 18:18:07 +0000")); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
instance.setTime(sdf.parse("2016/07/31 18:18:07 +0000"));
assertEquals(instance, requireNonNull(extractor.getUploadDate()).date()); assertEquals(instance, requireNonNull(extractor.getUploadDate()).date());
} }

View File

@ -5,6 +5,7 @@ import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudStreamLinkHandlerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -7,6 +7,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudSubscriptionExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem; import org.schabi.newpipe.extractor.subscription.SubscriptionItem;

View File

@ -17,7 +17,7 @@ import java.net.URLEncoder;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
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.SoundcloudSearchQueryHandlerFactory.*; import static org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudSearchQueryHandlerFactory.*;
public class SoundcloudSearchExtractorTest { public class SoundcloudSearchExtractorTest {

View File

@ -8,7 +8,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory.*; import static org.schabi.newpipe.extractor.services.soundcloud.linkHandler.SoundcloudSearchQueryHandlerFactory.*;
public class SoundcloudSearchQHTest { public class SoundcloudSearchQHTest {

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.extractor.services.youtube; package org.schabi.newpipe.extractor.services.youtube;
import org.jsoup.helper.StringUtil;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
@ -11,11 +10,15 @@ import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.DefaultTests; import org.schabi.newpipe.extractor.services.DefaultTests;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeCommentsExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeCommentsExtractor;
import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.ServiceList.YouTube;
public class YoutubeCommentsExtractorTest { public class YoutubeCommentsExtractorTest {
@ -68,15 +71,14 @@ public class YoutubeCommentsExtractorTest {
private boolean getCommentsFromCommentsInfoHelper(String url) throws IOException, ExtractionException { private boolean getCommentsFromCommentsInfoHelper(String url) throws IOException, ExtractionException {
boolean result = false; boolean result = false;
CommentsInfo commentsInfo = CommentsInfo.getInfo(url); CommentsInfo commentsInfo = CommentsInfo.getInfo(url);
assertEquals("what the fuck am i doing with my life", commentsInfo.getName());
result = findInComments(commentsInfo.getRelatedItems(), "s1ck m3m3"); result = findInComments(commentsInfo.getRelatedItems(), "s1ck m3m3");
String nextPage = commentsInfo.getNextPageUrl(); /* String nextPage = commentsInfo.getNextPageUrl();
while (!StringUtil.isBlank(nextPage) && !result) { while (!Utils.isBlank(nextPage) && !result) {
InfoItemsPage<CommentsInfoItem> moreItems = CommentsInfo.getMoreItems(YouTube, commentsInfo, nextPage); InfoItemsPage<CommentsInfoItem> moreItems = CommentsInfo.getMoreItems(YouTube, commentsInfo, nextPage);
result = findInComments(moreItems.getItems(), "s1ck m3m3"); result = findInComments(moreItems.getItems(), "s1ck m3m3");
nextPage = moreItems.getNextPageUrl(); nextPage = moreItems.getNextPageUrl();
} }*/
return result; return result;
} }
@ -86,16 +88,16 @@ public class YoutubeCommentsExtractorTest {
DefaultTests.defaultTestListOfItems(YouTube, comments.getItems(), comments.getErrors()); DefaultTests.defaultTestListOfItems(YouTube, comments.getItems(), comments.getErrors());
for (CommentsInfoItem c : comments.getItems()) { for (CommentsInfoItem c : comments.getItems()) {
assertFalse(StringUtil.isBlank(c.getAuthorEndpoint())); assertFalse(Utils.isBlank(c.getUploaderUrl()));
assertFalse(StringUtil.isBlank(c.getAuthorName())); assertFalse(Utils.isBlank(c.getUploaderName()));
assertFalse(StringUtil.isBlank(c.getAuthorThumbnail())); assertFalse(Utils.isBlank(c.getUploaderAvatarUrl()));
assertFalse(StringUtil.isBlank(c.getCommentId())); assertFalse(Utils.isBlank(c.getCommentId()));
assertFalse(StringUtil.isBlank(c.getCommentText())); assertFalse(Utils.isBlank(c.getCommentText()));
assertFalse(StringUtil.isBlank(c.getName())); assertFalse(Utils.isBlank(c.getName()));
assertFalse(StringUtil.isBlank(c.getTextualPublishedTime())); assertFalse(Utils.isBlank(c.getTextualUploadDate()));
assertNotNull(c.getPublishedTime()); assertNotNull(c.getUploadDate());
assertFalse(StringUtil.isBlank(c.getThumbnailUrl())); assertFalse(Utils.isBlank(c.getThumbnailUrl()));
assertFalse(StringUtil.isBlank(c.getUrl())); assertFalse(Utils.isBlank(c.getUrl()));
assertFalse(c.getLikeCount() < 0); assertFalse(c.getLikeCount() < 0);
} }
} }

View File

@ -5,7 +5,6 @@ import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import java.io.IOException; import java.io.IOException;

View File

@ -254,4 +254,102 @@ public class YoutubePlaylistExtractorTest {
assertTrue("Error in the streams count", extractor.getStreamCount() > 100); assertTrue("Error in the streams count", extractor.getStreamCount() > 100);
} }
} }
public static class LearningPlaylist implements BasePlaylistExtractorTest {
private static YoutubePlaylistExtractor extractor;
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubePlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/playlist?list=PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8");
extractor.fetchPage();
}
/*//////////////////////////////////////////////////////////////////////////
// Extractor
//////////////////////////////////////////////////////////////////////////*/
@Test
public void testServiceId() {
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
}
@Test
public void testName() throws Exception {
String name = extractor.getName();
assertTrue(name, name.startsWith("Anatomy & Physiology"));
}
@Test
public void testId() throws Exception {
assertEquals("PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8", extractor.getId());
}
@Test
public void testUrl() throws ParsingException {
assertEquals("https://www.youtube.com/playlist?list=PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8", extractor.getUrl());
}
@Test
public void testOriginalUrl() throws ParsingException {
assertEquals("https://www.youtube.com/playlist?list=PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8", extractor.getOriginalUrl());
}
/*//////////////////////////////////////////////////////////////////////////
// ListExtractor
//////////////////////////////////////////////////////////////////////////*/
@Test
public void testRelatedItems() throws Exception {
defaultTestRelatedItems(extractor);
}
@Ignore
@Test
public void testMoreRelatedItems() throws Exception {
defaultTestMoreItems(extractor);
}
/*//////////////////////////////////////////////////////////////////////////
// PlaylistExtractor
//////////////////////////////////////////////////////////////////////////*/
@Test
public void testThumbnailUrl() throws Exception {
final String thumbnailUrl = extractor.getThumbnailUrl();
assertIsSecureUrl(thumbnailUrl);
assertTrue(thumbnailUrl, thumbnailUrl.contains("yt"));
}
@Ignore
@Test
public void testBannerUrl() throws Exception {
final String bannerUrl = extractor.getBannerUrl();
assertIsSecureUrl(bannerUrl);
assertTrue(bannerUrl, bannerUrl.contains("yt"));
}
@Test
public void testUploaderUrl() throws Exception {
assertEquals("https://www.youtube.com/channel/UCX6b17PVsYBQ0ip5gyeme-Q", extractor.getUploaderUrl());
}
@Test
public void testUploaderName() throws Exception {
final String uploaderName = extractor.getUploaderName();
assertTrue(uploaderName, uploaderName.contains("CrashCourse"));
}
@Test
public void testUploaderAvatarUrl() throws Exception {
final String uploaderAvatarUrl = extractor.getUploaderAvatarUrl();
assertTrue(uploaderAvatarUrl, uploaderAvatarUrl.contains("yt"));
}
@Test
public void testStreamCount() throws Exception {
assertTrue("Error in the streams count", extractor.getStreamCount() > 40);
}
}
} }

View File

@ -79,6 +79,7 @@ public class YoutubeStreamLinkHandlerFactoryTest {
assertEquals("EhxJLojIE_o", linkHandler.fromUrl("http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare").getId()); assertEquals("EhxJLojIE_o", linkHandler.fromUrl("http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare").getId());
assertEquals("jZViOEv90dI", linkHandler.fromUrl("vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI").getId()); assertEquals("jZViOEv90dI", linkHandler.fromUrl("vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI").getId());
assertEquals("jZViOEv90dI", linkHandler.fromUrl("vnd.youtube:jZViOEv90dI").getId()); assertEquals("jZViOEv90dI", linkHandler.fromUrl("vnd.youtube:jZViOEv90dI").getId());
assertEquals("n8X9_MgEdCg", linkHandler.fromUrl("vnd.youtube://n8X9_MgEdCg").getId());
assertEquals("O0EDx9WAelc", linkHandler.fromUrl("https://music.youtube.com/watch?v=O0EDx9WAelc").getId()); assertEquals("O0EDx9WAelc", linkHandler.fromUrl("https://music.youtube.com/watch?v=O0EDx9WAelc").getId());
} }

View File

@ -150,4 +150,28 @@ public class YoutubeMusicSearchExtractorTest {
@Nullable @Override public String expectedSearchSuggestion() { return "mega man x3"; } @Nullable @Override public String expectedSearchSuggestion() { return "mega man x3"; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; } @Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
} }
public static class CorrectedSearch extends DefaultSearchExtractorTest {
private static SearchExtractor extractor;
private static final String QUERY = "duo lipa";
private static final String EXPECTED_SUGGESTION = "dua lipa";
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_SONGS), "");
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 "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
@Override public String expectedSearchString() { return QUERY; }
@Nullable @Override public String expectedSearchSuggestion() { return EXPECTED_SUGGESTION; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
@Override public boolean isCorrectedSearch() { return true; }
}
} }

View File

@ -20,6 +20,7 @@ import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmptyErrors;
import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.ServiceList.YouTube;
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.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.*; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.*;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class YoutubeSearchExtractorTest { public class YoutubeSearchExtractorTest {
public static class All extends DefaultSearchExtractorTest { public static class All extends DefaultSearchExtractorTest {
@ -113,6 +114,29 @@ public class YoutubeSearchExtractorTest {
} }
public static class Suggestion extends DefaultSearchExtractorTest { public static class Suggestion extends DefaultSearchExtractorTest {
private static SearchExtractor extractor;
private static final String QUERY = "newpip";
private static final String EXPECTED_SUGGESTION = "newpipe";
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = YouTube.getSearchExtractor(QUERY, singletonList(VIDEOS), "");
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 EXPECTED_SUGGESTION; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
}
public static class CorrectedSearch extends DefaultSearchExtractorTest {
private static SearchExtractor extractor; private static SearchExtractor extractor;
private static final String QUERY = "pewdeipie"; private static final String QUERY = "pewdeipie";
private static final String EXPECTED_SUGGESTION = "pewdiepie"; private static final String EXPECTED_SUGGESTION = "pewdiepie";
@ -132,8 +156,8 @@ public class YoutubeSearchExtractorTest {
@Override public String expectedOriginalUrlContains() { return "youtube.com/results?search_query=" + QUERY; } @Override public String expectedOriginalUrlContains() { return "youtube.com/results?search_query=" + QUERY; }
@Override public String expectedSearchString() { return QUERY; } @Override public String expectedSearchString() { return QUERY; }
@Nullable @Override public String expectedSearchSuggestion() { return EXPECTED_SUGGESTION; } @Nullable @Override public String expectedSearchSuggestion() { return EXPECTED_SUGGESTION; }
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; } @Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
@Override public boolean isCorrectedSearch() { return true; }
} }
public static class RandomQueryNoMorePages extends DefaultSearchExtractorTest { public static class RandomQueryNoMorePages extends DefaultSearchExtractorTest {
@ -170,7 +194,7 @@ public class YoutubeSearchExtractorTest {
assertFalse("More items available when it shouldn't", nextEmptyPage.hasNextPage()); assertFalse("More items available when it shouldn't", nextEmptyPage.hasNextPage());
final String nextPageUrl = nextEmptyPage.getNextPageUrl(); final String nextPageUrl = nextEmptyPage.getNextPageUrl();
assertTrue("Next page is not empty or null", nextPageUrl == null || nextPageUrl.isEmpty()); assertTrue("Next page is not empty or null", isNullOrEmpty(nextPageUrl));
} }
} }

Some files were not shown because too many files have changed in this diff Show More