Merge branch 'origin/dev' into dev
This commit is contained in:
		
						commit
						dd955c7f0c
					
				
					 102 changed files with 1716 additions and 589 deletions
				
			
		
							
								
								
									
										8
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,7 @@ jdk:
 | 
				
			||||||
  - openjdk8
 | 
					  - openjdk8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
script:
 | 
					script:
 | 
				
			||||||
  - ./gradlew check
 | 
					  - ./gradlew check aggregatedJavadocs
 | 
				
			||||||
  - ./gradlew aggregatedJavadocs
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
deploy:
 | 
					deploy:
 | 
				
			||||||
  provider: pages
 | 
					  provider: pages
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 "";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					@ -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"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -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?
 | 
				
			||||||
| 
						 | 
					@ -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() {
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 + "\"");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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<>();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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>.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.*;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.*;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue