Merge pull request #817 from Stypox/checkstyle

Add checkstyle
This commit is contained in:
litetex 2022-03-26 20:46:49 +01:00 committed by GitHub
commit bb49f7d857
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
175 changed files with 3221 additions and 2274 deletions

View File

@ -40,3 +40,10 @@ jobs:
echo running with mock downloader echo running with mock downloader
./gradlew check --stacktrace -Ddownloader=MOCK ./gradlew check --stacktrace -Ddownloader=MOCK
fi fi
- name: Upload test reports when failure occurs
uses: actions/upload-artifact@v3
if: failure()
with:
name: NewPipeExtractor-test-reports
path: extractor/build/reports/tests/test/**

View File

@ -30,6 +30,7 @@ allprojects {
nanojsonVersion = "1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" nanojsonVersion = "1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
spotbugsVersion = "4.6.0" spotbugsVersion = "4.6.0"
junitVersion = "5.8.2" junitVersion = "5.8.2"
checkstyleVersion = "9.3" // do not use latest version (10.0) as it requires compile JDK 11
} }
} }

189
checkstyle/checkstyle.xml Normal file
View File

@ -0,0 +1,189 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
https://checkstyle.org/5.x/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${config_loc}/suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Checks that a package-info.java file exists for each package. -->
<!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
<!--<module name="JavadocPackage"/>-->
<!-- Checks whether files end with a new line. -->
<!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See https://checkstyle.org/config_misc.html#Translation -->
<module name="Translation"/>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/config_sizes.html -->
<module name="FileLength"/>
<module name="LineLength">
<property name="max" value="100"/>
<property name="fileExtensions" value="java"/>
</module>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/config_misc.html -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- Checks for Headers -->
<!-- See https://checkstyle.org/config_header.html -->
<!-- <module name="Header"> -->
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
<!-- <property name="fileExtensions" value="java"/> -->
<!-- </module> -->
<module name="SuppressWarningsFilter" />
<module name="TreeWalker">
<!-- Checks for Javadoc comments. -->
<!-- See https://checkstyle.org/config_javadoc.html -->
<module name="InvalidJavadocPosition"/>
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
</module>
<module name="JavadocType"/>
<!--<module name="JavadocVariable"/>-->
<module name="JavadocStyle">
<property name="checkFirstSentence" value="false"/>
</module>
<!--<module name="MissingJavadocMethod"/>-->
<!-- Checks for Naming Conventions. -->
<!-- See https://checkstyle.org/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName">
<property name="format" value="^(TAG|DEBUG|[a-z][a-zA-Z0-9]*)$"/>
</module>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See https://checkstyle.org/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/config_sizes.html -->
<module name="MethodLength">
<property name="severity" value="warning"/>
</module>
<module name="ParameterNumber">
<property name="severity" value="warning"/>
</module>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See https://checkstyle.org/config_modifiers.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See https://checkstyle.org/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See https://checkstyle.org/config_coding.html -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode">
<property name="severity" value="warning"/>
</module>
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<!--<module name="MagicNumber"/>-->
<!--<module name="MissingSwitchDefault">
<property name="severity" value="warning"/>
</module>-->
<module name="MultipleVariableDeclarations"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="FinalLocalVariable">
<property name="tokens" value="VARIABLE_DEF,PARAMETER_DEF"/>
<property name="validateEnhancedForLoopVariable" value="true"/>
</module>
<!-- Checks for class design -->
<!-- See https://checkstyle.org/config_design.html -->
<!--<module name="DesignForExtension"/>-->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<!--<module name="VisibilityModifier">
<property name="ignoreAnnotationCanonicalNames" value="State,ColumnInfo"/>
<property name="severity" value="warning"/>
</module>-->
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/config_misc.html -->
<module name="ArrayTypeStyle"/>
<module name="FinalParameters"/>
<!--<module name="TodoComment">
<property name="format" value="(TODO:|FIXME:)"/>
<property name="severity" value="warning"/>
</module>-->
<module name="UpperEll"/>
<module name="SuppressWarningsHolder" />
</module>
</module>

View File

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<!-- Use @SuppressWarnings("...") if it is possible, only use this file if it is not -->
<suppress checks="LineLength"
files="BandcampExtractorHelper.java"
lines="54"/>
<suppress checks="LineLength"
files="ItagItem.java"
lines="19"/>
</suppressions>

View File

@ -1,9 +1,25 @@
plugins {
id 'checkstyle'
}
test { test {
// Pass on downloader type to tests for different CI jobs. See DownloaderFactory.java and ci.yml // Pass on downloader type to tests for different CI jobs. See DownloaderFactory.java and ci.yml
if (System.properties.containsKey('downloader')) { if (System.properties.containsKey('downloader')) {
systemProperty('downloader', System.getProperty('downloader')) systemProperty('downloader', System.getProperty('downloader'))
} }
useJUnitPlatform() useJUnitPlatform()
dependsOn checkstyleMain // run checkstyle when testing
}
checkstyle {
getConfigDirectory().set(rootProject.file("checkstyle"))
ignoreFailures false
showViolations true
toolVersion checkstyleVersion
}
checkstyleTest {
enabled false // do not checkstyle test files
} }
dependencies { dependencies {
@ -15,6 +31,8 @@ dependencies {
implementation "com.github.spotbugs:spotbugs-annotations:$spotbugsVersion" implementation "com.github.spotbugs:spotbugs-annotations:$spotbugsVersion"
implementation 'org.nibor.autolink:autolink:0.10.0' implementation 'org.nibor.autolink:autolink:0.10.0'
checkstyle "com.puppycrawl.tools:checkstyle:$checkstyleVersion"
testImplementation platform("org.junit:junit-bom:$junitVersion") testImplementation platform("org.junit:junit-bom:$junitVersion")
testImplementation 'org.junit.jupiter:junit-jupiter-api' testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'

View File

@ -10,13 +10,15 @@ import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public abstract class Extractor { public abstract class Extractor {
/** /**
* {@link StreamingService} currently related to this extractor.<br> * {@link StreamingService} currently related to this extractor.<br>
* Useful for getting other things from a service (like the url handlers for cleaning/accepting/get id from urls). * Useful for getting other things from a service (like the url handlers for
* cleaning/accepting/get id from urls).
*/ */
private final StreamingService service; private final StreamingService service;
private final LinkHandler linkHandler; private final LinkHandler linkHandler;
@ -27,16 +29,18 @@ public abstract class Extractor {
private ContentCountry forcedContentCountry = null; private ContentCountry forcedContentCountry = null;
private boolean pageFetched = false; private boolean pageFetched = false;
// called like this to prevent checkstyle errors about "hiding a field"
private final Downloader downloader; private final Downloader downloader;
public Extractor(final StreamingService service, final LinkHandler linkHandler) { protected Extractor(final StreamingService service, final LinkHandler linkHandler) {
this.service = Objects.requireNonNull(service, "service is null"); this.service = Objects.requireNonNull(service, "service is null");
this.linkHandler = Objects.requireNonNull(linkHandler, "LinkHandler is null"); this.linkHandler = Objects.requireNonNull(linkHandler, "LinkHandler is null");
this.downloader = Objects.requireNonNull(NewPipe.getDownloader(), "downloader is null"); this.downloader = Objects.requireNonNull(NewPipe.getDownloader(), "downloader is null");
} }
/** /**
* @return The {@link LinkHandler} of the current extractor object (e.g. a ChannelExtractor should return a channel url handler). * @return The {@link LinkHandler} of the current extractor object (e.g. a ChannelExtractor
* should return a channel url handler).
*/ */
@Nonnull @Nonnull
public LinkHandler getLinkHandler() { public LinkHandler getLinkHandler() {
@ -50,13 +54,17 @@ public abstract class Extractor {
* @throws ExtractionException if the pages content is not understood * @throws ExtractionException if the pages content is not understood
*/ */
public void fetchPage() throws IOException, ExtractionException { public void fetchPage() throws IOException, ExtractionException {
if (pageFetched) return; if (pageFetched) {
return;
}
onFetchPage(downloader); onFetchPage(downloader);
pageFetched = true; pageFetched = true;
} }
protected void assertPageFetched() { protected void assertPageFetched() {
if (!pageFetched) throw new IllegalStateException("Page is not fetched. Make sure you call fetchPage()"); if (!pageFetched) {
throw new IllegalStateException("Page is not fetched. Make sure you call fetchPage()");
}
} }
protected boolean isPageFetched() { protected boolean isPageFetched() {
@ -66,11 +74,13 @@ public abstract class Extractor {
/** /**
* Fetch the current page. * Fetch the current page.
* *
* @param downloader the download to use * @param downloader the downloader to use
* @throws IOException if the page can not be loaded * @throws IOException if the page can not be loaded
* @throws ExtractionException if the pages content is not understood * @throws ExtractionException if the pages content is not understood
*/ */
public abstract void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException; @SuppressWarnings("HiddenField")
public abstract void onFetchPage(@Nonnull Downloader downloader)
throws IOException, ExtractionException;
@Nonnull @Nonnull
public String getId() throws ParsingException { public String getId() throws ParsingException {
@ -118,11 +128,11 @@ public abstract class Extractor {
// Localization // Localization
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public void forceLocalization(Localization localization) { public void forceLocalization(final Localization localization) {
this.forcedLocalization = localization; this.forcedLocalization = localization;
} }
public void forceContentCountry(ContentCountry contentCountry) { public void forceContentCountry(final ContentCountry contentCountry) {
this.forcedContentCountry = contentCountry; this.forcedContentCountry = contentCountry;
} }
@ -133,7 +143,8 @@ public abstract class Extractor {
@Nonnull @Nonnull
public ContentCountry getExtractorContentCountry() { public ContentCountry getExtractorContentCountry() {
return forcedContentCountry == null ? getService().getContentCountry() : forcedContentCountry; return forcedContentCountry == null ? getService().getContentCountry()
: forcedContentCountry;
} }
@Nonnull @Nonnull

View File

@ -17,7 +17,8 @@ public abstract class Info implements Serializable {
*/ */
private final String id; private final String id;
/** /**
* Different than the {@link #originalUrl} in the sense that it <i>may</i> be set as a cleaned url. * Different than the {@link #originalUrl} in the sense that it <i>may</i> be set as a cleaned
* url.
* *
* @see LinkHandler#getUrl() * @see LinkHandler#getUrl()
* @see Extractor#getOriginalUrl() * @see Extractor#getOriginalUrl()
@ -33,15 +34,19 @@ public abstract class Info implements Serializable {
private final List<Throwable> errors = new ArrayList<>(); private final List<Throwable> errors = new ArrayList<>();
public void addError(Throwable throwable) { public void addError(final Throwable throwable) {
this.errors.add(throwable); this.errors.add(throwable);
} }
public void addAllErrors(Collection<Throwable> errors) { public void addAllErrors(final Collection<Throwable> throwables) {
this.errors.addAll(errors); this.errors.addAll(throwables);
} }
public Info(int serviceId, String id, String url, String originalUrl, String name) { public Info(final int serviceId,
final String id,
final String url,
final String originalUrl,
final String name) {
this.serviceId = serviceId; this.serviceId = serviceId;
this.id = id; this.id = id;
this.url = url; this.url = url;
@ -49,7 +54,7 @@ public abstract class Info implements Serializable {
this.name = name; this.name = name;
} }
public Info(int serviceId, LinkHandler linkHandler, String name) { public Info(final int serviceId, final LinkHandler linkHandler, final String name) {
this(serviceId, this(serviceId,
linkHandler.getId(), linkHandler.getId(),
linkHandler.getUrl(), linkHandler.getUrl(),
@ -59,14 +64,16 @@ public abstract class Info implements Serializable {
@Override @Override
public String toString() { public String toString() {
final String ifDifferentString = !url.equals(originalUrl) ? " (originalUrl=\"" + originalUrl + "\")" : ""; final String ifDifferentString
return getClass().getSimpleName() + "[url=\"" + url + "\"" + ifDifferentString + ", name=\"" + name + "\"]"; = url.equals(originalUrl) ? "" : " (originalUrl=\"" + originalUrl + "\")";
return getClass().getSimpleName() + "[url=\"" + url + "\"" + ifDifferentString
+ ", name=\"" + name + "\"]";
} }
// if you use an api and want to handle the website url // if you use an api and want to handle the website url
// overriding original url is essential // overriding original url is essential
public void setOriginalUrl(String url) { public void setOriginalUrl(final String originalUrl) {
originalUrl = url; this.originalUrl = originalUrl;
} }
public int getServiceId() { public int getServiceId() {

View File

@ -29,7 +29,10 @@ public abstract class InfoItem implements Serializable {
private final String name; private final String name;
private String thumbnailUrl; private String thumbnailUrl;
public InfoItem(InfoType infoType, int serviceId, String url, String name) { public InfoItem(final InfoType infoType,
final int serviceId,
final String url,
final String name) {
this.infoType = infoType; this.infoType = infoType;
this.serviceId = serviceId; this.serviceId = serviceId;
this.url = url; this.url = url;
@ -52,7 +55,7 @@ public abstract class InfoItem implements Serializable {
return name; return name;
} }
public void setThumbnailUrl(String thumbnailUrl) { public void setThumbnailUrl(final String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl; this.thumbnailUrl = thumbnailUrl;
} }

View File

@ -29,7 +29,8 @@ import java.util.List;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/ */
public abstract class InfoItemsCollector<I extends InfoItem, E extends InfoItemExtractor> implements Collector<I, E> { public abstract class InfoItemsCollector<I extends InfoItem, E extends InfoItemExtractor>
implements Collector<I, E> {
private final List<I> itemList = new ArrayList<>(); private final List<I> itemList = new ArrayList<>();
private final List<Throwable> errors = new ArrayList<>(); private final List<Throwable> errors = new ArrayList<>();
@ -77,7 +78,7 @@ public abstract class InfoItemsCollector<I extends InfoItem, E extends InfoItemE
* Add an error * Add an error
* @param error the error * @param error the error
*/ */
protected void addError(Exception error) { protected void addError(final Exception error) {
errors.add(error); errors.add(error);
} }
@ -85,7 +86,7 @@ public abstract class InfoItemsCollector<I extends InfoItem, E extends InfoItemE
* Add an item * Add an item
* @param item the item * @param item the item
*/ */
protected void addItem(I item) { protected void addItem(final I item) {
itemList.add(item); itemList.add(item);
} }
@ -98,12 +99,12 @@ public abstract class InfoItemsCollector<I extends InfoItem, E extends InfoItemE
} }
@Override @Override
public void commit(E extractor) { public void commit(final E extractor) {
try { try {
addItem(extract(extractor)); addItem(extract(extractor));
} catch (FoundAdException ae) { } catch (final FoundAdException ae) {
// found an ad. Maybe a debug line could be placed here // found an ad. Maybe a debug line could be placed here
} catch (ParsingException e) { } catch (final ParsingException e) {
addError(e); addError(e);
} }
} }

View File

@ -12,6 +12,7 @@ import javax.annotation.Nonnull;
/** /**
* Base class to extractors that have a list (e.g. playlists, users). * Base class to extractors that have a list (e.g. playlists, users).
* @param <R> the info item type this list extractor provides
*/ */
public abstract class ListExtractor<R extends InfoItem> extends Extractor { public abstract class ListExtractor<R extends InfoItem> extends Extractor {
/** /**
@ -30,7 +31,7 @@ public abstract class ListExtractor<R extends InfoItem> extends Extractor {
*/ */
public static final long ITEM_COUNT_MORE_THAN_100 = -3; public static final long ITEM_COUNT_MORE_THAN_100 = -3;
public ListExtractor(StreamingService service, ListLinkHandler linkHandler) { public ListExtractor(final StreamingService service, final ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@ -50,8 +51,9 @@ public abstract class ListExtractor<R extends InfoItem> extends Extractor {
* @return a {@link InfoItemsPage} corresponding to the requested page * @return a {@link InfoItemsPage} corresponding to the requested page
* @see InfoItemsPage#getNextPage() * @see InfoItemsPage#getNextPage()
*/ */
public abstract InfoItemsPage<R> getPage(final Page page) throws IOException, ExtractionException; public abstract InfoItemsPage<R> getPage(Page page) throws IOException, ExtractionException;
@Nonnull
@Override @Override
public ListLinkHandler getLinkHandler() { public ListLinkHandler getLinkHandler() {
return (ListLinkHandler) super.getLinkHandler(); return (ListLinkHandler) super.getLinkHandler();
@ -64,15 +66,17 @@ public abstract class ListExtractor<R extends InfoItem> extends Extractor {
/** /**
* A class that is used to wrap a list of gathered items and eventual errors, it * A class that is used to wrap a list of gathered items and eventual errors, it
* also contains a field that points to the next available page ({@link #nextPage}). * also contains a field that points to the next available page ({@link #nextPage}).
* @param <T> the info item type that this page is supposed to store and provide
*/ */
public static class InfoItemsPage<T extends InfoItem> { public static class InfoItemsPage<T extends InfoItem> {
private static final InfoItemsPage<InfoItem> EMPTY = private static final InfoItemsPage<InfoItem> EMPTY =
new InfoItemsPage<>(Collections.<InfoItem>emptyList(), null, Collections.<Throwable>emptyList()); new InfoItemsPage<>(Collections.emptyList(), null, Collections.emptyList());
/** /**
* A convenient method that returns a representation of an empty page. * A convenient method that returns a representation of an empty page.
* *
* @return a type-safe page with the list of items and errors empty and the nextPage set to {@code null}. * @return a type-safe page with the list of items and errors empty and the nextPage set to
* {@code null}.
*/ */
public static <T extends InfoItem> InfoItemsPage<T> emptyPage() { public static <T extends InfoItem> InfoItemsPage<T> emptyPage() {
//noinspection unchecked //noinspection unchecked
@ -97,11 +101,13 @@ public abstract class ListExtractor<R extends InfoItem> extends Extractor {
*/ */
private final List<Throwable> errors; private final List<Throwable> errors;
public InfoItemsPage(InfoItemsCollector<T, ?> collector, Page nextPage) { public InfoItemsPage(final InfoItemsCollector<T, ?> collector, final Page nextPage) {
this(collector.getItems(), nextPage, collector.getErrors()); this(collector.getItems(), nextPage, collector.getErrors());
} }
public InfoItemsPage(List<T> itemsList, Page nextPage, List<Throwable> errors) { public InfoItemsPage(final List<T> itemsList,
final Page nextPage,
final List<Throwable> errors) {
this.itemsList = itemsList; this.itemsList = itemsList;
this.nextPage = nextPage; this.nextPage = nextPage;
this.errors = errors; this.errors = errors;

View File

@ -10,19 +10,21 @@ public abstract class ListInfo<T extends InfoItem> extends Info {
private final List<String> contentFilters; private final List<String> contentFilters;
private final String sortFilter; private final String sortFilter;
public ListInfo(int serviceId, public ListInfo(final int serviceId,
String id, final String id,
String url, final String url,
String originalUrl, final String originalUrl,
String name, final String name,
List<String> contentFilter, final List<String> contentFilter,
String sortFilter) { final String sortFilter) {
super(serviceId, id, url, originalUrl, name); super(serviceId, id, url, originalUrl, name);
this.contentFilters = contentFilter; this.contentFilters = contentFilter;
this.sortFilter = sortFilter; this.sortFilter = sortFilter;
} }
public ListInfo(int serviceId, ListLinkHandler listUrlIdHandler, String name) { public ListInfo(final int serviceId,
final ListLinkHandler listUrlIdHandler,
final String name) {
super(serviceId, listUrlIdHandler, name); super(serviceId, listUrlIdHandler, name);
this.contentFilters = listUrlIdHandler.getContentFilters(); this.contentFilters = listUrlIdHandler.getContentFilters();
this.sortFilter = listUrlIdHandler.getSortFilter(); this.sortFilter = listUrlIdHandler.getSortFilter();
@ -32,7 +34,7 @@ public abstract class ListInfo<T extends InfoItem> extends Info {
return relatedItems; return relatedItems;
} }
public void setRelatedItems(List<T> relatedItems) { public void setRelatedItems(final List<T> relatedItems) {
this.relatedItems = relatedItems; this.relatedItems = relatedItems;
} }
@ -44,7 +46,7 @@ public abstract class ListInfo<T extends InfoItem> extends Info {
return nextPage; return nextPage;
} }
public void setNextPage(Page page) { public void setNextPage(final Page page) {
this.nextPage = page; this.nextPage = page;
} }

View File

@ -22,83 +22,90 @@ package org.schabi.newpipe.extractor;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/ */
import java.util.Arrays;
import java.util.function.Function;
/** /**
* Static data about various media formats support by NewPipe, eg mime type, extension * Static data about various media formats support by NewPipe, eg mime type, extension
*/ */
@SuppressWarnings("MethodParamPad") // we want the media format table below to be aligned
public enum MediaFormat { public enum MediaFormat {
// @formatter:off
//video and audio combined formats //video and audio combined formats
// id name suffix mime type // id name suffix mimeType
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"), MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
v3GPP (0x10, "3GPP", "3gp", "video/3gpp"), v3GPP (0x10, "3GPP", "3gp", "video/3gpp"),
WEBM (0x20, "WebM", "webm", "video/webm"), WEBM (0x20, "WebM", "webm", "video/webm"),
// audio formats // audio formats
M4A (0x100, "m4a", "m4a", "audio/mp4"), M4A (0x100, "m4a", "m4a", "audio/mp4"),
WEBMA (0x200, "WebM", "webm", "audio/webm"), WEBMA (0x200, "WebM", "webm", "audio/webm"),
MP3 (0x300, "MP3", "mp3", "audio/mpeg"), MP3 (0x300, "MP3", "mp3", "audio/mpeg"),
OPUS (0x400, "opus", "opus", "audio/opus"), OPUS (0x400, "opus", "opus", "audio/opus"),
OGG (0x500, "ogg", "ogg", "audio/ogg"), OGG (0x500, "ogg", "ogg", "audio/ogg"),
WEBMA_OPUS (0x200, "WebM Opus", "webm", "audio/webm"), WEBMA_OPUS(0x200, "WebM Opus", "webm", "audio/webm"),
// subtitles formats // subtitles formats
VTT (0x1000, "WebVTT", "vtt", "text/vtt"), VTT (0x1000, "WebVTT", "vtt", "text/vtt"),
TTML (0x2000, "Timed Text Markup Language", "ttml", "application/ttml+xml"), TTML (0x2000, "Timed Text Markup Language", "ttml", "application/ttml+xml"),
TRANSCRIPT1 (0x3000, "TranScript v1", "srv1", "text/xml"), TRANSCRIPT1(0x3000, "TranScript v1", "srv1", "text/xml"),
TRANSCRIPT2 (0x4000, "TranScript v2", "srv2", "text/xml"), TRANSCRIPT2(0x4000, "TranScript v2", "srv2", "text/xml"),
TRANSCRIPT3 (0x5000, "TranScript v3", "srv3", "text/xml"), TRANSCRIPT3(0x5000, "TranScript v3", "srv3", "text/xml"),
SRT (0x6000, "SubRip file format", "srt", "text/srt"); SRT (0x6000, "SubRip file format", "srt", "text/srt");
// @formatter:on
public final int id; public final int id;
public final String name; public final String name;
public final String suffix; public final String suffix;
public final String mimeType; public final String mimeType;
MediaFormat(int id, String name, String suffix, String mimeType) { MediaFormat(final int id, final String name, final String suffix, final String mimeType) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.suffix = suffix; this.suffix = suffix;
this.mimeType = mimeType; this.mimeType = mimeType;
} }
private static <T> T getById(final int id,
final Function<MediaFormat, T> field,
final T orElse) {
return Arrays.stream(MediaFormat.values())
.filter(mediaFormat -> mediaFormat.id == id)
.map(field)
.findFirst()
.orElse(orElse);
}
/** /**
* Return the friendly name of the media format with the supplied id * Return the friendly name of the media format with the supplied id
* *
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number. * @param id the id of the media format. Currently an arbitrary, NewPipe-specific number.
* @return the friendly name of the MediaFormat associated with this ids, * @return the friendly name of the MediaFormat associated with this ids,
* or an empty String if none match it. * or an empty String if none match it.
*/ */
public static String getNameById(int ident) { public static String getNameById(final int id) {
for (MediaFormat vf : MediaFormat.values()) { return getById(id, MediaFormat::getName, "");
if (vf.id == ident) return vf.name;
}
return "";
} }
/** /**
* Return the file extension of the media format with the supplied id * Return the file extension of the media format with the supplied id
* *
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number. * @param id the id of the media format. Currently an arbitrary, NewPipe-specific number.
* @return the file extension of the MediaFormat associated with this ids, * @return the file extension of the MediaFormat associated with this ids,
* or an empty String if none match it. * or an empty String if none match it.
*/ */
public static String getSuffixById(int ident) { public static String getSuffixById(final int id) {
for (MediaFormat vf : MediaFormat.values()) { return getById(id, MediaFormat::getSuffix, "");
if (vf.id == ident) return vf.suffix;
}
return "";
} }
/** /**
* Return the MIME type of the media format with the supplied id * Return the MIME type of the media format with the supplied id
* *
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number. * @param id the id of the media format. Currently an arbitrary, NewPipe-specific number.
* @return the MIME type of the MediaFormat associated with this ids, * @return the MIME type of the MediaFormat associated with this ids,
* or an empty String if none match it. * or an empty String if none match it.
*/ */
public static String getMimeById(int ident) { public static String getMimeById(final int id) {
for (MediaFormat vf : MediaFormat.values()) { return getById(id, MediaFormat::getMimeType, null);
if (vf.id == ident) return vf.mimeType;
}
return "";
} }
/** /**
@ -107,11 +114,11 @@ public enum MediaFormat {
* @return MediaFormat associated with this mime type, * @return MediaFormat associated with this mime type,
* or null if none match it. * or null if none match it.
*/ */
public static MediaFormat getFromMimeType(String mimeType) { public static MediaFormat getFromMimeType(final String mimeType) {
for (MediaFormat vf : MediaFormat.values()) { return Arrays.stream(MediaFormat.values())
if (vf.mimeType.equals(mimeType)) return vf; .filter(mediaFormat -> mediaFormat.mimeType.equals(mimeType))
} .findFirst()
return null; .orElse(null);
} }
/** /**
@ -120,18 +127,15 @@ public enum MediaFormat {
* @param id the id * @param id the id
* @return the id of the media format or null. * @return the id of the media format or null.
*/ */
public static MediaFormat getFormatById(int id) { public static MediaFormat getFormatById(final int id) {
for (MediaFormat vf : values()) { return getById(id, mediaFormat -> mediaFormat, null);
if (vf.id == id) return vf;
}
return null;
} }
public static MediaFormat getFromSuffix(String suffix) { public static MediaFormat getFromSuffix(final String suffix) {
for (MediaFormat vf : values()) { return Arrays.stream(MediaFormat.values())
if (vf.suffix.equals(suffix)) return vf; .filter(mediaFormat -> mediaFormat.suffix.equals(suffix))
} .findFirst()
return null; .orElse(null);
} }
/** /**

View File

@ -16,8 +16,10 @@ public class MetaInfo implements Serializable {
private List<URL> urls = new ArrayList<>(); private List<URL> urls = new ArrayList<>();
private List<String> urlTexts = new ArrayList<>(); private List<String> urlTexts = new ArrayList<>();
public MetaInfo(@Nonnull final String title, @Nonnull final Description content, public MetaInfo(@Nonnull final String title,
@Nonnull final List<URL> urls, @Nonnull final List<String> urlTexts) { @Nonnull final Description content,
@Nonnull final List<URL> urls,
@Nonnull final List<String> urlTexts) {
this.title = title; this.title = title;
this.content = content; this.content = content;
this.urls = urls; this.urls = urls;

View File

@ -50,7 +50,7 @@ public class MultiInfoItemsCollector extends InfoItemsCollector<InfoItem, InfoIt
private final ChannelInfoItemsCollector userCollector; private final ChannelInfoItemsCollector userCollector;
private final PlaylistInfoItemsCollector playlistCollector; private final PlaylistInfoItemsCollector playlistCollector;
public MultiInfoItemsCollector(int serviceId) { public MultiInfoItemsCollector(final int serviceId) {
super(serviceId); super(serviceId);
streamCollector = new StreamInfoItemsCollector(serviceId); streamCollector = new StreamInfoItemsCollector(serviceId);
userCollector = new ChannelInfoItemsCollector(serviceId); userCollector = new ChannelInfoItemsCollector(serviceId);
@ -76,7 +76,7 @@ public class MultiInfoItemsCollector extends InfoItemsCollector<InfoItem, InfoIt
} }
@Override @Override
public InfoItem extract(InfoItemExtractor extractor) throws ParsingException { public InfoItem extract(final InfoItemExtractor extractor) throws ParsingException {
// Use the corresponding collector for each item extractor type // Use the corresponding collector for each item extractor type
if (extractor instanceof StreamInfoItemExtractor) { if (extractor instanceof StreamInfoItemExtractor) {
return streamCollector.extract((StreamInfoItemExtractor) extractor); return streamCollector.extract((StreamInfoItemExtractor) extractor);

View File

@ -32,7 +32,7 @@ import java.util.List;
/** /**
* Provides access to streaming services supported by NewPipe. * Provides access to streaming services supported by NewPipe.
*/ */
public class NewPipe { public final class NewPipe {
private static Downloader downloader; private static Downloader downloader;
private static Localization preferredLocalization; private static Localization preferredLocalization;
private static ContentCountry preferredContentCountry; private static ContentCountry preferredContentCountry;
@ -40,19 +40,20 @@ public class NewPipe {
private NewPipe() { private NewPipe() {
} }
public static void init(Downloader d) { public static void init(final Downloader d) {
downloader = d; downloader = d;
preferredLocalization = Localization.DEFAULT; preferredLocalization = Localization.DEFAULT;
preferredContentCountry = ContentCountry.DEFAULT; preferredContentCountry = ContentCountry.DEFAULT;
} }
public static void init(Downloader d, Localization l) { public static void init(final Downloader d, final Localization l) {
downloader = d; downloader = d;
preferredLocalization = l; preferredLocalization = l;
preferredContentCountry = l.getCountryCode().isEmpty() ? ContentCountry.DEFAULT : new ContentCountry(l.getCountryCode()); preferredContentCountry = l.getCountryCode().isEmpty()
? ContentCountry.DEFAULT : new ContentCountry(l.getCountryCode());
} }
public static void init(Downloader d, Localization l, ContentCountry c) { public static void init(final Downloader d, final Localization l, final ContentCountry c) {
downloader = d; downloader = d;
preferredLocalization = l; preferredLocalization = l;
preferredContentCountry = c; preferredContentCountry = c;
@ -70,26 +71,24 @@ public class NewPipe {
return ServiceList.all(); return ServiceList.all();
} }
public static StreamingService getService(int serviceId) throws ExtractionException { public static StreamingService getService(final int serviceId) throws ExtractionException {
for (StreamingService service : ServiceList.all()) { return ServiceList.all().stream()
if (service.getServiceId() == serviceId) { .filter(service -> service.getServiceId() == serviceId)
return service; .findFirst()
} .orElseThrow(() -> new ExtractionException(
} "There's no service with the id = \"" + serviceId + "\""));
throw new ExtractionException("There's no service with the id = \"" + serviceId + "\"");
} }
public static StreamingService getService(String serviceName) throws ExtractionException { public static StreamingService getService(final String serviceName) throws ExtractionException {
for (StreamingService service : ServiceList.all()) { return ServiceList.all().stream()
if (service.getServiceInfo().getName().equals(serviceName)) { .filter(service -> service.getServiceInfo().getName().equals(serviceName))
return service; .findFirst()
} .orElseThrow(() -> new ExtractionException(
} "There's no service with the name = \"" + serviceName + "\""));
throw new ExtractionException("There's no service with the name = \"" + serviceName + "\"");
} }
public static StreamingService getServiceByUrl(String url) throws ExtractionException { public static StreamingService getServiceByUrl(final String url) throws ExtractionException {
for (StreamingService service : ServiceList.all()) { for (final StreamingService service : ServiceList.all()) {
if (service.getLinkTypeByUrl(url) != StreamingService.LinkType.NONE) { if (service.getLinkTypeByUrl(url) != StreamingService.LinkType.NONE) {
return service; return service;
} }
@ -97,18 +96,18 @@ public class NewPipe {
throw new ExtractionException("No service can handle the url = \"" + url + "\""); throw new ExtractionException("No service can handle the url = \"" + url + "\"");
} }
public static int getIdOfService(String serviceName) { public static int getIdOfService(final String serviceName) {
try { try {
return getService(serviceName).getServiceId(); return getService(serviceName).getServiceId();
} catch (ExtractionException ignored) { } catch (final ExtractionException ignored) {
return -1; return -1;
} }
} }
public static String getNameOfService(int id) { public static String getNameOfService(final int id) {
try { try {
return getService(id).getServiceInfo().getName(); return getService(id).getServiceInfo().getName();
} catch (Exception e) { } catch (final Exception e) {
System.err.println("Service id not known"); System.err.println("Service id not known");
e.printStackTrace(); e.printStackTrace();
return "<unknown>"; return "<unknown>";
@ -119,19 +118,21 @@ public class NewPipe {
// Localization // Localization
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public static void setupLocalization(Localization preferredLocalization) { public static void setupLocalization(final Localization thePreferredLocalization) {
setupLocalization(preferredLocalization, null); setupLocalization(thePreferredLocalization, null);
} }
public static void setupLocalization(Localization preferredLocalization, @Nullable ContentCountry preferredContentCountry) { public static void setupLocalization(
NewPipe.preferredLocalization = preferredLocalization; final Localization thePreferredLocalization,
@Nullable final ContentCountry thePreferredContentCountry) {
NewPipe.preferredLocalization = thePreferredLocalization;
if (preferredContentCountry != null) { if (thePreferredContentCountry != null) {
NewPipe.preferredContentCountry = preferredContentCountry; NewPipe.preferredContentCountry = thePreferredContentCountry;
} else { } else {
NewPipe.preferredContentCountry = preferredLocalization.getCountryCode().isEmpty() NewPipe.preferredContentCountry = thePreferredLocalization.getCountryCode().isEmpty()
? ContentCountry.DEFAULT ? ContentCountry.DEFAULT
: new ContentCountry(preferredLocalization.getCountryCode()); : new ContentCountry(thePreferredLocalization.getCountryCode());
} }
} }
@ -140,7 +141,7 @@ public class NewPipe {
return preferredLocalization == null ? Localization.DEFAULT : preferredLocalization; return preferredLocalization == null ? Localization.DEFAULT : preferredLocalization;
} }
public static void setPreferredLocalization(Localization preferredLocalization) { public static void setPreferredLocalization(final Localization preferredLocalization) {
NewPipe.preferredLocalization = preferredLocalization; NewPipe.preferredLocalization = preferredLocalization;
} }
@ -149,7 +150,7 @@ public class NewPipe {
return preferredContentCountry == null ? ContentCountry.DEFAULT : preferredContentCountry; return preferredContentCountry == null ? ContentCountry.DEFAULT : preferredContentCountry;
} }
public static void setPreferredContentCountry(ContentCountry preferredContentCountry) { public static void setPreferredContentCountry(final ContentCountry preferredContentCountry) {
NewPipe.preferredContentCountry = preferredContentCountry; NewPipe.preferredContentCountry = preferredContentCountry;
} }
} }

View File

@ -17,8 +17,11 @@ public class Page implements Serializable {
@Nullable @Nullable
private final byte[] body; private final byte[] body;
public Page(final String url, final String id, final List<String> ids, public Page(final String url,
final Map<String, String> cookies, @Nullable final byte[] body) { final String id,
final List<String> ids,
final Map<String, String> cookies,
@Nullable final byte[] body) {
this.url = url; this.url = url;
this.id = id; this.id = id;
this.ids = ids; this.ids = ids;

View File

@ -31,6 +31,7 @@ import java.util.List;
/** /**
* A list of supported services. * A list of supported services.
*/ */
@SuppressWarnings({"ConstantName", "InnerAssignment"}) // keep unusual names and inner assignments
public final class ServiceList { public final class ServiceList {
private ServiceList() { private ServiceList() {
//no instance //no instance

View File

@ -6,7 +6,12 @@ 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.feed.FeedExtractor; import org.schabi.newpipe.extractor.feed.FeedExtractor;
import org.schabi.newpipe.extractor.kiosk.KioskList; import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.extractor.linkhandler.*; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.localization.TimeAgoParser; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
@ -55,7 +60,7 @@ public abstract class StreamingService {
* @param name the name of the service * @param name the name of the service
* @param mediaCapabilities the type of media this service can handle * @param mediaCapabilities the type of media this service can handle
*/ */
public ServiceInfo(String name, List<MediaCapability> mediaCapabilities) { public ServiceInfo(final String name, final List<MediaCapability> mediaCapabilities) {
this.name = name; this.name = name;
this.mediaCapabilities = Collections.unmodifiableList(mediaCapabilities); this.mediaCapabilities = Collections.unmodifiableList(mediaCapabilities);
} }
@ -74,8 +79,8 @@ public abstract class StreamingService {
} }
/** /**
* LinkType will be used to determine which type of URL you are handling, and therefore which part * LinkType will be used to determine which type of URL you are handling, and therefore which
* of NewPipe should handle a certain URL. * part of NewPipe should handle a certain URL.
*/ */
public enum LinkType { public enum LinkType {
NONE, NONE,
@ -90,14 +95,15 @@ public abstract class StreamingService {
/** /**
* Creates a new Streaming service. * Creates a new Streaming service.
* If you Implement one do not set id within your implementation of this extractor, instead * If you Implement one do not set id within your implementation of this extractor, instead
* set the id when you put the extractor into * set the id when you put the extractor into {@link ServiceList}
* <a href="https://teamnewpipe.github.io/NewPipeExtractor/javadoc/org/schabi/newpipe/extractor/ServiceList.html">ServiceList</a>.
* All other parameters can be set directly from the overriding constructor. * All other parameters can be set directly from the overriding constructor.
* @param id the number of the service to identify him within the NewPipe frontend * @param id the number of the service to identify him within the NewPipe frontend
* @param name the name of the service * @param name the name of the service
* @param capabilities the type of media this service can handle * @param capabilities the type of media this service can handle
*/ */
public StreamingService(int id, String name, List<ServiceInfo.MediaCapability> capabilities) { public StreamingService(final int id,
final String name,
final List<ServiceInfo.MediaCapability> capabilities) {
this.serviceId = id; this.serviceId = id;
this.serviceInfo = new ServiceInfo(name, capabilities); this.serviceInfo = new ServiceInfo(name, capabilities);
} }
@ -172,22 +178,21 @@ public abstract class StreamingService {
public abstract SubscriptionExtractor getSubscriptionExtractor(); public abstract SubscriptionExtractor getSubscriptionExtractor();
/** /**
* This method decides which strategy will be chosen to fetch the feed. In YouTube, for example, a separate feed * This method decides which strategy will be chosen to fetch the feed. In YouTube, for example,
* exists which is lightweight and made specifically to be used like this. * a separate feed exists which is lightweight and made specifically to be used like this.
* <p> * <p>
* In services which there's no other way to retrieve them, null should be returned. * In services which there's no other way to retrieve them, null should be returned.
* *
* @return a {@link FeedExtractor} instance or null. * @return a {@link FeedExtractor} instance or null.
*/ */
@Nullable @Nullable
public FeedExtractor getFeedExtractor(String url) throws ExtractionException { public FeedExtractor getFeedExtractor(final String url) throws ExtractionException {
return null; return null;
} }
/** /**
* Must create a new instance of a KioskList implementation. * Must create a new instance of a KioskList implementation.
* @return a new KioskList instance * @return a new KioskList instance
* @throws ExtractionException
*/ */
public abstract KioskList getKioskList() throws ExtractionException; public abstract KioskList getKioskList() throws ExtractionException;
@ -195,49 +200,52 @@ public abstract class StreamingService {
* Must create a new instance of a ChannelExtractor implementation. * Must create a new instance of a ChannelExtractor implementation.
* @param linkHandler is pointing to the channel which should be handled by this new instance. * @param linkHandler is pointing to the channel which should be handled by this new instance.
* @return a new ChannelExtractor * @return a new ChannelExtractor
* @throws ExtractionException
*/ */
public abstract ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) throws ExtractionException; public abstract ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler)
throws ExtractionException;
/** /**
* Must crete a new instance of a PlaylistExtractor implementation. * Must crete a new instance of a PlaylistExtractor implementation.
* @param linkHandler is pointing to the playlist which should be handled by this new instance. * @param linkHandler is pointing to the playlist which should be handled by this new instance.
* @return a new PlaylistExtractor * @return a new PlaylistExtractor
* @throws ExtractionException
*/ */
public abstract PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) throws ExtractionException; public abstract PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler)
throws ExtractionException;
/** /**
* Must create a new instance of a StreamExtractor implementation. * Must create a new instance of a StreamExtractor implementation.
* @param linkHandler is pointing to the stream which should be handled by this new instance. * @param linkHandler is pointing to the stream which should be handled by this new instance.
* @return a new StreamExtractor * @return a new StreamExtractor
* @throws ExtractionException
*/ */
public abstract StreamExtractor getStreamExtractor(LinkHandler linkHandler) throws ExtractionException; public abstract StreamExtractor getStreamExtractor(LinkHandler linkHandler)
throws ExtractionException;
public abstract CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) throws ExtractionException; public abstract CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler)
throws ExtractionException;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Extractors without link handler // Extractors without link handler
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public SearchExtractor getSearchExtractor(String query, public SearchExtractor getSearchExtractor(final String query,
List<String> contentFilter, final List<String> contentFilter,
String sortFilter) throws ExtractionException { final String sortFilter) throws ExtractionException {
return getSearchExtractor(getSearchQHFactory() return getSearchExtractor(getSearchQHFactory()
.fromQuery(query, contentFilter, sortFilter)); .fromQuery(query, contentFilter, sortFilter));
} }
public ChannelExtractor getChannelExtractor(String id, public ChannelExtractor getChannelExtractor(final String id,
List<String> contentFilter, final List<String> contentFilter,
String sortFilter) throws ExtractionException { final String sortFilter)
throws ExtractionException {
return getChannelExtractor(getChannelLHFactory() return getChannelExtractor(getChannelLHFactory()
.fromQuery(id, contentFilter, sortFilter)); .fromQuery(id, contentFilter, sortFilter));
} }
public PlaylistExtractor getPlaylistExtractor(String id, public PlaylistExtractor getPlaylistExtractor(final String id,
List<String> contentFilter, final List<String> contentFilter,
String sortFilter) throws ExtractionException { final String sortFilter)
throws ExtractionException {
return getPlaylistExtractor(getPlaylistLHFactory() return getPlaylistExtractor(getPlaylistLHFactory()
.fromQuery(id, contentFilter, sortFilter)); .fromQuery(id, contentFilter, sortFilter));
} }
@ -246,28 +254,28 @@ public abstract class StreamingService {
// Short extractors overloads // Short extractors overloads
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public SearchExtractor getSearchExtractor(String query) throws ExtractionException { public SearchExtractor getSearchExtractor(final String query) throws ExtractionException {
return getSearchExtractor(getSearchQHFactory().fromQuery(query)); return getSearchExtractor(getSearchQHFactory().fromQuery(query));
} }
public ChannelExtractor getChannelExtractor(String url) throws ExtractionException { public ChannelExtractor getChannelExtractor(final String url) throws ExtractionException {
return getChannelExtractor(getChannelLHFactory().fromUrl(url)); return getChannelExtractor(getChannelLHFactory().fromUrl(url));
} }
public PlaylistExtractor getPlaylistExtractor(String url) throws ExtractionException { public PlaylistExtractor getPlaylistExtractor(final String url) throws ExtractionException {
return getPlaylistExtractor(getPlaylistLHFactory().fromUrl(url)); return getPlaylistExtractor(getPlaylistLHFactory().fromUrl(url));
} }
public StreamExtractor getStreamExtractor(String url) throws ExtractionException { public StreamExtractor getStreamExtractor(final String url) throws ExtractionException {
return getStreamExtractor(getStreamLHFactory().fromUrl(url)); return getStreamExtractor(getStreamLHFactory().fromUrl(url));
} }
public CommentsExtractor getCommentsExtractor(String url) throws ExtractionException { public CommentsExtractor getCommentsExtractor(final String url) throws ExtractionException {
ListLinkHandlerFactory llhf = getCommentsLHFactory(); final ListLinkHandlerFactory listLinkHandlerFactory = getCommentsLHFactory();
if (llhf == null) { if (listLinkHandlerFactory == null) {
return null; return null;
} }
return getCommentsExtractor(llhf.fromUrl(url)); return getCommentsExtractor(listLinkHandlerFactory.fromUrl(url));
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -320,7 +328,8 @@ public abstract class StreamingService {
* the user prefer (using {@link NewPipe#getPreferredLocalization()}), then it will: * the user prefer (using {@link NewPipe#getPreferredLocalization()}), then it will:
* <ul> * <ul>
* <li>Check if the exactly localization is supported by this service.</li> * <li>Check if the exactly localization is supported by this service.</li>
* <li>If not, check if a less specific localization is available, using only the language code.</li> * <li>If not, check if a less specific localization is available, using only the language
* code.</li>
* <li>Fallback to the {@link Localization#DEFAULT default} localization.</li> * <li>Fallback to the {@link Localization#DEFAULT default} localization.</li>
* </ul> * </ul>
*/ */
@ -333,8 +342,9 @@ public abstract class StreamingService {
} }
// Fallback to the first supported language that matches the preferred language // Fallback to the first supported language that matches the preferred language
for (Localization supportedLanguage : getSupportedLocalizations()) { for (final Localization supportedLanguage : getSupportedLocalizations()) {
if (supportedLanguage.getLanguageCode().equals(preferredLocalization.getLanguageCode())) { if (supportedLanguage.getLanguageCode()
.equals(preferredLocalization.getLanguageCode())) {
return supportedLanguage; return supportedLanguage;
} }
} }
@ -343,8 +353,8 @@ public abstract class StreamingService {
} }
/** /**
* Returns the country that should be used to fetch content in this service. It will get which country * Returns the country that should be used to fetch content in this service. It will get which
* the user prefer (using {@link NewPipe#getPreferredContentCountry()}), then it will: * country the user prefer (using {@link NewPipe#getPreferredContentCountry()}), then it will:
* <ul> * <ul>
* <li>Check if the country is supported by this service.</li> * <li>Check if the country is supported by this service.</li>
* <li>If not, fallback to the {@link ContentCountry#DEFAULT default} country.</li> * <li>If not, fallback to the {@link ContentCountry#DEFAULT default} country.</li>
@ -361,14 +371,15 @@ public abstract class StreamingService {
} }
/** /**
* Get an instance of the time ago parser using the patterns related to the passed localization.<br> * Get an instance of the time ago parser using the patterns related to the passed localization.
* <br> * <br><br>
* Just like {@link #getLocalization()}, it will also try to fallback to a less specific localization if * Just like {@link #getLocalization()}, it will also try to fallback to a less specific
* the exact one is not available/supported. * localization if the exact one is not available/supported.
* *
* @throws IllegalArgumentException if the localization is not supported (parsing patterns are not present). * @throws IllegalArgumentException if the localization is not supported (parsing patterns are
* not present).
*/ */
public TimeAgoParser getTimeAgoParser(Localization localization) { public TimeAgoParser getTimeAgoParser(final Localization localization) {
final TimeAgoParser targetParser = TimeAgoPatternsManager.getTimeAgoParserFor(localization); final TimeAgoParser targetParser = TimeAgoPatternsManager.getTimeAgoParserFor(localization);
if (targetParser != null) { if (targetParser != null) {
@ -376,15 +387,18 @@ public abstract class StreamingService {
} }
if (!localization.getCountryCode().isEmpty()) { if (!localization.getCountryCode().isEmpty()) {
final Localization lessSpecificLocalization = new Localization(localization.getLanguageCode()); final Localization lessSpecificLocalization
final TimeAgoParser lessSpecificParser = TimeAgoPatternsManager.getTimeAgoParserFor(lessSpecificLocalization); = new Localization(localization.getLanguageCode());
final TimeAgoParser lessSpecificParser
= TimeAgoPatternsManager.getTimeAgoParserFor(lessSpecificLocalization);
if (lessSpecificParser != null) { if (lessSpecificParser != null) {
return lessSpecificParser; return lessSpecificParser;
} }
} }
throw new IllegalArgumentException("Localization is not supported (\"" + localization.toString() + "\")"); throw new IllegalArgumentException(
"Localization is not supported (\"" + localization + "\")");
} }
} }

View File

@ -30,7 +30,7 @@ public abstract class ChannelExtractor extends ListExtractor<StreamInfoItem> {
public static final long UNKNOWN_SUBSCRIBER_COUNT = -1; public static final long UNKNOWN_SUBSCRIBER_COUNT = -1;
public ChannelExtractor(StreamingService service, ListLinkHandler linkHandler) { public ChannelExtractor(final StreamingService service, final ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }

View File

@ -34,27 +34,36 @@ import java.io.IOException;
public class ChannelInfo extends ListInfo<StreamInfoItem> { public class ChannelInfo extends ListInfo<StreamInfoItem> {
public ChannelInfo(int serviceId, String id, String url, String originalUrl, String name, ListLinkHandler listLinkHandler) { public ChannelInfo(final int serviceId,
super(serviceId, id, url, originalUrl, name, listLinkHandler.getContentFilters(), listLinkHandler.getSortFilter()); final String id,
final String url,
final String originalUrl,
final String name,
final ListLinkHandler listLinkHandler) {
super(serviceId, id, url, originalUrl, name, listLinkHandler.getContentFilters(),
listLinkHandler.getSortFilter());
} }
public static ChannelInfo getInfo(String url) throws IOException, ExtractionException { public static ChannelInfo getInfo(final String url) throws IOException, ExtractionException {
return getInfo(NewPipe.getServiceByUrl(url), url); return getInfo(NewPipe.getServiceByUrl(url), url);
} }
public static ChannelInfo getInfo(StreamingService service, String url) throws IOException, ExtractionException { public static ChannelInfo getInfo(final StreamingService service, final String url)
ChannelExtractor extractor = service.getChannelExtractor(url); throws IOException, ExtractionException {
final ChannelExtractor extractor = service.getChannelExtractor(url);
extractor.fetchPage(); extractor.fetchPage();
return getInfo(extractor); return getInfo(extractor);
} }
public static InfoItemsPage<StreamInfoItem> getMoreItems(StreamingService service, public static InfoItemsPage<StreamInfoItem> getMoreItems(final StreamingService service,
String url, final String url,
Page page) throws IOException, ExtractionException { final Page page)
throws IOException, ExtractionException {
return service.getChannelExtractor(url).getPage(page); return service.getChannelExtractor(url).getPage(page);
} }
public static ChannelInfo getInfo(ChannelExtractor extractor) throws IOException, ExtractionException { public static ChannelInfo getInfo(final ChannelExtractor extractor)
throws IOException, ExtractionException {
final int serviceId = extractor.getServiceId(); final int serviceId = extractor.getServiceId();
final String id = extractor.getId(); final String id = extractor.getId();
@ -62,60 +71,62 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
final String originalUrl = extractor.getOriginalUrl(); final String originalUrl = extractor.getOriginalUrl();
final String name = extractor.getName(); final String name = extractor.getName();
final ChannelInfo info = new ChannelInfo(serviceId, id, url, originalUrl, name, extractor.getLinkHandler()); final ChannelInfo info =
new ChannelInfo(serviceId, id, url, originalUrl, name, extractor.getLinkHandler());
try { try {
info.setAvatarUrl(extractor.getAvatarUrl()); info.setAvatarUrl(extractor.getAvatarUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setBannerUrl(extractor.getBannerUrl()); info.setBannerUrl(extractor.getBannerUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setFeedUrl(extractor.getFeedUrl()); info.setFeedUrl(extractor.getFeedUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
final InfoItemsPage<StreamInfoItem> itemsPage = ExtractorHelper.getItemsPageOrLogError(info, extractor); final InfoItemsPage<StreamInfoItem> itemsPage =
ExtractorHelper.getItemsPageOrLogError(info, extractor);
info.setRelatedItems(itemsPage.getItems()); info.setRelatedItems(itemsPage.getItems());
info.setNextPage(itemsPage.getNextPage()); info.setNextPage(itemsPage.getNextPage());
try { try {
info.setSubscriberCount(extractor.getSubscriberCount()); info.setSubscriberCount(extractor.getSubscriberCount());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setDescription(extractor.getDescription()); info.setDescription(extractor.getDescription());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setParentChannelName(extractor.getParentChannelName()); info.setParentChannelName(extractor.getParentChannelName());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setParentChannelUrl(extractor.getParentChannelUrl()); info.setParentChannelUrl(extractor.getParentChannelUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setParentChannelAvatarUrl(extractor.getParentChannelAvatarUrl()); info.setParentChannelAvatarUrl(extractor.getParentChannelAvatarUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setVerified(extractor.isVerified()); info.setVerified(extractor.isVerified());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
@ -137,7 +148,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return parentChannelName; return parentChannelName;
} }
public void setParentChannelName(String parentChannelName) { public void setParentChannelName(final String parentChannelName) {
this.parentChannelName = parentChannelName; this.parentChannelName = parentChannelName;
} }
@ -145,7 +156,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return parentChannelUrl; return parentChannelUrl;
} }
public void setParentChannelUrl(String parentChannelUrl) { public void setParentChannelUrl(final String parentChannelUrl) {
this.parentChannelUrl = parentChannelUrl; this.parentChannelUrl = parentChannelUrl;
} }
@ -153,7 +164,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return parentChannelAvatarUrl; return parentChannelAvatarUrl;
} }
public void setParentChannelAvatarUrl(String parentChannelAvatarUrl) { public void setParentChannelAvatarUrl(final String parentChannelAvatarUrl) {
this.parentChannelAvatarUrl = parentChannelAvatarUrl; this.parentChannelAvatarUrl = parentChannelAvatarUrl;
} }
@ -161,7 +172,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return avatarUrl; return avatarUrl;
} }
public void setAvatarUrl(String avatarUrl) { public void setAvatarUrl(final String avatarUrl) {
this.avatarUrl = avatarUrl; this.avatarUrl = avatarUrl;
} }
@ -169,7 +180,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return bannerUrl; return bannerUrl;
} }
public void setBannerUrl(String bannerUrl) { public void setBannerUrl(final String bannerUrl) {
this.bannerUrl = bannerUrl; this.bannerUrl = bannerUrl;
} }
@ -177,7 +188,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return feedUrl; return feedUrl;
} }
public void setFeedUrl(String feedUrl) { public void setFeedUrl(final String feedUrl) {
this.feedUrl = feedUrl; this.feedUrl = feedUrl;
} }
@ -185,7 +196,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return subscriberCount; return subscriberCount;
} }
public void setSubscriberCount(long subscriberCount) { public void setSubscriberCount(final long subscriberCount) {
this.subscriberCount = subscriberCount; this.subscriberCount = subscriberCount;
} }
@ -193,7 +204,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return description; return description;
} }
public void setDescription(String description) { public void setDescription(final String description) {
this.description = description; this.description = description;
} }
@ -201,7 +212,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return donationLinks; return donationLinks;
} }
public void setDonationLinks(String[] donationLinks) { public void setDonationLinks(final String[] donationLinks) {
this.donationLinks = donationLinks; this.donationLinks = donationLinks;
} }
@ -209,7 +220,7 @@ public class ChannelInfo extends ListInfo<StreamInfoItem> {
return verified; return verified;
} }
public void setVerified(boolean verified) { public void setVerified(final boolean verified) {
this.verified = verified; this.verified = verified;
} }
} }

View File

@ -29,7 +29,7 @@ public class ChannelInfoItem extends InfoItem {
private long streamCount = -1; private long streamCount = -1;
private boolean verified = false; private boolean verified = false;
public ChannelInfoItem(int serviceId, String url, String name) { public ChannelInfoItem(final int serviceId, final String url, final String name) {
super(InfoType.CHANNEL, serviceId, url, name); super(InfoType.CHANNEL, serviceId, url, name);
} }
@ -37,7 +37,7 @@ public class ChannelInfoItem extends InfoItem {
return description; return description;
} }
public void setDescription(String description) { public void setDescription(final String description) {
this.description = description; this.description = description;
} }
@ -45,23 +45,23 @@ public class ChannelInfoItem extends InfoItem {
return subscriberCount; return subscriberCount;
} }
public void setSubscriberCount(long subscriber_count) { public void setSubscriberCount(final long subscriberCount) {
this.subscriberCount = subscriber_count; this.subscriberCount = subscriberCount;
} }
public long getStreamCount() { public long getStreamCount() {
return streamCount; return streamCount;
} }
public void setStreamCount(long stream_count) { public void setStreamCount(final long streamCount) {
this.streamCount = stream_count; this.streamCount = streamCount;
} }
public boolean isVerified() { public boolean isVerified() {
return verified; return verified;
} }
public void setVerified(boolean verified) { public void setVerified(final boolean verified) {
this.verified = verified; this.verified = verified;
} }
} }

View File

@ -23,45 +23,42 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/ */
public class ChannelInfoItemsCollector extends InfoItemsCollector<ChannelInfoItem, ChannelInfoItemExtractor> { public final class ChannelInfoItemsCollector
public ChannelInfoItemsCollector(int serviceId) { extends InfoItemsCollector<ChannelInfoItem, ChannelInfoItemExtractor> {
public ChannelInfoItemsCollector(final int serviceId) {
super(serviceId); super(serviceId);
} }
@Override @Override
public ChannelInfoItem extract(ChannelInfoItemExtractor extractor) throws ParsingException { public ChannelInfoItem extract(final ChannelInfoItemExtractor extractor)
// important information throws ParsingException {
int serviceId = getServiceId(); final ChannelInfoItem resultItem = new ChannelInfoItem(
String name = extractor.getName(); getServiceId(), extractor.getUrl(), extractor.getName());
String url = extractor.getUrl();
ChannelInfoItem resultItem = new ChannelInfoItem(serviceId, url, name);
// optional information // optional information
try { try {
resultItem.setSubscriberCount(extractor.getSubscriberCount()); resultItem.setSubscriberCount(extractor.getSubscriberCount());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setStreamCount(extractor.getStreamCount()); resultItem.setStreamCount(extractor.getStreamCount());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setThumbnailUrl(extractor.getThumbnailUrl()); resultItem.setThumbnailUrl(extractor.getThumbnailUrl());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setDescription(extractor.getDescription()); resultItem.setDescription(extractor.getDescription());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setVerified(extractor.isVerified()); resultItem.setVerified(extractor.isVerified());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }

View File

@ -11,7 +11,7 @@ import org.schabi.newpipe.extractor.utils.ExtractorHelper;
import java.io.IOException; import java.io.IOException;
public class CommentsInfo extends ListInfo<CommentsInfoItem> { public final class CommentsInfo extends ListInfo<CommentsInfoItem> {
private CommentsInfo( private CommentsInfo(
final int serviceId, final int serviceId,
@ -56,7 +56,8 @@ public class CommentsInfo extends ListInfo<CommentsInfoItem> {
public static InfoItemsPage<CommentsInfoItem> getMoreItems( public static InfoItemsPage<CommentsInfoItem> getMoreItems(
final CommentsInfo commentsInfo, final CommentsInfo commentsInfo,
final Page page) throws ExtractionException, IOException { final Page page) throws ExtractionException, IOException {
return getMoreItems(NewPipe.getService(commentsInfo.getServiceId()), commentsInfo.getUrl(), page); return getMoreItems(NewPipe.getService(commentsInfo.getServiceId()), commentsInfo.getUrl(),
page);
} }
public static InfoItemsPage<CommentsInfoItem> getMoreItems( public static InfoItemsPage<CommentsInfoItem> getMoreItems(
@ -86,7 +87,7 @@ public class CommentsInfo extends ListInfo<CommentsInfoItem> {
/** /**
* @apiNote Warning: This method is experimental and may get removed in a future release. * @apiNote Warning: This method is experimental and may get removed in a future release.
* @return <code>true</code> if the comments are disabled otherwise <code>false</code> (default) * @return {@code true} if the comments are disabled otherwise {@code false} (default)
* @see CommentsExtractor#isCommentsDisabled() * @see CommentsExtractor#isCommentsDisabled()
*/ */
public boolean isCommentsDisabled() { public boolean isCommentsDisabled() {
@ -95,7 +96,7 @@ public class CommentsInfo extends ListInfo<CommentsInfoItem> {
/** /**
* @apiNote Warning: This method is experimental and may get removed in a future release. * @apiNote Warning: This method is experimental and may get removed in a future release.
* @param commentsDisabled <code>true</code> if the comments are disabled otherwise <code>false</code> * @param commentsDisabled {@code true} if the comments are disabled otherwise {@code false}
*/ */
public void setCommentsDisabled(final boolean commentsDisabled) { public void setCommentsDisabled(final boolean commentsDisabled) {
this.commentsDisabled = commentsDisabled; this.commentsDisabled = commentsDisabled;

View File

@ -28,7 +28,7 @@ public class CommentsInfoItem extends InfoItem {
public static final int NO_LIKE_COUNT = -1; public static final int NO_LIKE_COUNT = -1;
public static final int NO_STREAM_POSITION = -1; public static final int NO_STREAM_POSITION = -1;
public CommentsInfoItem(int serviceId, String url, String name) { public CommentsInfoItem(final int serviceId, final String url, final String name) {
super(InfoType.COMMENT, serviceId, url, name); super(InfoType.COMMENT, serviceId, url, name);
} }
@ -36,7 +36,7 @@ public class CommentsInfoItem extends InfoItem {
return commentId; return commentId;
} }
public void setCommentId(String commentId) { public void setCommentId(final String commentId) {
this.commentId = commentId; this.commentId = commentId;
} }
@ -44,7 +44,7 @@ public class CommentsInfoItem extends InfoItem {
return commentText; return commentText;
} }
public void setCommentText(String commentText) { public void setCommentText(final String commentText) {
this.commentText = commentText; this.commentText = commentText;
} }
@ -52,7 +52,7 @@ public class CommentsInfoItem extends InfoItem {
return uploaderName; return uploaderName;
} }
public void setUploaderName(String uploaderName) { public void setUploaderName(final String uploaderName) {
this.uploaderName = uploaderName; this.uploaderName = uploaderName;
} }
@ -60,7 +60,7 @@ public class CommentsInfoItem extends InfoItem {
return uploaderAvatarUrl; return uploaderAvatarUrl;
} }
public void setUploaderAvatarUrl(String uploaderAvatarUrl) { public void setUploaderAvatarUrl(final String uploaderAvatarUrl) {
this.uploaderAvatarUrl = uploaderAvatarUrl; this.uploaderAvatarUrl = uploaderAvatarUrl;
} }
@ -68,7 +68,7 @@ public class CommentsInfoItem extends InfoItem {
return uploaderUrl; return uploaderUrl;
} }
public void setUploaderUrl(String uploaderUrl) { public void setUploaderUrl(final String uploaderUrl) {
this.uploaderUrl = uploaderUrl; this.uploaderUrl = uploaderUrl;
} }
@ -76,7 +76,7 @@ public class CommentsInfoItem extends InfoItem {
return textualUploadDate; return textualUploadDate;
} }
public void setTextualUploadDate(String textualUploadDate) { public void setTextualUploadDate(final String textualUploadDate) {
this.textualUploadDate = textualUploadDate; this.textualUploadDate = textualUploadDate;
} }
@ -85,7 +85,7 @@ public class CommentsInfoItem extends InfoItem {
return uploadDate; return uploadDate;
} }
public void setUploadDate(@Nullable DateWrapper uploadDate) { public void setUploadDate(@Nullable final DateWrapper uploadDate) {
this.uploadDate = uploadDate; this.uploadDate = uploadDate;
} }
@ -97,7 +97,7 @@ public class CommentsInfoItem extends InfoItem {
return likeCount; return likeCount;
} }
public void setLikeCount(int likeCount) { public void setLikeCount(final int likeCount) {
this.likeCount = likeCount; this.likeCount = likeCount;
} }
@ -105,11 +105,11 @@ public class CommentsInfoItem extends InfoItem {
return textualLikeCount; return textualLikeCount;
} }
public void setTextualLikeCount(String textualLikeCount) { public void setTextualLikeCount(final String textualLikeCount) {
this.textualLikeCount = textualLikeCount; this.textualLikeCount = textualLikeCount;
} }
public void setHeartedByUploader(boolean isHeartedByUploader) { public void setHeartedByUploader(final boolean isHeartedByUploader) {
this.heartedByUploader = isHeartedByUploader; this.heartedByUploader = isHeartedByUploader;
} }
@ -121,11 +121,11 @@ public class CommentsInfoItem extends InfoItem {
return pinned; return pinned;
} }
public void setPinned(boolean pinned) { public void setPinned(final boolean pinned) {
this.pinned = pinned; this.pinned = pinned;
} }
public void setUploaderVerified(boolean uploaderVerified) { public void setUploaderVerified(final boolean uploaderVerified) {
this.uploaderVerified = uploaderVerified; this.uploaderVerified = uploaderVerified;
} }
@ -146,7 +146,12 @@ public class CommentsInfoItem extends InfoItem {
return streamPosition; return streamPosition;
} }
public void setReplies(@Nullable Page replies) { this.replies = replies; } public void setReplies(@Nullable final Page replies) {
this.replies = replies;
}
public Page getReplies() { return this.replies; } @Nullable
public Page getReplies() {
return this.replies;
}
} }

View File

@ -18,7 +18,8 @@ public interface CommentsInfoItemExtractor extends InfoItemExtractor {
* *
* <br> * <br>
* *
* NOTE: Currently only implemented for YT {@link YoutubeCommentsInfoItemExtractor#getLikeCount()} * NOTE: Currently only implemented for YT {@link
* YoutubeCommentsInfoItemExtractor#getLikeCount()}
* with limitations (only approximate like count is returned) * with limitations (only approximate like count is returned)
* *
* @see StreamExtractor#getLikeCount() * @see StreamExtractor#getLikeCount()

View File

@ -1,101 +1,97 @@
package org.schabi.newpipe.extractor.comments; package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.InfoItemsCollector; import org.schabi.newpipe.extractor.InfoItemsCollector;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class CommentsInfoItemsCollector extends InfoItemsCollector<CommentsInfoItem, CommentsInfoItemExtractor> { public final class CommentsInfoItemsCollector
extends InfoItemsCollector<CommentsInfoItem, CommentsInfoItemExtractor> {
public CommentsInfoItemsCollector(int serviceId) { public CommentsInfoItemsCollector(final int serviceId) {
super(serviceId); super(serviceId);
} }
@Override @Override
public CommentsInfoItem extract(CommentsInfoItemExtractor extractor) throws ParsingException { public CommentsInfoItem extract(final CommentsInfoItemExtractor extractor)
throws ParsingException {
// important information final CommentsInfoItem resultItem = new CommentsInfoItem(
int serviceId = getServiceId(); getServiceId(), extractor.getUrl(), extractor.getName());
String url = extractor.getUrl();
String name = extractor.getName();
CommentsInfoItem resultItem = new CommentsInfoItem(serviceId, url, name);
// optional information // optional information
try { try {
resultItem.setCommentId(extractor.getCommentId()); resultItem.setCommentId(extractor.getCommentId());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setCommentText(extractor.getCommentText()); resultItem.setCommentText(extractor.getCommentText());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setUploaderName(extractor.getUploaderName()); resultItem.setUploaderName(extractor.getUploaderName());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl()); resultItem.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setUploaderUrl(extractor.getUploaderUrl()); resultItem.setUploaderUrl(extractor.getUploaderUrl());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setTextualUploadDate(extractor.getTextualUploadDate()); resultItem.setTextualUploadDate(extractor.getTextualUploadDate());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setUploadDate(extractor.getUploadDate()); resultItem.setUploadDate(extractor.getUploadDate());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setLikeCount(extractor.getLikeCount()); resultItem.setLikeCount(extractor.getLikeCount());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setTextualLikeCount(extractor.getTextualLikeCount()); resultItem.setTextualLikeCount(extractor.getTextualLikeCount());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setThumbnailUrl(extractor.getThumbnailUrl()); resultItem.setThumbnailUrl(extractor.getThumbnailUrl());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setHeartedByUploader(extractor.isHeartedByUploader()); resultItem.setHeartedByUploader(extractor.isHeartedByUploader());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setPinned(extractor.isPinned()); resultItem.setPinned(extractor.isPinned());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setStreamPosition(extractor.getStreamPosition()); resultItem.setStreamPosition(extractor.getStreamPosition());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setReplies(extractor.getReplies()); resultItem.setReplies(extractor.getReplies());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
@ -103,10 +99,10 @@ public class CommentsInfoItemsCollector extends InfoItemsCollector<CommentsInfoI
} }
@Override @Override
public void commit(CommentsInfoItemExtractor extractor) { public void commit(final CommentsInfoItemExtractor extractor) {
try { try {
addItem(extract(extractor)); addItem(extract(extractor));
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
} }

View File

@ -19,13 +19,14 @@ public abstract class Downloader {
/** /**
* Do a GET request to get the resource that the url is pointing to.<br> * Do a GET request to get the resource that the url is pointing to.<br>
* <br> * <br>
* This method calls {@link #get(String, Map, Localization)} with the default preferred localization. It should only be * This method calls {@link #get(String, Map, Localization)} with the default preferred
* used when the resource that will be fetched won't be affected by the localization. * localization. It should only be used when the resource that will be fetched won't be affected
* by the localization.
* *
* @param url the URL that is pointing to the wanted resource * @param url the URL that is pointing to the wanted resource
* @return the result of the GET request * @return the result of the GET request
*/ */
public Response get(String url) throws IOException, ReCaptchaException { public Response get(final String url) throws IOException, ReCaptchaException {
return get(url, null, NewPipe.getPreferredLocalization()); return get(url, null, NewPipe.getPreferredLocalization());
} }
@ -38,7 +39,8 @@ public abstract class Downloader {
* @param localization the source of the value of the {@code Accept-Language} header * @param localization the source of the value of the {@code Accept-Language} header
* @return the result of the GET request * @return the result of the GET request
*/ */
public Response get(String url, @Nullable Localization localization) throws IOException, ReCaptchaException { public Response get(final String url, @Nullable final Localization localization)
throws IOException, ReCaptchaException {
return get(url, null, localization); return get(url, null, localization);
} }
@ -50,7 +52,8 @@ public abstract class Downloader {
* Any default headers <b>should</b> be overridden by these. * Any default headers <b>should</b> be overridden by these.
* @return the result of the GET request * @return the result of the GET request
*/ */
public Response get(String url, @Nullable Map<String, List<String>> headers) throws IOException, ReCaptchaException { public Response get(final String url, @Nullable final Map<String, List<String>> headers)
throws IOException, ReCaptchaException {
return get(url, headers, NewPipe.getPreferredLocalization()); return get(url, headers, NewPipe.getPreferredLocalization());
} }
@ -65,7 +68,9 @@ public abstract class Downloader {
* @param localization the source of the value of the {@code Accept-Language} header * @param localization the source of the value of the {@code Accept-Language} header
* @return the result of the GET request * @return the result of the GET request
*/ */
public Response get(String url, @Nullable Map<String, List<String>> headers, @Nullable Localization localization) public Response get(final String url,
@Nullable final Map<String, List<String>> headers,
@Nullable final Localization localization)
throws IOException, ReCaptchaException { throws IOException, ReCaptchaException {
return execute(Request.newBuilder() return execute(Request.newBuilder()
.get(url) .get(url)
@ -80,7 +85,7 @@ public abstract class Downloader {
* @param url the URL that is pointing to the wanted resource * @param url the URL that is pointing to the wanted resource
* @return the result of the HEAD request * @return the result of the HEAD request
*/ */
public Response head(String url) throws IOException, ReCaptchaException { public Response head(final String url) throws IOException, ReCaptchaException {
return head(url, null); return head(url, null);
} }
@ -92,7 +97,7 @@ public abstract class Downloader {
* Any default headers <b>should</b> be overridden by these. * Any default headers <b>should</b> be overridden by these.
* @return the result of the HEAD request * @return the result of the HEAD request
*/ */
public Response head(String url, @Nullable Map<String, List<String>> headers) public Response head(final String url, @Nullable final Map<String, List<String>> headers)
throws IOException, ReCaptchaException { throws IOException, ReCaptchaException {
return execute(Request.newBuilder() return execute(Request.newBuilder()
.head(url) .head(url)
@ -109,7 +114,9 @@ public abstract class Downloader {
* @param dataToSend byte array that will be sent when doing the request. * @param dataToSend byte array that will be sent when doing the request.
* @return the result of the GET request * @return the result of the GET request
*/ */
public Response post(String url, @Nullable Map<String, List<String>> headers, @Nullable byte[] dataToSend) public Response post(final String url,
@Nullable final Map<String, List<String>> headers,
@Nullable final byte[] dataToSend)
throws IOException, ReCaptchaException { throws IOException, ReCaptchaException {
return post(url, headers, dataToSend, NewPipe.getPreferredLocalization()); return post(url, headers, dataToSend, NewPipe.getPreferredLocalization());
} }
@ -126,7 +133,10 @@ public abstract class Downloader {
* @param localization the source of the value of the {@code Accept-Language} header * @param localization the source of the value of the {@code Accept-Language} header
* @return the result of the GET request * @return the result of the GET request
*/ */
public Response post(String url, @Nullable Map<String, List<String>> headers, @Nullable byte[] dataToSend, @Nullable Localization localization) public Response post(final String url,
@Nullable final Map<String, List<String>> headers,
@Nullable final byte[] dataToSend,
@Nullable final Localization localization)
throws IOException, ReCaptchaException { throws IOException, ReCaptchaException {
return execute(Request.newBuilder() return execute(Request.newBuilder()
.post(url, dataToSend) .post(url, dataToSend)
@ -140,5 +150,6 @@ public abstract class Downloader {
* *
* @return the result of the request * @return the result of the request
*/ */
public abstract Response execute(@Nonnull Request request) throws IOException, ReCaptchaException; public abstract Response execute(@Nonnull Request request)
throws IOException, ReCaptchaException;
} }

View File

@ -2,42 +2,60 @@ package org.schabi.newpipe.extractor.downloader;
import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.localization.Localization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*;
/** /**
* An object that holds request information used when {@link Downloader#execute(Request) executing} a request. * An object that holds request information used when {@link Downloader#execute(Request) executing}
* a request.
*/ */
public class Request { public class Request {
private final String httpMethod; private final String httpMethod;
private final String url; private final String url;
private final Map<String, List<String>> headers; private final Map<String, List<String>> headers;
@Nullable private final byte[] dataToSend; @Nullable
@Nullable private final Localization localization; private final byte[] dataToSend;
@Nullable
private final Localization localization;
public Request(String httpMethod, String url, Map<String, List<String>> headers, @Nullable byte[] dataToSend, public Request(final String httpMethod,
@Nullable Localization localization, boolean automaticLocalizationHeader) { final String url,
if (httpMethod == null) throw new IllegalArgumentException("Request's httpMethod is null"); @Nullable final Map<String, List<String>> headers,
if (url == null) throw new IllegalArgumentException("Request's url is null"); @Nullable final byte[] dataToSend,
@Nullable final Localization localization,
final boolean automaticLocalizationHeader) {
if (httpMethod == null) {
throw new IllegalArgumentException("Request's httpMethod is null");
}
if (url == null) {
throw new IllegalArgumentException("Request's url is null");
}
this.httpMethod = httpMethod; this.httpMethod = httpMethod;
this.url = url; this.url = url;
this.dataToSend = dataToSend; this.dataToSend = dataToSend;
this.localization = localization; this.localization = localization;
Map<String, List<String>> headersToSet = null; final Map<String, List<String>> actualHeaders = new LinkedHashMap<>();
if (headers == null) headers = Collections.emptyMap(); if (headers != null) {
actualHeaders.putAll(headers);
}
if (automaticLocalizationHeader && localization != null) { if (automaticLocalizationHeader && localization != null) {
headersToSet = new LinkedHashMap<>(headersFromLocalization(localization)); actualHeaders.putAll(headersFromLocalization(localization));
headersToSet.putAll(headers);
} }
this.headers = Collections.unmodifiableMap(headersToSet == null ? headers : headersToSet); this.headers = Collections.unmodifiableMap(actualHeaders);
} }
private Request(Builder builder) { private Request(final Builder builder) {
this(builder.httpMethod, builder.url, builder.headers, builder.dataToSend, this(builder.httpMethod, builder.url, builder.headers, builder.dataToSend,
builder.localization, builder.automaticLocalizationHeader); builder.localization, builder.automaticLocalizationHeader);
} }
@ -94,7 +112,7 @@ public class Request {
public static final class Builder { public static final class Builder {
private String httpMethod; private String httpMethod;
private String url; private String url;
private Map<String, List<String>> headers = new LinkedHashMap<>(); private final Map<String, List<String>> headers = new LinkedHashMap<>();
private byte[] dataToSend; private byte[] dataToSend;
private Localization localization; private Localization localization;
private boolean automaticLocalizationHeader = true; private boolean automaticLocalizationHeader = true;
@ -105,27 +123,28 @@ public class Request {
/** /**
* A http method (i.e. {@code GET, POST, HEAD}). * A http method (i.e. {@code GET, POST, HEAD}).
*/ */
public Builder httpMethod(String httpMethod) { public Builder httpMethod(final String httpMethodToSet) {
this.httpMethod = httpMethod; this.httpMethod = httpMethodToSet;
return this; return this;
} }
/** /**
* The URL that is pointing to the wanted resource. * The URL that is pointing to the wanted resource.
*/ */
public Builder url(String url) { public Builder url(final String urlToSet) {
this.url = url; this.url = urlToSet;
return this; return this;
} }
/** /**
* A list of headers that will be used in the request.<br> * A list of headers that will be used in the request.<br>
* Any default headers that the implementation may have, <b>should</b> be overridden by these. * Any default headers that the implementation may have, <b>should</b> be overridden by
* these.
*/ */
public Builder headers(@Nullable Map<String, List<String>> headers) { public Builder headers(@Nullable final Map<String, List<String>> headersToSet) {
this.headers.clear(); this.headers.clear();
if (headers != null) { if (headersToSet != null) {
this.headers.putAll(headers); this.headers.putAll(headersToSet);
} }
return this; return this;
} }
@ -137,8 +156,8 @@ public class Request {
* The implementation should make note of some recommended headers * The implementation should make note of some recommended headers
* (for example, {@code Content-Length} in a post request). * (for example, {@code Content-Length} in a post request).
*/ */
public Builder dataToSend(byte[] dataToSend) { public Builder dataToSend(final byte[] dataToSendToSet) {
this.dataToSend = dataToSend; this.dataToSend = dataToSendToSet;
return this; return this;
} }
@ -148,16 +167,16 @@ public class Request {
* Usually the {@code Accept-Language} will be set to this value (a helper * Usually the {@code Accept-Language} will be set to this value (a helper
* method to do this easily: {@link Request#headersFromLocalization(Localization)}). * method to do this easily: {@link Request#headersFromLocalization(Localization)}).
*/ */
public Builder localization(Localization localization) { public Builder localization(final Localization localizationToSet) {
this.localization = localization; this.localization = localizationToSet;
return this; return this;
} }
/** /**
* If localization headers should automatically be included in the request. * If localization headers should automatically be included in the request.
*/ */
public Builder automaticLocalizationHeader(boolean automaticLocalizationHeader) { public Builder automaticLocalizationHeader(final boolean automaticLocalizationHeaderToSet) {
this.automaticLocalizationHeader = automaticLocalizationHeader; this.automaticLocalizationHeader = automaticLocalizationHeaderToSet;
return this; return this;
} }
@ -170,22 +189,22 @@ public class Request {
// Http Methods Utils // Http Methods Utils
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public Builder get(String url) { public Builder get(final String urlToSet) {
this.httpMethod = "GET"; this.httpMethod = "GET";
this.url = url; this.url = urlToSet;
return this; return this;
} }
public Builder head(String url) { public Builder head(final String urlToSet) {
this.httpMethod = "HEAD"; this.httpMethod = "HEAD";
this.url = url; this.url = urlToSet;
return this; return this;
} }
public Builder post(String url, @Nullable byte[] dataToSend) { public Builder post(final String urlToSet, @Nullable final byte[] dataToSendToSet) {
this.httpMethod = "POST"; this.httpMethod = "POST";
this.url = url; this.url = urlToSet;
this.dataToSend = dataToSend; this.dataToSend = dataToSendToSet;
return this; return this;
} }
@ -193,13 +212,13 @@ public class Request {
// Additional Headers Utils // Additional Headers Utils
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public Builder setHeaders(String headerName, List<String> headerValueList) { public Builder setHeaders(final String headerName, final List<String> headerValueList) {
this.headers.remove(headerName); this.headers.remove(headerName);
this.headers.put(headerName, headerValueList); this.headers.put(headerName, headerValueList);
return this; return this;
} }
public Builder addHeaders(String headerName, List<String> headerValueList) { public Builder addHeaders(final String headerName, final List<String> headerValueList) {
@Nullable List<String> currentHeaderValueList = this.headers.get(headerName); @Nullable List<String> currentHeaderValueList = this.headers.get(headerName);
if (currentHeaderValueList == null) { if (currentHeaderValueList == null) {
currentHeaderValueList = new ArrayList<>(); currentHeaderValueList = new ArrayList<>();
@ -210,11 +229,11 @@ public class Request {
return this; return this;
} }
public Builder setHeader(String headerName, String headerValue) { public Builder setHeader(final String headerName, final String headerValue) {
return setHeaders(headerName, Collections.singletonList(headerValue)); return setHeaders(headerName, Collections.singletonList(headerValue));
} }
public Builder addHeader(String headerName, String headerValue) { public Builder addHeader(final String headerName, final String headerValue) {
return addHeaders(headerName, Collections.singletonList(headerValue)); return addHeaders(headerName, Collections.singletonList(headerValue));
} }
@ -226,15 +245,20 @@ public class Request {
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@Nonnull @Nonnull
public static Map<String, List<String>> headersFromLocalization(@Nullable Localization localization) { public static Map<String, List<String>> headersFromLocalization(
if (localization == null) return Collections.emptyMap(); @Nullable final Localization localization) {
if (localization == null) {
return Collections.emptyMap();
}
final Map<String, List<String>> headers = new LinkedHashMap<>(); final Map<String, List<String>> headers = new LinkedHashMap<>();
if (!localization.getCountryCode().isEmpty()) { if (!localization.getCountryCode().isEmpty()) {
headers.put("Accept-Language", Collections.singletonList(localization.getLocalizationCode() + headers.put("Accept-Language",
", " + localization.getLanguageCode() + ";q=0.9")); Collections.singletonList(localization.getLocalizationCode()
+ ", " + localization.getLanguageCode() + ";q=0.9"));
} else { } else {
headers.put("Accept-Language", Collections.singletonList(localization.getLanguageCode())); headers.put("Accept-Language",
Collections.singletonList(localization.getLanguageCode()));
} }
return headers; return headers;
@ -245,15 +269,19 @@ public class Request {
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
public boolean equals(Object o) { public boolean equals(final Object o) {
if (this == o) return true; if (this == o) {
if (o == null || getClass() != o.getClass()) return false; return true;
Request request = (Request) o; }
return httpMethod.equals(request.httpMethod) && if (o == null || getClass() != o.getClass()) {
url.equals(request.url) && return false;
headers.equals(request.headers) && }
Arrays.equals(dataToSend, request.dataToSend) && final Request request = (Request) o;
Objects.equals(localization, request.localization); return httpMethod.equals(request.httpMethod)
&& url.equals(request.url)
&& headers.equals(request.headers)
&& Arrays.equals(dataToSend, request.dataToSend)
&& Objects.equals(localization, request.localization);
} }
@Override @Override

View File

@ -17,11 +17,14 @@ public class Response {
private final String latestUrl; private final String latestUrl;
public Response(int responseCode, String responseMessage, Map<String, List<String>> responseHeaders, public Response(final int responseCode,
@Nullable String responseBody, @Nullable String latestUrl) { final String responseMessage,
@Nullable final Map<String, List<String>> responseHeaders,
@Nullable final String responseBody,
@Nullable final String latestUrl) {
this.responseCode = responseCode; this.responseCode = responseCode;
this.responseMessage = responseMessage; this.responseMessage = responseMessage;
this.responseHeaders = responseHeaders != null ? responseHeaders : Collections.<String, List<String>>emptyMap(); this.responseHeaders = responseHeaders == null ? Collections.emptyMap() : responseHeaders;
this.responseBody = responseBody == null ? "" : responseBody; this.responseBody = responseBody == null ? "" : responseBody;
this.latestUrl = latestUrl; this.latestUrl = latestUrl;
@ -60,14 +63,15 @@ public class Response {
/** /**
* For easy access to some header value that (usually) don't repeat itself. * For easy access to some header value that (usually) don't repeat itself.
* <p>For getting all the values associated to the header, use {@link #responseHeaders()} (e.g. {@code Set-Cookie}). * <p>For getting all the values associated to the header, use {@link #responseHeaders()} (e.g.
* {@code Set-Cookie}).
* *
* @param name the name of the header * @param name the name of the header
* @return the first value assigned to this header * @return the first value assigned to this header
*/ */
@Nullable @Nullable
public String getHeader(String name) { public String getHeader(final String name) {
for (Map.Entry<String, List<String>> headerEntry : responseHeaders.entrySet()) { for (final Map.Entry<String, List<String>> headerEntry : responseHeaders.entrySet()) {
final String key = headerEntry.getKey(); final String key = headerEntry.getKey();
if (key != null && key.equalsIgnoreCase(name) && !headerEntry.getValue().isEmpty()) { if (key != null && key.equalsIgnoreCase(name) && !headerEntry.getValue().isEmpty()) {
return headerEntry.getValue().get(0); return headerEntry.getValue().get(0);

View File

@ -11,7 +11,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
* YouTube is an example of a service that has this alternative available. * YouTube is an example of a service that has this alternative available.
*/ */
public abstract class FeedExtractor extends ListExtractor<StreamInfoItem> { public abstract class FeedExtractor extends ListExtractor<StreamInfoItem> {
public FeedExtractor(StreamingService service, ListLinkHandler listLinkHandler) { public FeedExtractor(final StreamingService service, final ListLinkHandler listLinkHandler) {
super(service, listLinkHandler); super(service, listLinkHandler);
} }
} }

View File

@ -13,26 +13,35 @@ import java.util.List;
public class FeedInfo extends ListInfo<StreamInfoItem> { public class FeedInfo extends ListInfo<StreamInfoItem> {
public FeedInfo(int serviceId, String id, String url, String originalUrl, String name, List<String> contentFilter, String sortFilter) { public FeedInfo(final int serviceId,
final String id,
final String url,
final String originalUrl,
final String name,
final List<String> contentFilter,
final String sortFilter) {
super(serviceId, id, url, originalUrl, name, contentFilter, sortFilter); super(serviceId, id, url, originalUrl, name, contentFilter, sortFilter);
} }
public static FeedInfo getInfo(String url) throws IOException, ExtractionException { public static FeedInfo getInfo(final String url) throws IOException, ExtractionException {
return getInfo(NewPipe.getServiceByUrl(url), url); return getInfo(NewPipe.getServiceByUrl(url), url);
} }
public static FeedInfo getInfo(StreamingService service, String url) throws IOException, ExtractionException { public static FeedInfo getInfo(final StreamingService service, final String url)
throws IOException, ExtractionException {
final FeedExtractor extractor = service.getFeedExtractor(url); final FeedExtractor extractor = service.getFeedExtractor(url);
if (extractor == null) { if (extractor == null) {
throw new IllegalArgumentException("Service \"" + service.getServiceInfo().getName() + "\" doesn't support FeedExtractor."); throw new IllegalArgumentException("Service \"" + service.getServiceInfo().getName()
+ "\" doesn't support FeedExtractor.");
} }
extractor.fetchPage(); extractor.fetchPage();
return getInfo(extractor); return getInfo(extractor);
} }
public static FeedInfo getInfo(FeedExtractor extractor) throws IOException, ExtractionException { public static FeedInfo getInfo(final FeedExtractor extractor)
throws IOException, ExtractionException {
extractor.fetchPage(); extractor.fetchPage();
final int serviceId = extractor.getServiceId(); final int serviceId = extractor.getServiceId();
@ -43,7 +52,8 @@ public class FeedInfo extends ListInfo<StreamInfoItem> {
final FeedInfo info = new FeedInfo(serviceId, id, url, originalUrl, name, null, null); final FeedInfo info = new FeedInfo(serviceId, id, url, originalUrl, name, null, null);
final InfoItemsPage<StreamInfoItem> itemsPage = ExtractorHelper.getItemsPageOrLogError(info, extractor); final InfoItemsPage<StreamInfoItem> itemsPage
= ExtractorHelper.getItemsPageOrLogError(info, extractor);
info.setRelatedItems(itemsPage.getItems()); info.setRelatedItems(itemsPage.getItems());
info.setNextPage(itemsPage.getNextPage()); info.setNextPage(itemsPage.getNextPage());

View File

@ -31,9 +31,9 @@ import javax.annotation.Nonnull;
public abstract class KioskExtractor<T extends InfoItem> extends ListExtractor<T> { public abstract class KioskExtractor<T extends InfoItem> extends ListExtractor<T> {
private final String id; private final String id;
public KioskExtractor(StreamingService streamingService, public KioskExtractor(final StreamingService streamingService,
ListLinkHandler linkHandler, final ListLinkHandler linkHandler,
String kioskId) { final String kioskId) {
super(streamingService, linkHandler); super(streamingService, linkHandler);
this.id = kioskId; this.id = kioskId;
} }
@ -50,7 +50,6 @@ public abstract class KioskExtractor<T extends InfoItem> extends ListExtractor<T
* In order to get the name of the kiosk in the desired language we have to * In order to get the name of the kiosk in the desired language we have to
* crawl if from the website. * crawl if from the website.
* @return the translated version of id * @return the translated version of id
* @throws ParsingException
*/ */
@Nonnull @Nonnull
@Override @Override

View File

@ -26,34 +26,30 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
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 org.schabi.newpipe.extractor.utils.ExtractorHelper; import org.schabi.newpipe.extractor.utils.ExtractorHelper;
import java.io.IOException; import java.io.IOException;
public class KioskInfo extends ListInfo<StreamInfoItem> { public final class KioskInfo extends ListInfo<StreamInfoItem> {
private KioskInfo(int serviceId, ListLinkHandler linkHandler, String name) throws ParsingException { private KioskInfo(final int serviceId, final ListLinkHandler linkHandler, final String name) {
super(serviceId, linkHandler, name); super(serviceId, linkHandler, name);
} }
public static ListExtractor.InfoItemsPage<StreamInfoItem> getMoreItems(StreamingService service, public static ListExtractor.InfoItemsPage<StreamInfoItem> getMoreItems(
String url, final StreamingService service, final String url, final Page page)
Page page)
throws IOException, ExtractionException { throws IOException, ExtractionException {
KioskList kl = service.getKioskList(); return service.getKioskList().getExtractorByUrl(url, page).getPage(page);
KioskExtractor extractor = kl.getExtractorByUrl(url, page);
return extractor.getPage(page);
} }
public static KioskInfo getInfo(String url) throws IOException, ExtractionException { public static KioskInfo getInfo(final String url) throws IOException, ExtractionException {
return getInfo(NewPipe.getServiceByUrl(url), url); return getInfo(NewPipe.getServiceByUrl(url), url);
} }
public static KioskInfo getInfo(StreamingService service, String url) throws IOException, ExtractionException { public static KioskInfo getInfo(final StreamingService service, final String url)
KioskList kl = service.getKioskList(); throws IOException, ExtractionException {
KioskExtractor extractor = kl.getExtractorByUrl(url, null); final KioskExtractor extractor = service.getKioskList().getExtractorByUrl(url, null);
extractor.fetchPage(); extractor.fetchPage();
return getInfo(extractor); return getInfo(extractor);
} }
@ -63,13 +59,14 @@ public class KioskInfo extends ListInfo<StreamInfoItem> {
* *
* @param extractor an extractor where fetchPage() was already got called on. * @param extractor an extractor where fetchPage() was already got called on.
*/ */
public static KioskInfo getInfo(KioskExtractor extractor) throws ExtractionException { public static KioskInfo getInfo(final KioskExtractor extractor) throws ExtractionException {
final KioskInfo info = new KioskInfo(extractor.getServiceId(), final KioskInfo info = new KioskInfo(extractor.getServiceId(),
extractor.getLinkHandler(), extractor.getLinkHandler(),
extractor.getName()); extractor.getName());
final ListExtractor.InfoItemsPage<StreamInfoItem> itemsPage = ExtractorHelper.getItemsPageOrLogError(info, extractor); final ListExtractor.InfoItemsPage<StreamInfoItem> itemsPage
= ExtractorHelper.getItemsPageOrLogError(info, extractor);
info.setRelatedItems(itemsPage.getItems()); info.setRelatedItems(itemsPage.getItems());
info.setNextPage(itemsPage.getNextPage()); info.setNextPage(itemsPage.getNextPage());

View File

@ -19,9 +19,9 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class KioskList { public class KioskList {
public interface KioskExtractorFactory { public interface KioskExtractorFactory {
KioskExtractor createNewKiosk(final StreamingService streamingService, KioskExtractor createNewKiosk(StreamingService streamingService,
final String url, String url,
final String kioskId) String kioskId)
throws ExtractionException, IOException; throws ExtractionException, IOException;
} }
@ -34,8 +34,8 @@ public class KioskList {
@Nullable @Nullable
private ContentCountry forcedContentCountry; private ContentCountry forcedContentCountry;
private class KioskEntry { private static class KioskEntry {
public KioskEntry(KioskExtractorFactory ef, ListLinkHandlerFactory h) { KioskEntry(final KioskExtractorFactory ef, final ListLinkHandlerFactory h) {
extractorFactory = ef; extractorFactory = ef;
handlerFactory = h; handlerFactory = h;
} }
@ -44,11 +44,13 @@ public class KioskList {
final ListLinkHandlerFactory handlerFactory; final ListLinkHandlerFactory handlerFactory;
} }
public KioskList(StreamingService service) { public KioskList(final StreamingService service) {
this.service = service; this.service = service;
} }
public void addKioskEntry(KioskExtractorFactory extractorFactory, ListLinkHandlerFactory handlerFactory, String id) public void addKioskEntry(final KioskExtractorFactory extractorFactory,
final ListLinkHandlerFactory handlerFactory,
final String id)
throws Exception { throws Exception {
if (kioskList.get(id) != null) { if (kioskList.get(id) != null) {
throw new Exception("Kiosk with type " + id + " already exists."); throw new Exception("Kiosk with type " + id + " already exists.");
@ -56,7 +58,7 @@ public class KioskList {
kioskList.put(id, new KioskEntry(extractorFactory, handlerFactory)); kioskList.put(id, new KioskEntry(extractorFactory, handlerFactory));
} }
public void setDefaultKiosk(String kioskType) { public void setDefaultKiosk(final String kioskType) {
defaultKiosk = kioskType; defaultKiosk = kioskType;
} }
@ -65,19 +67,20 @@ public class KioskList {
return getDefaultKioskExtractor(null); return getDefaultKioskExtractor(null);
} }
public KioskExtractor getDefaultKioskExtractor(Page nextPage) public KioskExtractor getDefaultKioskExtractor(final Page nextPage)
throws ExtractionException, IOException { throws ExtractionException, IOException {
return getDefaultKioskExtractor(nextPage, NewPipe.getPreferredLocalization()); return getDefaultKioskExtractor(nextPage, NewPipe.getPreferredLocalization());
} }
public KioskExtractor getDefaultKioskExtractor(Page nextPage, Localization localization) public KioskExtractor getDefaultKioskExtractor(final Page nextPage,
final Localization localization)
throws ExtractionException, IOException { throws ExtractionException, IOException {
if (!isNullOrEmpty(defaultKiosk)) { if (!isNullOrEmpty(defaultKiosk)) {
return getExtractorById(defaultKiosk, nextPage, localization); return getExtractorById(defaultKiosk, nextPage, localization);
} else { } else {
if (!kioskList.isEmpty()) { if (!kioskList.isEmpty()) {
// if not set get any entry // if not set get any entry
Object[] keySet = kioskList.keySet().toArray(); final Object[] keySet = kioskList.keySet().toArray();
return getExtractorById(keySet[0].toString(), nextPage, localization); return getExtractorById(keySet[0].toString(), nextPage, localization);
} else { } else {
return null; return null;
@ -89,22 +92,28 @@ public class KioskList {
return defaultKiosk; return defaultKiosk;
} }
public KioskExtractor getExtractorById(String kioskId, Page nextPage) public KioskExtractor getExtractorById(final String kioskId, final Page nextPage)
throws ExtractionException, IOException { throws ExtractionException, IOException {
return getExtractorById(kioskId, nextPage, NewPipe.getPreferredLocalization()); return getExtractorById(kioskId, nextPage, NewPipe.getPreferredLocalization());
} }
public KioskExtractor getExtractorById(String kioskId, Page nextPage, Localization localization) public KioskExtractor getExtractorById(final String kioskId,
final Page nextPage,
final Localization localization)
throws ExtractionException, IOException { throws ExtractionException, IOException {
KioskEntry ke = kioskList.get(kioskId); final KioskEntry ke = kioskList.get(kioskId);
if (ke == null) { if (ke == null) {
throw new ExtractionException("No kiosk found with the type: " + kioskId); throw new ExtractionException("No kiosk found with the type: " + kioskId);
} else { } else {
final KioskExtractor kioskExtractor = ke.extractorFactory.createNewKiosk(service, final KioskExtractor kioskExtractor = ke.extractorFactory.createNewKiosk(service,
ke.handlerFactory.fromId(kioskId).getUrl(), kioskId); ke.handlerFactory.fromId(kioskId).getUrl(), kioskId);
if (forcedLocalization != null) kioskExtractor.forceLocalization(forcedLocalization); if (forcedLocalization != null) {
if (forcedContentCountry != null) kioskExtractor.forceContentCountry(forcedContentCountry); kioskExtractor.forceLocalization(forcedLocalization);
}
if (forcedContentCountry != null) {
kioskExtractor.forceContentCountry(forcedContentCountry);
}
return kioskExtractor; return kioskExtractor;
} }
@ -114,15 +123,17 @@ public class KioskList {
return kioskList.keySet(); return kioskList.keySet();
} }
public KioskExtractor getExtractorByUrl(String url, Page nextPage) public KioskExtractor getExtractorByUrl(final String url, final Page nextPage)
throws ExtractionException, IOException { throws ExtractionException, IOException {
return getExtractorByUrl(url, nextPage, NewPipe.getPreferredLocalization()); return getExtractorByUrl(url, nextPage, NewPipe.getPreferredLocalization());
} }
public KioskExtractor getExtractorByUrl(String url, Page nextPage, Localization localization) public KioskExtractor getExtractorByUrl(final String url,
final Page nextPage,
final Localization localization)
throws ExtractionException, IOException { throws ExtractionException, IOException {
for (Map.Entry<String, KioskEntry> e : kioskList.entrySet()) { for (final Map.Entry<String, KioskEntry> e : kioskList.entrySet()) {
KioskEntry ke = e.getValue(); final KioskEntry ke = e.getValue();
if (ke.handlerFactory.acceptUrl(url)) { if (ke.handlerFactory.acceptUrl(url)) {
return getExtractorById(ke.handlerFactory.getId(url), nextPage, localization); return getExtractorById(ke.handlerFactory.getId(url), nextPage, localization);
} }
@ -130,15 +141,15 @@ public class KioskList {
throw new ExtractionException("Could not find a kiosk that fits to the url: " + url); throw new ExtractionException("Could not find a kiosk that fits to the url: " + url);
} }
public ListLinkHandlerFactory getListLinkHandlerFactoryByType(String type) { public ListLinkHandlerFactory getListLinkHandlerFactoryByType(final String type) {
return kioskList.get(type).handlerFactory; return kioskList.get(type).handlerFactory;
} }
public void forceLocalization(@Nullable Localization localization) { public void forceLocalization(@Nullable final Localization localization) {
this.forcedLocalization = localization; this.forcedLocalization = localization;
} }
public void forceContentCountry(@Nullable ContentCountry contentCountry) { public void forceContentCountry(@Nullable final ContentCountry contentCountry) {
this.forcedContentCountry = contentCountry; this.forcedContentCountry = contentCountry;
} }
} }

View File

@ -10,13 +10,13 @@ public class LinkHandler implements Serializable {
protected final String url; protected final String url;
protected final String id; protected final String id;
public LinkHandler(String originalUrl, String url, String id) { public LinkHandler(final String originalUrl, final String url, final String id) {
this.originalUrl = originalUrl; this.originalUrl = originalUrl;
this.url = url; this.url = url;
this.id = id; this.id = id;
} }
public LinkHandler(LinkHandler handler) { public LinkHandler(final LinkHandler handler) {
this(handler.originalUrl, handler.url, handler.id); this(handler.originalUrl, handler.url, handler.id);
} }

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.extractor.linkhandler; package org.schabi.newpipe.extractor.linkhandler;
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.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
@ -31,10 +30,12 @@ public abstract class LinkHandlerFactory {
/////////////////////////////////// ///////////////////////////////////
public abstract String getId(String url) throws ParsingException; public abstract String getId(String url) throws ParsingException;
public abstract String getUrl(String id) throws ParsingException;
public abstract boolean onAcceptUrl(final String url) throws ParsingException;
public String getUrl(String id, String baseUrl) throws ParsingException { public abstract String getUrl(String id) throws ParsingException;
public abstract boolean onAcceptUrl(String url) throws ParsingException;
public String getUrl(final String id, final String baseUrl) throws ParsingException {
return getUrl(id); return getUrl(id);
} }
@ -46,6 +47,7 @@ public abstract class LinkHandlerFactory {
* Builds a {@link LinkHandler} from a url.<br> * Builds a {@link LinkHandler} from a url.<br>
* Be sure to call {@link Utils#followGoogleRedirectIfNeeded(String)} on the url if overriding * Be sure to call {@link Utils#followGoogleRedirectIfNeeded(String)} on the url if overriding
* this function. * this function.
*
* @param url the url to extract path and id from * @param url the url to extract path and id from
* @return a {@link LinkHandler} complete with information * @return a {@link LinkHandler} complete with information
*/ */
@ -64,12 +66,15 @@ public abstract class LinkHandlerFactory {
* extracted?).<br> * extracted?).<br>
* So do not call {@link Utils#followGoogleRedirectIfNeeded(String)} on the URL if overriding * So do not call {@link Utils#followGoogleRedirectIfNeeded(String)} on the URL if overriding
* this function, since that should be done in {@link #fromUrl(String)}. * this function, since that should be done in {@link #fromUrl(String)}.
* @param url the URL without Google search redirects to extract id from *
* @param url the URL without Google search redirects to extract id from
* @param baseUrl the base URL * @param baseUrl the base URL
* @return a {@link LinkHandler} complete with information * @return a {@link LinkHandler} complete with information
*/ */
public LinkHandler fromUrl(String url, String baseUrl) throws ParsingException { public LinkHandler fromUrl(final String url, final String baseUrl) throws ParsingException {
if (url == null) throw new IllegalArgumentException("URL cannot be null"); if (url == null) {
throw new IllegalArgumentException("URL cannot be null");
}
if (!acceptUrl(url)) { if (!acceptUrl(url)) {
throw new ParsingException("URL not accepted: " + url); throw new ParsingException("URL not accepted: " + url);
} }
@ -78,14 +83,18 @@ public abstract class LinkHandlerFactory {
return new LinkHandler(url, getUrl(id, baseUrl), id); return new LinkHandler(url, getUrl(id, baseUrl), id);
} }
public LinkHandler fromId(String id) throws ParsingException { public LinkHandler fromId(final String id) throws ParsingException {
if (id == null) throw new IllegalArgumentException("id can not be null"); if (id == null) {
throw new IllegalArgumentException("id can not be null");
}
final String url = getUrl(id); final String url = getUrl(id);
return new LinkHandler(url, url, id); return new LinkHandler(url, url, id);
} }
public LinkHandler fromId(String id, String baseUrl) throws ParsingException { public LinkHandler fromId(final String id, final String baseUrl) throws ParsingException {
if (id == null) throw new IllegalArgumentException("id can not be null"); if (id == null) {
throw new IllegalArgumentException("id can not be null");
}
final String url = getUrl(id, baseUrl); final String url = getUrl(id, baseUrl);
return new LinkHandler(url, url, id); return new LinkHandler(url, url, id);
} }
@ -96,11 +105,6 @@ public abstract class LinkHandlerFactory {
* Return false if this service shall not allow to be called through ACTIONs. * Return false if this service shall not allow to be called through ACTIONs.
*/ */
public boolean acceptUrl(final String url) throws ParsingException { public boolean acceptUrl(final String url) throws ParsingException {
try { return onAcceptUrl(url);
return onAcceptUrl(url);
} catch (FoundAdException fe) {
throw fe;
}
} }
} }

View File

@ -1,5 +1,7 @@
package org.schabi.newpipe.extractor.linkhandler; package org.schabi.newpipe.extractor.linkhandler;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -7,17 +9,17 @@ public class ListLinkHandler extends LinkHandler {
protected final List<String> contentFilters; protected final List<String> contentFilters;
protected final String sortFilter; protected final String sortFilter;
public ListLinkHandler(String originalUrl, public ListLinkHandler(final String originalUrl,
String url, final String url,
String id, final String id,
List<String> contentFilters, final List<String> contentFilters,
String sortFilter) { final String sortFilter) {
super(originalUrl, url, id); super(originalUrl, url, id);
this.contentFilters = Collections.unmodifiableList(contentFilters); this.contentFilters = Collections.unmodifiableList(contentFilters);
this.sortFilter = sortFilter; this.sortFilter = sortFilter;
} }
public ListLinkHandler(ListLinkHandler handler) { public ListLinkHandler(final ListLinkHandler handler) {
this(handler.originalUrl, this(handler.originalUrl,
handler.url, handler.url,
handler.id, handler.id,
@ -25,14 +27,12 @@ public class ListLinkHandler extends LinkHandler {
handler.sortFilter); handler.sortFilter);
} }
public ListLinkHandler(LinkHandler handler, public ListLinkHandler(final LinkHandler handler) {
List<String> contentFilters,
String sortFilter) {
this(handler.originalUrl, this(handler.originalUrl,
handler.url, handler.url,
handler.id, handler.id,
contentFilters, Collections.emptyList(),
sortFilter); EMPTY_STRING);
} }
public List<String> getContentFilters() { public List<String> getContentFilters() {

View File

@ -4,7 +4,6 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
public abstract class ListLinkHandlerFactory extends LinkHandlerFactory { public abstract class ListLinkHandlerFactory extends LinkHandlerFactory {
@ -13,17 +12,13 @@ public abstract class ListLinkHandlerFactory extends LinkHandlerFactory {
// To Override // To Override
/////////////////////////////////// ///////////////////////////////////
public List<String> getContentFilter(String url) throws ParsingException { public abstract String getUrl(String id, List<String> contentFilter, String sortFilter)
return Collections.emptyList(); throws ParsingException;
}
public String getSortFilter(String url) throws ParsingException { public String getUrl(final String id,
return ""; final List<String> contentFilter,
} final String sortFilter,
final String baseUrl) throws ParsingException {
public abstract String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException;
public String getUrl(String id, List<String> contentFilter, String sortFilter, String baseUrl) throws ParsingException {
return getUrl(id, contentFilter, sortFilter); return getUrl(id, contentFilter, sortFilter);
} }
@ -39,55 +34,58 @@ public abstract class ListLinkHandlerFactory extends LinkHandlerFactory {
} }
@Override @Override
public ListLinkHandler fromUrl(String url, String baseUrl) throws ParsingException { public ListLinkHandler fromUrl(final String url, final String baseUrl) throws ParsingException {
if (url == null) throw new IllegalArgumentException("url may not be null"); if (url == null) {
throw new IllegalArgumentException("url may not be null");
}
return new ListLinkHandler(super.fromUrl(url, baseUrl), getContentFilter(url), getSortFilter(url)); return new ListLinkHandler(super.fromUrl(url, baseUrl));
} }
@Override @Override
public ListLinkHandler fromId(String id) throws ParsingException { public ListLinkHandler fromId(final String id) throws ParsingException {
return new ListLinkHandler(super.fromId(id), new ArrayList<String>(0), ""); return new ListLinkHandler(super.fromId(id));
} }
@Override @Override
public ListLinkHandler fromId(String id, String baseUrl) throws ParsingException { public ListLinkHandler fromId(final String id, final String baseUrl) throws ParsingException {
return new ListLinkHandler(super.fromId(id, baseUrl), new ArrayList<String>(0), ""); return new ListLinkHandler(super.fromId(id, baseUrl));
} }
public ListLinkHandler fromQuery(String id, public ListLinkHandler fromQuery(final String id,
List<String> contentFilters, final List<String> contentFilters,
String sortFilter) throws ParsingException { final String sortFilter) throws ParsingException {
final String url = getUrl(id, contentFilters, sortFilter); final String url = getUrl(id, contentFilters, sortFilter);
return new ListLinkHandler(url, url, id, contentFilters, sortFilter); return new ListLinkHandler(url, url, id, contentFilters, sortFilter);
} }
public ListLinkHandler fromQuery(String id, public ListLinkHandler fromQuery(final String id,
List<String> contentFilters, final List<String> contentFilters,
String sortFilter, String baseUrl) throws ParsingException { final String sortFilter,
final String baseUrl) throws ParsingException {
final String url = getUrl(id, contentFilters, sortFilter, baseUrl); final String url = getUrl(id, contentFilters, sortFilter, baseUrl);
return new ListLinkHandler(url, url, id, contentFilters, sortFilter); return new ListLinkHandler(url, url, id, contentFilters, sortFilter);
} }
/** /**
* For making ListLinkHandlerFactory compatible with LinkHandlerFactory we need to override this, * For making ListLinkHandlerFactory compatible with LinkHandlerFactory we need to override
* however it should not be overridden by the actual implementation. * this, however it should not be overridden by the actual implementation.
* *
* @param id
* @return the url corresponding to id without any filters applied * @return the url corresponding to id without any filters applied
*/ */
public String getUrl(String id) throws ParsingException { public String getUrl(final String id) throws ParsingException {
return getUrl(id, new ArrayList<String>(0), ""); return getUrl(id, new ArrayList<>(0), "");
} }
@Override @Override
public String getUrl(String id, String baseUrl) throws ParsingException { public String getUrl(final String id, final String baseUrl) throws ParsingException {
return getUrl(id, new ArrayList<String>(0), "", baseUrl); return getUrl(id, new ArrayList<>(0), "", baseUrl);
} }
/** /**
* Will returns content filter the corresponding extractor can handle like "channels", "videos", "music", etc. * Will returns content filter the corresponding extractor can handle like "channels", "videos",
* "music", etc.
* *
* @return filter that can be applied when building a query for getting a list * @return filter that can be applied when building a query for getting a list
*/ */
@ -96,7 +94,8 @@ public abstract class ListLinkHandlerFactory extends LinkHandlerFactory {
} }
/** /**
* Will returns sort filter the corresponding extractor can handle like "A-Z", "oldest first", "size", etc. * Will returns sort filter the corresponding extractor can handle like "A-Z", "oldest first",
* "size", etc.
* *
* @return filter that can be applied when building a query for getting a list * @return filter that can be applied when building a query for getting a list
*/ */

View File

@ -4,15 +4,15 @@ import java.util.List;
public class SearchQueryHandler extends ListLinkHandler { public class SearchQueryHandler extends ListLinkHandler {
public SearchQueryHandler(String originalUrl, public SearchQueryHandler(final String originalUrl,
String url, final String url,
String searchString, final String searchString,
List<String> contentFilters, final List<String> contentFilters,
String sortFilter) { final String sortFilter) {
super(originalUrl, url, searchString, contentFilters, sortFilter); super(originalUrl, url, searchString, contentFilters, sortFilter);
} }
public SearchQueryHandler(ListLinkHandler handler) { public SearchQueryHandler(final ListLinkHandler handler) {
this(handler.originalUrl, this(handler.originalUrl,
handler.url, handler.url,
handler.id, handler.id,

View File

@ -1,12 +1,12 @@
package org.schabi.newpipe.extractor.linkhandler; package org.schabi.newpipe.extractor.linkhandler;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import java.util.ArrayList; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
public abstract class SearchQueryHandlerFactory extends ListLinkHandlerFactory { public abstract class SearchQueryHandlerFactory extends ListLinkHandlerFactory {
/////////////////////////////////// ///////////////////////////////////
@ -14,9 +14,11 @@ public abstract class SearchQueryHandlerFactory extends ListLinkHandlerFactory {
/////////////////////////////////// ///////////////////////////////////
@Override @Override
public abstract String getUrl(String query, List<String> contentFilter, String sortFilter) throws ParsingException; public abstract String getUrl(String query, List<String> contentFilter, String sortFilter)
throws ParsingException;
public String getSearchString(String url) { @SuppressWarnings("unused")
public String getSearchString(final String url) {
return ""; return "";
} }
@ -25,28 +27,26 @@ public abstract class SearchQueryHandlerFactory extends ListLinkHandlerFactory {
/////////////////////////////////// ///////////////////////////////////
@Override @Override
public String getId(String url) { public String getId(final String url) {
return getSearchString(url); return getSearchString(url);
} }
@Override @Override
public SearchQueryHandler fromQuery(String query, public SearchQueryHandler fromQuery(final String query,
List<String> contentFilter, final List<String> contentFilter,
String sortFilter) throws ParsingException { final String sortFilter) throws ParsingException {
return new SearchQueryHandler(super.fromQuery(query, contentFilter, sortFilter)); return new SearchQueryHandler(super.fromQuery(query, contentFilter, sortFilter));
} }
public SearchQueryHandler fromQuery(String query) throws ParsingException { public SearchQueryHandler fromQuery(final String query) throws ParsingException {
return fromQuery(query, new ArrayList<>(0), EMPTY_STRING); return fromQuery(query, Collections.emptyList(), EMPTY_STRING);
} }
/** /**
* It's not mandatory for NewPipe to handle the Url * It's not mandatory for NewPipe to handle the Url
*
* @param url
*/ */
@Override @Override
public boolean onAcceptUrl(String url) { public boolean onAcceptUrl(final String url) {
return false; return false;
} }
} }

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.extractor.localization; package org.schabi.newpipe.extractor.localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -9,23 +10,26 @@ import java.util.List;
/** /**
* Represents a country that should be used when fetching content. * Represents a country that should be used when fetching content.
* <p> * <p>
* YouTube, for example, give different results in their feed depending on which country is selected. * YouTube, for example, give different results in their feed depending on which country is
* selected.
* </p> * </p>
*/ */
public class ContentCountry implements Serializable { public class ContentCountry implements Serializable {
public static final ContentCountry DEFAULT = new ContentCountry(Localization.DEFAULT.getCountryCode()); public static final ContentCountry DEFAULT =
new ContentCountry(Localization.DEFAULT.getCountryCode());
@Nonnull private final String countryCode; @Nonnull
private final String countryCode;
public static List<ContentCountry> listFrom(String... countryCodeList) { public static List<ContentCountry> listFrom(final String... countryCodeList) {
final List<ContentCountry> toReturn = new ArrayList<>(); final List<ContentCountry> toReturn = new ArrayList<>();
for (String countryCode : countryCodeList) { for (final String countryCode : countryCodeList) {
toReturn.add(new ContentCountry(countryCode)); toReturn.add(new ContentCountry(countryCode));
} }
return Collections.unmodifiableList(toReturn); return Collections.unmodifiableList(toReturn);
} }
public ContentCountry(@Nonnull String countryCode) { public ContentCountry(@Nonnull final String countryCode) {
this.countryCode = countryCode; this.countryCode = countryCode;
} }
@ -40,11 +44,15 @@ public class ContentCountry implements Serializable {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(final Object o) {
if (this == o) return true; if (this == o) {
if (!(o instanceof ContentCountry)) return false; return true;
}
if (!(o instanceof ContentCountry)) {
return false;
}
ContentCountry that = (ContentCountry) o; final ContentCountry that = (ContentCountry) o;
return countryCode.equals(that.countryCode); return countryCode.equals(that.countryCode);
} }

View File

@ -9,7 +9,8 @@ import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
/** /**
* A wrapper class that provides a field to describe if the date/time is precise or just an approximation. * A wrapper class that provides a field to describe if the date/time is precise or just an
* approximation.
*/ */
public class DateWrapper implements Serializable { public class DateWrapper implements Serializable {
@Nonnull @Nonnull
@ -20,7 +21,8 @@ public class DateWrapper implements Serializable {
* @deprecated Use {@link #DateWrapper(OffsetDateTime)} instead. * @deprecated Use {@link #DateWrapper(OffsetDateTime)} instead.
*/ */
@Deprecated @Deprecated
public DateWrapper(@Nonnull Calendar calendar) { public DateWrapper(@Nonnull final Calendar calendar) {
//noinspection deprecation
this(calendar, false); this(calendar, false);
} }
@ -28,15 +30,16 @@ public class DateWrapper implements Serializable {
* @deprecated Use {@link #DateWrapper(OffsetDateTime, boolean)} instead. * @deprecated Use {@link #DateWrapper(OffsetDateTime, boolean)} instead.
*/ */
@Deprecated @Deprecated
public DateWrapper(@Nonnull Calendar calendar, boolean isApproximation) { public DateWrapper(@Nonnull final Calendar calendar, final boolean isApproximation) {
this(OffsetDateTime.ofInstant(calendar.toInstant(), ZoneOffset.UTC), isApproximation); this(OffsetDateTime.ofInstant(calendar.toInstant(), ZoneOffset.UTC), isApproximation);
} }
public DateWrapper(@Nonnull OffsetDateTime offsetDateTime) { public DateWrapper(@Nonnull final OffsetDateTime offsetDateTime) {
this(offsetDateTime, false); this(offsetDateTime, false);
} }
public DateWrapper(@Nonnull OffsetDateTime offsetDateTime, boolean isApproximation) { public DateWrapper(@Nonnull final OffsetDateTime offsetDateTime,
final boolean isApproximation) {
this.offsetDateTime = offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC); this.offsetDateTime = offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC);
this.isApproximation = isApproximation; this.isApproximation = isApproximation;
} }
@ -60,8 +63,8 @@ public class DateWrapper implements Serializable {
} }
/** /**
* @return if the date is considered is precise or just an approximation (e.g. service only returns an approximation * @return if the date is considered is precise or just an approximation (e.g. service only
* like 2 weeks ago instead of a precise date). * returns an approximation like 2 weeks ago instead of a precise date).
*/ */
public boolean isApproximation() { public boolean isApproximation() {
return isApproximation; return isApproximation;

View File

@ -4,8 +4,15 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
public class Localization implements Serializable { public class Localization implements Serializable {
public static final Localization DEFAULT = new Localization("en", "GB"); public static final Localization DEFAULT = new Localization("en", "GB");
@ -16,11 +23,12 @@ public class Localization implements Serializable {
private final String countryCode; private final String countryCode;
/** /**
* @param localizationCodeList a list of localization code, formatted like {@link #getLocalizationCode()} * @param localizationCodeList a list of localization code, formatted like {@link
* #getLocalizationCode()}
*/ */
public static List<Localization> listFrom(String... localizationCodeList) { public static List<Localization> listFrom(final String... localizationCodeList) {
final List<Localization> toReturn = new ArrayList<>(); final List<Localization> toReturn = new ArrayList<>();
for (String localizationCode : localizationCodeList) { for (final String localizationCode : localizationCodeList) {
toReturn.add(fromLocalizationCode(localizationCode)); toReturn.add(fromLocalizationCode(localizationCode));
} }
return Collections.unmodifiableList(toReturn); return Collections.unmodifiableList(toReturn);
@ -29,10 +37,11 @@ public class Localization implements Serializable {
/** /**
* @param localizationCode a localization code, formatted like {@link #getLocalizationCode()} * @param localizationCode a localization code, formatted like {@link #getLocalizationCode()}
*/ */
public static Localization fromLocalizationCode(String localizationCode) { public static Localization fromLocalizationCode(final String localizationCode) {
final int indexSeparator = localizationCode.indexOf("-"); final int indexSeparator = localizationCode.indexOf("-");
final String languageCode, countryCode; final String languageCode;
final String countryCode;
if (indexSeparator != -1) { if (indexSeparator != -1) {
languageCode = localizationCode.substring(0, indexSeparator); languageCode = localizationCode.substring(0, indexSeparator);
countryCode = localizationCode.substring(indexSeparator + 1); countryCode = localizationCode.substring(indexSeparator + 1);
@ -44,15 +53,16 @@ public class Localization implements Serializable {
return new Localization(languageCode, countryCode); return new Localization(languageCode, countryCode);
} }
public Localization(@Nonnull String languageCode, @Nullable String countryCode) { public Localization(@Nonnull final String languageCode, @Nullable final String countryCode) {
this.languageCode = languageCode; this.languageCode = languageCode;
this.countryCode = countryCode; this.countryCode = countryCode;
} }
public Localization(@Nonnull String languageCode) { public Localization(@Nonnull final String languageCode) {
this(languageCode, null); this(languageCode, null);
} }
@Nonnull
public String getLanguageCode() { public String getLanguageCode() {
return languageCode; return languageCode;
} }
@ -66,7 +76,7 @@ public class Localization implements Serializable {
return new Locale(getLanguageCode(), getCountryCode()); return new Locale(getLanguageCode(), getCountryCode());
} }
public static Localization fromLocale(@Nonnull Locale locale) { public static Localization fromLocale(@Nonnull final Locale locale) {
return new Localization(locale.getLanguage(), locale.getCountry()); return new Localization(locale.getLanguage(), locale.getCountry());
} }
@ -84,14 +94,18 @@ public class Localization implements Serializable {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(final Object o) {
if (this == o) return true; if (this == o) {
if (!(o instanceof Localization)) return false; return true;
}
if (!(o instanceof Localization)) {
return false;
}
Localization that = (Localization) o; final Localization that = (Localization) o;
return languageCode.equals(that.languageCode) && return languageCode.equals(that.languageCode)
Objects.equals(countryCode, that.countryCode); && Objects.equals(countryCode, that.countryCode);
} }
@Override @Override
@ -108,17 +122,19 @@ public class Localization implements Serializable {
* @param code a three letter language code * @param code a three letter language code
* @return the Locale corresponding * @return the Locale corresponding
*/ */
public static Locale getLocaleFromThreeLetterCode(@Nonnull String code) throws ParsingException { public static Locale getLocaleFromThreeLetterCode(@Nonnull final String code)
throws ParsingException {
final String[] languages = Locale.getISOLanguages(); final String[] languages = Locale.getISOLanguages();
final Map<String, Locale> localeMap = new HashMap<>(languages.length); final Map<String, Locale> localeMap = new HashMap<>(languages.length);
for (String language : languages) { for (final String language : languages) {
final Locale locale = new Locale(language); final Locale locale = new Locale(language);
localeMap.put(locale.getISO3Language(), locale); localeMap.put(locale.getISO3Language(), locale);
} }
if (localeMap.containsKey(code)) { if (localeMap.containsKey(code)) {
return localeMap.get(code); return localeMap.get(code);
} else { } else {
throw new ParsingException("Could not get Locale from this three letter language code" + code); throw new ParsingException(
"Could not get Locale from this three letter language code" + code);
} }
} }
} }

View File

@ -25,16 +25,17 @@ public class TimeAgoParser {
* Instantiate a new {@link TimeAgoParser} every time you extract a new batch of items. * Instantiate a new {@link TimeAgoParser} every time you extract a new batch of items.
* </p> * </p>
* *
* @param patternsHolder An object that holds the "time ago" patterns, special cases, and the language word separator. * @param patternsHolder An object that holds the "time ago" patterns, special cases, and the
* language word separator.
*/ */
public TimeAgoParser(PatternsHolder patternsHolder) { public TimeAgoParser(final PatternsHolder patternsHolder) {
this.patternsHolder = patternsHolder; this.patternsHolder = patternsHolder;
now = OffsetDateTime.now(ZoneOffset.UTC); now = OffsetDateTime.now(ZoneOffset.UTC);
} }
/** /**
* Parses a textual date in the format '2 days ago' into a Calendar representation which is then wrapped in a * Parses a textual date in the format '2 days ago' into a Calendar representation which is then
* {@link DateWrapper} object. * wrapped in a {@link DateWrapper} object.
* <p> * <p>
* Beginning with days ago, the date is considered as an approximation. * Beginning with days ago, the date is considered as an approximation.
* *
@ -42,10 +43,12 @@ public class TimeAgoParser {
* @return The parsed time (can be approximated) * @return The parsed time (can be approximated)
* @throws ParsingException if the time unit could not be recognized * @throws ParsingException if the time unit could not be recognized
*/ */
public DateWrapper parse(String textualDate) throws ParsingException { public DateWrapper parse(final String textualDate) throws ParsingException {
for (Map.Entry<ChronoUnit, Map<String, Integer>> caseUnitEntry : patternsHolder.specialCases().entrySet()) { for (final Map.Entry<ChronoUnit, Map<String, Integer>> caseUnitEntry
: patternsHolder.specialCases().entrySet()) {
final ChronoUnit chronoUnit = caseUnitEntry.getKey(); final ChronoUnit chronoUnit = caseUnitEntry.getKey();
for (Map.Entry<String, Integer> caseMapToAmountEntry : caseUnitEntry.getValue().entrySet()) { for (final Map.Entry<String, Integer> caseMapToAmountEntry
: caseUnitEntry.getValue().entrySet()) {
final String caseText = caseMapToAmountEntry.getKey(); final String caseText = caseMapToAmountEntry.getKey();
final Integer caseAmount = caseMapToAmountEntry.getValue(); final Integer caseAmount = caseMapToAmountEntry.getValue();
@ -58,7 +61,7 @@ public class TimeAgoParser {
int timeAgoAmount; int timeAgoAmount;
try { try {
timeAgoAmount = parseTimeAgoAmount(textualDate); timeAgoAmount = parseTimeAgoAmount(textualDate);
} catch (NumberFormatException e) { } catch (final NumberFormatException e) {
// If there is no valid number in the textual date, // If there is no valid number in the textual date,
// assume it is 1 (as in 'a second ago'). // assume it is 1 (as in 'a second ago').
timeAgoAmount = 1; timeAgoAmount = 1;
@ -68,16 +71,16 @@ public class TimeAgoParser {
return getResultFor(timeAgoAmount, chronoUnit); return getResultFor(timeAgoAmount, chronoUnit);
} }
private int parseTimeAgoAmount(String textualDate) throws NumberFormatException { private int parseTimeAgoAmount(final String textualDate) throws NumberFormatException {
String timeValueStr = textualDate.replaceAll("\\D+", ""); return Integer.parseInt(textualDate.replaceAll("\\D+", ""));
return Integer.parseInt(timeValueStr);
} }
private ChronoUnit parseChronoUnit(String textualDate) throws ParsingException { private ChronoUnit parseChronoUnit(final String textualDate) throws ParsingException {
for (Map.Entry<ChronoUnit, Collection<String>> entry : patternsHolder.asMap().entrySet()) { for (final Map.Entry<ChronoUnit, Collection<String>> entry
: patternsHolder.asMap().entrySet()) {
final ChronoUnit chronoUnit = entry.getKey(); final ChronoUnit chronoUnit = entry.getKey();
for (String agoPhrase : entry.getValue()) { for (final String agoPhrase : entry.getValue()) {
if (textualDateMatches(textualDate, agoPhrase)) { if (textualDateMatches(textualDate, agoPhrase)) {
return chronoUnit; return chronoUnit;
} }
@ -87,7 +90,7 @@ public class TimeAgoParser {
throw new ParsingException("Unable to parse the date: " + textualDate); throw new ParsingException("Unable to parse the date: " + textualDate);
} }
private boolean textualDateMatches(String textualDate, String agoPhrase) { private boolean textualDateMatches(final String textualDate, final String agoPhrase) {
if (textualDate.equals(agoPhrase)) { if (textualDate.equals(agoPhrase)) {
return true; return true;
} }
@ -98,7 +101,8 @@ public class TimeAgoParser {
final String escapedPhrase = Pattern.quote(agoPhrase.toLowerCase()); final String escapedPhrase = Pattern.quote(agoPhrase.toLowerCase());
final String escapedSeparator; final String escapedSeparator;
if (patternsHolder.wordSeparator().equals(" ")) { if (patternsHolder.wordSeparator().equals(" ")) {
// From JDK8 \h - Treat horizontal spaces as a normal one (non-breaking space, thin space, etc.) // From JDK8 \h - Treat horizontal spaces as a normal one
// (non-breaking space, thin space, etc.)
escapedSeparator = "[ \\t\\xA0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000]"; escapedSeparator = "[ \\t\\xA0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000]";
} else { } else {
escapedSeparator = Pattern.quote(patternsHolder.wordSeparator()); escapedSeparator = Pattern.quote(patternsHolder.wordSeparator());
@ -113,7 +117,7 @@ public class TimeAgoParser {
} }
} }
private DateWrapper getResultFor(int timeAgoAmount, ChronoUnit chronoUnit) { private DateWrapper getResultFor(final int timeAgoAmount, final ChronoUnit chronoUnit) {
OffsetDateTime offsetDateTime = now; OffsetDateTime offsetDateTime = now;
boolean isApproximation = false; boolean isApproximation = false;

View File

@ -6,14 +6,18 @@ import org.schabi.newpipe.extractor.timeago.PatternsManager;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class TimeAgoPatternsManager { public final class TimeAgoPatternsManager {
@Nullable private TimeAgoPatternsManager() {
private static PatternsHolder getPatternsFor(@Nonnull Localization localization) {
return PatternsManager.getPatterns(localization.getLanguageCode(), localization.getCountryCode());
} }
@Nullable @Nullable
public static TimeAgoParser getTimeAgoParserFor(@Nonnull Localization localization) { private static PatternsHolder getPatternsFor(@Nonnull final Localization localization) {
return PatternsManager.getPatterns(localization.getLanguageCode(),
localization.getCountryCode());
}
@Nullable
public static TimeAgoParser getTimeAgoParserFor(@Nonnull final Localization localization) {
final PatternsHolder holder = getPatternsFor(localization); final PatternsHolder holder = getPatternsFor(localization);
if (holder == null) { if (holder == null) {

View File

@ -15,7 +15,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class PlaylistInfo extends ListInfo<StreamInfoItem> { public final class PlaylistInfo extends ListInfo<StreamInfoItem> {
/** /**
* Mixes are handled as particular playlists in NewPipeExtractor. {@link PlaylistType#NORMAL} is * Mixes are handled as particular playlists in NewPipeExtractor. {@link PlaylistType#NORMAL} is
@ -52,23 +52,27 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
MIX_GENRE, MIX_GENRE,
} }
private PlaylistInfo(int serviceId, ListLinkHandler linkHandler, String name) throws ParsingException { @SuppressWarnings("RedundantThrows")
private PlaylistInfo(final int serviceId, final ListLinkHandler linkHandler, final String name)
throws ParsingException {
super(serviceId, linkHandler, name); super(serviceId, linkHandler, name);
} }
public static PlaylistInfo getInfo(String url) throws IOException, ExtractionException { public static PlaylistInfo getInfo(final String url) throws IOException, ExtractionException {
return getInfo(NewPipe.getServiceByUrl(url), url); return getInfo(NewPipe.getServiceByUrl(url), url);
} }
public static PlaylistInfo getInfo(StreamingService service, String url) throws IOException, ExtractionException { public static PlaylistInfo getInfo(final StreamingService service, final String url)
PlaylistExtractor extractor = service.getPlaylistExtractor(url); throws IOException, ExtractionException {
final PlaylistExtractor extractor = service.getPlaylistExtractor(url);
extractor.fetchPage(); extractor.fetchPage();
return getInfo(extractor); return getInfo(extractor);
} }
public static InfoItemsPage<StreamInfoItem> getMoreItems(StreamingService service, public static InfoItemsPage<StreamInfoItem> getMoreItems(final StreamingService service,
String url, final String url,
Page page) throws IOException, ExtractionException { final Page page)
throws IOException, ExtractionException {
return service.getPlaylistExtractor(url).getPage(page); return service.getPlaylistExtractor(url).getPage(page);
} }
@ -77,7 +81,8 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
* *
* @param extractor an extractor where fetchPage() was already got called on. * @param extractor an extractor where fetchPage() was already got called on.
*/ */
public static PlaylistInfo getInfo(PlaylistExtractor extractor) throws ExtractionException { public static PlaylistInfo getInfo(final PlaylistExtractor extractor)
throws ExtractionException {
final PlaylistInfo info = new PlaylistInfo( final PlaylistInfo info = new PlaylistInfo(
extractor.getServiceId(), extractor.getServiceId(),
@ -85,73 +90,75 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
extractor.getName()); extractor.getName());
// collect uploader extraction failures until we are sure this is not // collect uploader extraction failures until we are sure this is not
// just a playlist without an uploader // just a playlist without an uploader
List<Throwable> uploaderParsingErrors = new ArrayList<Throwable>(3); final List<Throwable> uploaderParsingErrors = new ArrayList<>();
try { try {
info.setOriginalUrl(extractor.getOriginalUrl()); info.setOriginalUrl(extractor.getOriginalUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setStreamCount(extractor.getStreamCount()); info.setStreamCount(extractor.getStreamCount());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setThumbnailUrl(extractor.getThumbnailUrl()); info.setThumbnailUrl(extractor.getThumbnailUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setUploaderUrl(extractor.getUploaderUrl()); info.setUploaderUrl(extractor.getUploaderUrl());
} catch (Exception e) { } catch (final Exception e) {
info.setUploaderUrl(""); info.setUploaderUrl("");
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try { try {
info.setUploaderName(extractor.getUploaderName()); info.setUploaderName(extractor.getUploaderName());
} catch (Exception e) { } catch (final Exception e) {
info.setUploaderName(""); info.setUploaderName("");
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try { try {
info.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl()); info.setUploaderAvatarUrl(extractor.getUploaderAvatarUrl());
} catch (Exception e) { } catch (final Exception e) {
info.setUploaderAvatarUrl(""); info.setUploaderAvatarUrl("");
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try { try {
info.setSubChannelUrl(extractor.getSubChannelUrl()); info.setSubChannelUrl(extractor.getSubChannelUrl());
} catch (Exception e) { } catch (final Exception e) {
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try { try {
info.setSubChannelName(extractor.getSubChannelName()); info.setSubChannelName(extractor.getSubChannelName());
} catch (Exception e) { } catch (final Exception e) {
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try { try {
info.setSubChannelAvatarUrl(extractor.getSubChannelAvatarUrl()); info.setSubChannelAvatarUrl(extractor.getSubChannelAvatarUrl());
} catch (Exception e) { } catch (final Exception e) {
uploaderParsingErrors.add(e); uploaderParsingErrors.add(e);
} }
try { try {
info.setBannerUrl(extractor.getBannerUrl()); info.setBannerUrl(extractor.getBannerUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setPlaylistType(extractor.getPlaylistType()); info.setPlaylistType(extractor.getPlaylistType());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
// do not fail if everything but the uploader infos could be collected
if (!uploaderParsingErrors.isEmpty() && // do not fail if everything but the uploader infos could be collected (TODO better comment)
(!info.getErrors().isEmpty() || uploaderParsingErrors.size() < 3)) { if (!uploaderParsingErrors.isEmpty()
&& (!info.getErrors().isEmpty() || uploaderParsingErrors.size() < 3)) {
info.addAllErrors(uploaderParsingErrors); info.addAllErrors(uploaderParsingErrors);
} }
final InfoItemsPage<StreamInfoItem> itemsPage = ExtractorHelper.getItemsPageOrLogError(info, extractor); final InfoItemsPage<StreamInfoItem> itemsPage
= ExtractorHelper.getItemsPageOrLogError(info, extractor);
info.setRelatedItems(itemsPage.getItems()); info.setRelatedItems(itemsPage.getItems());
info.setNextPage(itemsPage.getNextPage()); info.setNextPage(itemsPage.getNextPage());
@ -173,7 +180,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return thumbnailUrl; return thumbnailUrl;
} }
public void setThumbnailUrl(String thumbnailUrl) { public void setThumbnailUrl(final String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl; this.thumbnailUrl = thumbnailUrl;
} }
@ -181,7 +188,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return bannerUrl; return bannerUrl;
} }
public void setBannerUrl(String bannerUrl) { public void setBannerUrl(final String bannerUrl) {
this.bannerUrl = bannerUrl; this.bannerUrl = bannerUrl;
} }
@ -189,7 +196,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return uploaderUrl; return uploaderUrl;
} }
public void setUploaderUrl(String uploaderUrl) { public void setUploaderUrl(final String uploaderUrl) {
this.uploaderUrl = uploaderUrl; this.uploaderUrl = uploaderUrl;
} }
@ -197,7 +204,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return uploaderName; return uploaderName;
} }
public void setUploaderName(String uploaderName) { public void setUploaderName(final String uploaderName) {
this.uploaderName = uploaderName; this.uploaderName = uploaderName;
} }
@ -205,7 +212,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return uploaderAvatarUrl; return uploaderAvatarUrl;
} }
public void setUploaderAvatarUrl(String uploaderAvatarUrl) { public void setUploaderAvatarUrl(final String uploaderAvatarUrl) {
this.uploaderAvatarUrl = uploaderAvatarUrl; this.uploaderAvatarUrl = uploaderAvatarUrl;
} }
@ -213,7 +220,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return subChannelUrl; return subChannelUrl;
} }
public void setSubChannelUrl(String subChannelUrl) { public void setSubChannelUrl(final String subChannelUrl) {
this.subChannelUrl = subChannelUrl; this.subChannelUrl = subChannelUrl;
} }
@ -221,7 +228,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return subChannelName; return subChannelName;
} }
public void setSubChannelName(String subChannelName) { public void setSubChannelName(final String subChannelName) {
this.subChannelName = subChannelName; this.subChannelName = subChannelName;
} }
@ -229,7 +236,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return subChannelAvatarUrl; return subChannelAvatarUrl;
} }
public void setSubChannelAvatarUrl(String subChannelAvatarUrl) { public void setSubChannelAvatarUrl(final String subChannelAvatarUrl) {
this.subChannelAvatarUrl = subChannelAvatarUrl; this.subChannelAvatarUrl = subChannelAvatarUrl;
} }
@ -237,7 +244,7 @@ public class PlaylistInfo extends ListInfo<StreamInfoItem> {
return streamCount; return streamCount;
} }
public void setStreamCount(long streamCount) { public void setStreamCount(final long streamCount) {
this.streamCount = streamCount; this.streamCount = streamCount;
} }

View File

@ -11,7 +11,7 @@ public class PlaylistInfoItem extends InfoItem {
private long streamCount = 0; private long streamCount = 0;
private PlaylistInfo.PlaylistType playlistType; private PlaylistInfo.PlaylistType playlistType;
public PlaylistInfoItem(int serviceId, String url, String name) { public PlaylistInfoItem(final int serviceId, final String url, final String name) {
super(InfoType.PLAYLIST, serviceId, url, name); super(InfoType.PLAYLIST, serviceId, url, name);
} }
@ -19,16 +19,16 @@ public class PlaylistInfoItem extends InfoItem {
return uploaderName; return uploaderName;
} }
public void setUploaderName(String uploader_name) { public void setUploaderName(final String uploaderName) {
this.uploaderName = uploader_name; this.uploaderName = uploaderName;
} }
public long getStreamCount() { public long getStreamCount() {
return streamCount; return streamCount;
} }
public void setStreamCount(long stream_count) { public void setStreamCount(final long streamCount) {
this.streamCount = stream_count; this.streamCount = streamCount;
} }
public PlaylistInfo.PlaylistType getPlaylistType() { public PlaylistInfo.PlaylistType getPlaylistType() {

View File

@ -10,14 +10,12 @@ public interface PlaylistInfoItemExtractor extends InfoItemExtractor {
/** /**
* Get the uploader name * Get the uploader name
* @return the uploader name * @return the uploader name
* @throws ParsingException
*/ */
String getUploaderName() throws ParsingException; String getUploaderName() throws ParsingException;
/** /**
* Get the number of streams * Get the number of streams
* @return the number of streams * @return the number of streams
* @throws ParsingException
*/ */
long getStreamCount() throws ParsingException; long getStreamCount() throws ParsingException;

View File

@ -3,39 +3,37 @@ package org.schabi.newpipe.extractor.playlist;
import org.schabi.newpipe.extractor.InfoItemsCollector; import org.schabi.newpipe.extractor.InfoItemsCollector;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
public class PlaylistInfoItemsCollector extends InfoItemsCollector<PlaylistInfoItem, PlaylistInfoItemExtractor> { public class PlaylistInfoItemsCollector
extends InfoItemsCollector<PlaylistInfoItem, PlaylistInfoItemExtractor> {
public PlaylistInfoItemsCollector(int serviceId) { public PlaylistInfoItemsCollector(final int serviceId) {
super(serviceId); super(serviceId);
} }
@Override @Override
public PlaylistInfoItem extract(PlaylistInfoItemExtractor extractor) throws ParsingException { public PlaylistInfoItem extract(final PlaylistInfoItemExtractor extractor)
throws ParsingException {
String name = extractor.getName(); final PlaylistInfoItem resultItem = new PlaylistInfoItem(
int serviceId = getServiceId(); getServiceId(), extractor.getUrl(), extractor.getName());
String url = extractor.getUrl();
PlaylistInfoItem resultItem = new PlaylistInfoItem(serviceId, url, name);
try { try {
resultItem.setUploaderName(extractor.getUploaderName()); resultItem.setUploaderName(extractor.getUploaderName());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setThumbnailUrl(extractor.getThumbnailUrl()); resultItem.setThumbnailUrl(extractor.getThumbnailUrl());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setStreamCount(extractor.getStreamCount()); resultItem.setStreamCount(extractor.getStreamCount());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
try { try {
resultItem.setPlaylistType(extractor.getPlaylistType()); resultItem.setPlaylistType(extractor.getPlaylistType());
} catch (Exception e) { } catch (final Exception e) {
addError(e); addError(e);
} }
return resultItem; return resultItem;

View File

@ -14,12 +14,12 @@ import java.util.List;
public abstract class SearchExtractor extends ListExtractor<InfoItem> { public abstract class SearchExtractor extends ListExtractor<InfoItem> {
public static class NothingFoundException extends ExtractionException { public static class NothingFoundException extends ExtractionException {
public NothingFoundException(String message) { public NothingFoundException(final String message) {
super(message); super(message);
} }
} }
public SearchExtractor(StreamingService service, SearchQueryHandler linkHandler) { public SearchExtractor(final StreamingService service, final SearchQueryHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@ -34,11 +34,11 @@ public abstract class SearchExtractor extends ListExtractor<InfoItem> {
* {@link SearchExtractor#isCorrectedSearch()} is true. * {@link SearchExtractor#isCorrectedSearch()} is true.
* *
* @return a suggestion to another query, the corrected query, or an empty String. * @return a suggestion to another query, the corrected query, or an empty String.
* @throws ParsingException
*/ */
@Nonnull @Nonnull
public abstract String getSearchSuggestion() throws ParsingException; public abstract String getSearchSuggestion() throws ParsingException;
@Nonnull
@Override @Override
public SearchQueryHandler getLinkHandler() { public SearchQueryHandler getLinkHandler() {
return (SearchQueryHandler) super.getLinkHandler(); return (SearchQueryHandler) super.getLinkHandler();
@ -66,8 +66,7 @@ public abstract class SearchExtractor extends ListExtractor<InfoItem> {
* Example: on YouTube, if you search for "Covid-19", * Example: on YouTube, if you search for "Covid-19",
* there is a box with information from the WHO about Covid-19 and a link to the WHO's website. * there is a box with information from the WHO about Covid-19 and a link to the WHO's website.
* @return additional meta information about the search query * @return additional meta information about the search query
* @throws ParsingException
*/ */
@Nonnull @Nonnull
public abstract List<MetaInfo> getMetaInfo() throws ParsingException; public abstract List<MetaInfo> getMetaInfo() throws ParsingException;
} }

View File

@ -1,6 +1,11 @@
package org.schabi.newpipe.extractor.search; package org.schabi.newpipe.extractor.search;
import org.schabi.newpipe.extractor.*; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.utils.ExtractorHelper; import org.schabi.newpipe.extractor.utils.ExtractorHelper;
@ -11,26 +16,29 @@ import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class SearchInfo extends ListInfo<InfoItem> { public class SearchInfo extends ListInfo<InfoItem> {
private String searchString; private final String searchString;
private String searchSuggestion; private String searchSuggestion;
private boolean isCorrectedSearch; private boolean isCorrectedSearch;
private List<MetaInfo> metaInfo; private List<MetaInfo> metaInfo;
public SearchInfo(int serviceId, public SearchInfo(final int serviceId,
SearchQueryHandler qIHandler, final SearchQueryHandler qIHandler,
String searchString) { final String searchString) {
super(serviceId, qIHandler, "Search"); super(serviceId, qIHandler, "Search");
this.searchString = searchString; this.searchString = searchString;
} }
public static SearchInfo getInfo(StreamingService service, SearchQueryHandler searchQuery) throws ExtractionException, IOException { public static SearchInfo getInfo(final StreamingService service,
SearchExtractor extractor = service.getSearchExtractor(searchQuery); final SearchQueryHandler searchQuery)
throws ExtractionException, IOException {
final SearchExtractor extractor = service.getSearchExtractor(searchQuery);
extractor.fetchPage(); extractor.fetchPage();
return getInfo(extractor); return getInfo(extractor);
} }
public static SearchInfo getInfo(SearchExtractor extractor) throws ExtractionException, IOException { public static SearchInfo getInfo(final SearchExtractor extractor)
throws ExtractionException, IOException {
final SearchInfo info = new SearchInfo( final SearchInfo info = new SearchInfo(
extractor.getServiceId(), extractor.getServiceId(),
extractor.getLinkHandler(), extractor.getLinkHandler(),
@ -38,26 +46,27 @@ public class SearchInfo extends ListInfo<InfoItem> {
try { try {
info.setOriginalUrl(extractor.getOriginalUrl()); info.setOriginalUrl(extractor.getOriginalUrl());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setSearchSuggestion(extractor.getSearchSuggestion()); info.setSearchSuggestion(extractor.getSearchSuggestion());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setIsCorrectedSearch(extractor.isCorrectedSearch()); info.setIsCorrectedSearch(extractor.isCorrectedSearch());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
try { try {
info.setMetaInfo(extractor.getMetaInfo()); info.setMetaInfo(extractor.getMetaInfo());
} catch (Exception e) { } catch (final Exception e) {
info.addError(e); info.addError(e);
} }
ListExtractor.InfoItemsPage<InfoItem> page = ExtractorHelper.getItemsPageOrLogError(info, extractor); final ListExtractor.InfoItemsPage<InfoItem> page
= ExtractorHelper.getItemsPageOrLogError(info, extractor);
info.setRelatedItems(page.getItems()); info.setRelatedItems(page.getItems());
info.setNextPage(page.getNextPage()); info.setNextPage(page.getNextPage());
@ -65,9 +74,9 @@ public class SearchInfo extends ListInfo<InfoItem> {
} }
public static ListExtractor.InfoItemsPage<InfoItem> getMoreItems(StreamingService service, public static ListExtractor.InfoItemsPage<InfoItem> getMoreItems(final StreamingService service,
SearchQueryHandler query, final SearchQueryHandler query,
Page page) final Page page)
throws IOException, ExtractionException { throws IOException, ExtractionException {
return service.getSearchExtractor(query).getPage(page); return service.getSearchExtractor(query).getPage(page);
} }
@ -85,11 +94,11 @@ public class SearchInfo extends ListInfo<InfoItem> {
return this.isCorrectedSearch; return this.isCorrectedSearch;
} }
public void setIsCorrectedSearch(boolean isCorrectedSearch) { public void setIsCorrectedSearch(final boolean isCorrectedSearch) {
this.isCorrectedSearch = isCorrectedSearch; this.isCorrectedSearch = isCorrectedSearch;
} }
public void setSearchSuggestion(String searchSuggestion) { public void setSearchSuggestion(final String searchSuggestion) {
this.searchSuggestion = searchSuggestion; this.searchSuggestion = searchSuggestion;
} }
@ -98,7 +107,7 @@ public class SearchInfo extends ListInfo<InfoItem> {
return metaInfo; return metaInfo;
} }
public void setMetaInfo(@Nonnull List<MetaInfo> metaInfo) { public void setMetaInfo(@Nonnull final List<MetaInfo> metaInfo) {
this.metaInfo = metaInfo; this.metaInfo = metaInfo;
} }
} }

View File

@ -2,23 +2,6 @@
package org.schabi.newpipe.extractor.services.bandcamp; package org.schabi.newpipe.extractor.services.bandcamp;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.extractor.linkhandler.*;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.*;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.*;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.util.Arrays;
import java.util.Collections;
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; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL; import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
@ -27,6 +10,41 @@ import static org.schabi.newpipe.extractor.services.bandcamp.extractors.Bandcamp
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.KIOSK_RADIO; import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.KIOSK_RADIO;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.RADIO_API_URL; import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.RADIO_API_URL;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampChannelExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampCommentsExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampFeaturedExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampPlaylistExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioStreamExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampSearchExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampStreamExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampSuggestionExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampCommentsLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampFeaturedLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampPlaylistLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.util.Arrays;
public class BandcampService extends StreamingService { public class BandcampService extends StreamingService {
public BandcampService(final int id) { public BandcampService(final int id) {
@ -81,19 +99,28 @@ public class BandcampService extends StreamingService {
@Override @Override
public KioskList getKioskList() throws ExtractionException { public KioskList getKioskList() throws ExtractionException {
KioskList kioskList = new KioskList(this); final KioskList kioskList = new KioskList(this);
try { try {
kioskList.addKioskEntry((streamingService, url, kioskId) -> kioskList.addKioskEntry(
new BandcampFeaturedExtractor( (streamingService, url, kioskId) -> new BandcampFeaturedExtractor(
BandcampService.this, BandcampService.this,
new BandcampFeaturedLinkHandlerFactory().fromUrl(FEATURED_API_URL), kioskId), new BandcampFeaturedLinkHandlerFactory().fromUrl(FEATURED_API_URL),
new BandcampFeaturedLinkHandlerFactory(), KIOSK_FEATURED); kioskId
),
new BandcampFeaturedLinkHandlerFactory(),
KIOSK_FEATURED
);
kioskList.addKioskEntry((streamingService, url, kioskId) -> kioskList.addKioskEntry(
new BandcampRadioExtractor(BandcampService.this, (streamingService, url, kioskId) -> new BandcampRadioExtractor(
new BandcampFeaturedLinkHandlerFactory().fromUrl(RADIO_API_URL), kioskId), BandcampService.this,
new BandcampFeaturedLinkHandlerFactory(), KIOSK_RADIO); new BandcampFeaturedLinkHandlerFactory().fromUrl(RADIO_API_URL),
kioskId
),
new BandcampFeaturedLinkHandlerFactory(),
KIOSK_RADIO
);
kioskList.setDefaultKiosk(KIOSK_FEATURED); kioskList.setDefaultKiosk(KIOSK_FEATURED);
@ -116,14 +143,14 @@ public class BandcampService extends StreamingService {
@Override @Override
public StreamExtractor getStreamExtractor(final LinkHandler linkHandler) { public StreamExtractor getStreamExtractor(final LinkHandler linkHandler) {
if (BandcampExtractorHelper.isRadioUrl(linkHandler.getUrl())) if (BandcampExtractorHelper.isRadioUrl(linkHandler.getUrl())) {
return new BandcampRadioStreamExtractor(this, linkHandler); return new BandcampRadioStreamExtractor(this, linkHandler);
else }
return new BandcampStreamExtractor(this, linkHandler); return new BandcampStreamExtractor(this, linkHandler);
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) { public CommentsExtractor getCommentsExtractor(final ListLinkHandler linkHandler) {
return new BandcampCommentsExtractor(this, linkHandler); return new BandcampCommentsExtractor(this, linkHandler);
} }
} }

View File

@ -4,6 +4,7 @@ package org.schabi.newpipe.extractor.services.bandcamp.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
@ -17,20 +18,24 @@ import org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem.
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 javax.annotation.Nonnull;
public class BandcampChannelExtractor extends ChannelExtractor { public class BandcampChannelExtractor extends ChannelExtractor {
private JsonObject channelInfo; private JsonObject channelInfo;
public BandcampChannelExtractor(final StreamingService service, final ListLinkHandler linkHandler) { public BandcampChannelExtractor(final StreamingService service,
final ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@Override @Override
public String getAvatarUrl() { public String getAvatarUrl() {
if (channelInfo.getLong("bio_image_id") == 0) return ""; if (channelInfo.getLong("bio_image_id") == 0) {
return "";
}
return BandcampExtractorHelper.getImageUrl(channelInfo.getLong("bio_image_id"), false); return BandcampExtractorHelper.getImageUrl(channelInfo.getLong("bio_image_id"), false);
} }
@ -43,7 +48,8 @@ public class BandcampChannelExtractor extends ChannelExtractor {
*/ */
try { try {
final String html = getDownloader() final String html = getDownloader()
.get(channelInfo.getString("bandcamp_url").replace("http://", "https://")) .get(channelInfo.getString("bandcamp_url")
.replace("http://", "https://"))
.responseBody(); .responseBody();
return Jsoup.parse(html) return Jsoup.parse(html)
@ -110,7 +116,9 @@ public class BandcampChannelExtractor extends ChannelExtractor {
// A discograph is as an item appears in a discography // A discograph is as an item appears in a discography
final JsonObject discograph = discography.getObject(i); final JsonObject discograph = discography.getObject(i);
if (!discograph.getString("item_type").equals("track")) continue; if (!discograph.getString("item_type").equals("track")) {
continue;
}
collector.commit(new BandcampDiscographStreamInfoItemExtractor(discograph, getUrl())); collector.commit(new BandcampDiscographStreamInfoItemExtractor(discograph, getUrl()));
} }
@ -119,12 +127,13 @@ public class BandcampChannelExtractor extends ChannelExtractor {
} }
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(Page page) { public InfoItemsPage<StreamInfoItem> getPage(final Page page) {
return null; return null;
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
channelInfo = BandcampExtractorHelper.getArtistDetails(getId()); channelInfo = BandcampExtractorHelper.getArtistDetails(getId());
} }

View File

@ -8,7 +8,8 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
public class BandcampChannelInfoItemExtractor implements ChannelInfoItemExtractor { public class BandcampChannelInfoItemExtractor implements ChannelInfoItemExtractor {
private final Element resultInfo, searchResult; private final Element resultInfo;
private final Element searchResult;
public BandcampChannelInfoItemExtractor(final Element searchResult) { public BandcampChannelInfoItemExtractor(final Element searchResult) {
this.searchResult = searchResult; this.searchResult = searchResult;

View File

@ -21,25 +21,27 @@ public class BandcampCommentsExtractor extends CommentsExtractor {
private Document document; private Document document;
public BandcampCommentsExtractor(StreamingService service, ListLinkHandler linkHandler) { public BandcampCommentsExtractor(final StreamingService service,
final ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
String html = downloader.get(getLinkHandler().getUrl()).responseBody(); throws IOException, ExtractionException {
document = Jsoup.parse(html); document = Jsoup.parse(downloader.get(getLinkHandler().getUrl()).responseBody());
} }
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getInitialPage()
throws IOException, ExtractionException {
CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId()); final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId());
Elements writings = document.getElementsByClass("writing"); final Elements writings = document.getElementsByClass("writing");
for (Element writing : writings) { for (final Element writing : writings) {
collector.commit(new BandcampCommentsInfoItemExtractor(writing, getUrl())); collector.commit(new BandcampCommentsInfoItemExtractor(writing, getUrl()));
} }
@ -47,7 +49,8 @@ public class BandcampCommentsExtractor extends CommentsExtractor {
} }
@Override @Override
public InfoItemsPage<CommentsInfoItem> getPage(Page page) throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getPage(final Page page)
throws IOException, ExtractionException {
return null; return null;
} }
} }

View File

@ -9,7 +9,7 @@ public class BandcampCommentsInfoItemExtractor implements CommentsInfoItemExtrac
private final Element writing; private final Element writing;
private final String url; private final String url;
public BandcampCommentsInfoItemExtractor(Element writing, String url) { public BandcampCommentsInfoItemExtractor(final Element writing, final String url) {
this.writing = writing; this.writing = writing;
this.url = url; this.url = url;
} }

View File

@ -6,6 +6,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.jsoup.Jsoup; import org.jsoup.Jsoup;
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;
@ -18,18 +19,21 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Locale; import java.util.Locale;
public class BandcampExtractorHelper { public final class BandcampExtractorHelper {
public static final String BASE_URL = "https://bandcamp.com"; public static final String BASE_URL = "https://bandcamp.com";
public static final String BASE_API_URL = BASE_URL + "/api"; public static final String BASE_API_URL = BASE_URL + "/api";
private BandcampExtractorHelper() {
}
/** /**
* Translate all these parameters together to the URL of the corresponding album or track * Translate all these parameters together to the URL of the corresponding album or track
* using the mobile API * using the mobile API
*/ */
public static String getStreamUrlFromIds(final long bandId, final long itemId, final String itemType) public static String getStreamUrlFromIds(final long bandId,
throws ParsingException { final long itemId,
final String itemType) throws ParsingException {
try { try {
final String jsonString = NewPipe.getDownloader().get( final String jsonString = NewPipe.getDownloader().get(
BASE_API_URL + "/mobile/22/tralbum_details?band_id=" + bandId BASE_API_URL + "/mobile/22/tralbum_details?band_id=" + bandId
@ -50,7 +54,7 @@ public class BandcampExtractorHelper {
* <a href=https://notabug.org/fynngodau/bandcampDirect/wiki/rewindBandcamp+%E2%80%93+Fetching+artist+details> * <a href=https://notabug.org/fynngodau/bandcampDirect/wiki/rewindBandcamp+%E2%80%93+Fetching+artist+details>
* More technical info.</a> * More technical info.</a>
*/ */
public static JsonObject getArtistDetails(String id) throws ParsingException { public static JsonObject getArtistDetails(final String id) throws ParsingException {
try { try {
return return
JsonParser.object().from( JsonParser.object().from(
@ -91,24 +95,24 @@ public class BandcampExtractorHelper {
public static boolean isSupportedDomain(final String url) throws ParsingException { public static boolean isSupportedDomain(final String url) throws ParsingException {
// Accept all bandcamp.com URLs // Accept all bandcamp.com URLs
if (url.toLowerCase().matches("https?://.+\\.bandcamp\\.com(/.*)?")) return true; if (url.toLowerCase().matches("https?://.+\\.bandcamp\\.com(/.*)?")) {
return true;
}
try { try {
// Test other URLs for whether they contain a footer that links to bandcamp // Test other URLs for whether they contain a footer that links to bandcamp
return Jsoup.parse( return Jsoup.parse(NewPipe.getDownloader().get(url).responseBody())
NewPipe.getDownloader().get(url).responseBody()
)
.getElementById("pgFt") .getElementById("pgFt")
.getElementById("pgFt-inner") .getElementById("pgFt-inner")
.getElementById("footer-logo-wrapper") .getElementById("footer-logo-wrapper")
.getElementById("footer-logo") .getElementById("footer-logo")
.getElementsByClass("hiddenAccess") .getElementsByClass("hiddenAccess")
.text().equals("Bandcamp"); .text().equals("Bandcamp");
} catch (NullPointerException e) { } catch (final NullPointerException e) {
return false; return false;
} catch (IOException | ReCaptchaException e) { } catch (final IOException | ReCaptchaException e) {
throw new ParsingException("Could not determine whether URL is custom domain " + throw new ParsingException("Could not determine whether URL is custom domain "
"(not available? network error?)"); + "(not available? network error?)");
} }
} }
@ -121,10 +125,10 @@ public class BandcampExtractorHelper {
return url.toLowerCase().matches("https?://bandcamp\\.com/\\?show=\\d+"); return url.toLowerCase().matches("https?://bandcamp\\.com/\\?show=\\d+");
} }
static DateWrapper parseDate(final String textDate) throws ParsingException { public static DateWrapper parseDate(final String textDate) throws ParsingException {
try { try {
final ZonedDateTime zonedDateTime = ZonedDateTime.parse( final ZonedDateTime zonedDateTime = ZonedDateTime.parse(textDate,
textDate, DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH)); DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH));
return new DateWrapper(zonedDateTime.toOffsetDateTime(), false); return new DateWrapper(zonedDateTime.toOffsetDateTime(), false);
} catch (final DateTimeException e) { } catch (final DateTimeException e) {
throw new ParsingException("Could not parse date '" + textDate + "'", e); throw new ParsingException("Could not parse date '" + textDate + "'", e);

View File

@ -25,17 +25,20 @@ public class BandcampFeaturedExtractor extends KioskExtractor<PlaylistInfoItem>
public static final String KIOSK_FEATURED = "Featured"; public static final String KIOSK_FEATURED = "Featured";
public static final String FEATURED_API_URL = BASE_API_URL + "/mobile/24/bootstrap_data"; public static final String FEATURED_API_URL = BASE_API_URL + "/mobile/24/bootstrap_data";
public static final String MORE_FEATURED_API_URL = BASE_API_URL + "/mobile/24/feed_older_logged_out"; public static final String MORE_FEATURED_API_URL
= BASE_API_URL + "/mobile/24/feed_older_logged_out";
private JsonObject json; private JsonObject json;
public BandcampFeaturedExtractor(final StreamingService streamingService, final ListLinkHandler listLinkHandler, public BandcampFeaturedExtractor(final StreamingService streamingService,
final ListLinkHandler listLinkHandler,
final String kioskId) { final String kioskId) {
super(streamingService, listLinkHandler, kioskId); super(streamingService, listLinkHandler, kioskId);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
try { try {
json = JsonParser.object().from( json = JsonParser.object().from(
getDownloader().post( getDownloader().post(
@ -55,9 +58,8 @@ public class BandcampFeaturedExtractor extends KioskExtractor<PlaylistInfoItem>
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<PlaylistInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<PlaylistInfoItem> getInitialPage()
throws IOException, ExtractionException {
final JsonArray featuredStories = json.getObject("feed_content") final JsonArray featuredStories = json.getObject("feed_content")
.getObject("stories") .getObject("stories")
.getArray("featured"); .getArray("featured");
@ -65,8 +67,7 @@ public class BandcampFeaturedExtractor extends KioskExtractor<PlaylistInfoItem>
return extractItems(featuredStories); return extractItems(featuredStories);
} }
private InfoItemsPage<PlaylistInfoItem> extractItems(JsonArray featuredStories) { private InfoItemsPage<PlaylistInfoItem> extractItems(final JsonArray featuredStories) {
final PlaylistInfoItemsCollector c = new PlaylistInfoItemsCollector(getServiceId()); final PlaylistInfoItemsCollector c = new PlaylistInfoItemsCollector(getServiceId());
for (int i = 0; i < featuredStories.size(); i++) { for (int i = 0; i < featuredStories.size(); i++) {
@ -81,14 +82,13 @@ public class BandcampFeaturedExtractor extends KioskExtractor<PlaylistInfoItem>
} }
final JsonObject lastFeaturedStory = featuredStories.getObject(featuredStories.size() - 1); final JsonObject lastFeaturedStory = featuredStories.getObject(featuredStories.size() - 1);
return new InfoItemsPage<>(c, getNextPageFrom(lastFeaturedStory)); return new InfoItemsPage<>(c, getNextPageFrom(lastFeaturedStory));
} }
/** /**
* Next Page can be generated from metadata of last featured story * Next Page can be generated from metadata of last featured story
*/ */
private Page getNextPageFrom(JsonObject lastFeaturedStory) { private Page getNextPageFrom(final JsonObject lastFeaturedStory) {
final long lastStoryDate = lastFeaturedStory.getLong("story_date"); final long lastStoryDate = lastFeaturedStory.getLong("story_date");
final long lastStoryId = lastFeaturedStory.getLong("ntid"); final long lastStoryId = lastFeaturedStory.getLong("ntid");
final String lastStoryType = lastFeaturedStory.getString("story_type"); final String lastStoryType = lastFeaturedStory.getString("story_type");
@ -99,9 +99,10 @@ public class BandcampFeaturedExtractor extends KioskExtractor<PlaylistInfoItem>
} }
@Override @Override
public InfoItemsPage<PlaylistInfoItem> getPage(Page page) throws IOException, ExtractionException { public InfoItemsPage<PlaylistInfoItem> getPage(final Page page)
throws IOException, ExtractionException {
JsonObject response; final JsonObject response;
try { try {
response = JsonParser.object().from( response = JsonParser.object().from(
getDownloader().get(page.getUrl()).responseBody() getDownloader().get(page.getUrl()).responseBody()

View File

@ -30,9 +30,9 @@ import static org.schabi.newpipe.extractor.utils.Utils.HTTPS;
public class BandcampPlaylistExtractor extends PlaylistExtractor { public class BandcampPlaylistExtractor extends PlaylistExtractor {
/** /**
* An arbitrarily chosen number above which cover arts won't be fetched individually for each track; * An arbitrarily chosen number above which cover arts won't be fetched individually for each
* instead, it will be assumed that every track has the same cover art as the album, which is not * track; instead, it will be assumed that every track has the same cover art as the album,
* always the case. * which is not always the case.
*/ */
private static final int MAXIMUM_INDIVIDUAL_COVER_ARTS = 10; private static final int MAXIMUM_INDIVIDUAL_COVER_ARTS = 10;
@ -41,12 +41,14 @@ public class BandcampPlaylistExtractor extends PlaylistExtractor {
private JsonArray trackInfo; private JsonArray trackInfo;
private String name; private String name;
public BandcampPlaylistExtractor(final StreamingService service, final ListLinkHandler linkHandler) { public BandcampPlaylistExtractor(final StreamingService service,
final ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
final String html = downloader.get(getLinkHandler().getUrl()).responseBody(); final String html = downloader.get(getLinkHandler().getUrl()).responseBody();
document = Jsoup.parse(html); document = Jsoup.parse(html);
albumJson = getAlbumInfoJson(html); albumJson = getAlbumInfoJson(html);
@ -115,7 +117,7 @@ public class BandcampPlaylistExtractor extends PlaylistExtractor {
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
for (int i = 0; i < trackInfo.size(); i++) { for (int i = 0; i < trackInfo.size(); i++) {
JsonObject track = trackInfo.getObject(i); final JsonObject track = trackInfo.getObject(i);
if (trackInfo.size() < MAXIMUM_INDIVIDUAL_COVER_ARTS) { if (trackInfo.size() < MAXIMUM_INDIVIDUAL_COVER_ARTS) {
// Load cover art of every track individually // Load cover art of every track individually

View File

@ -6,9 +6,10 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class BandcampPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor { public class BandcampPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
private final Element searchResult, resultInfo; private final Element searchResult;
private final Element resultInfo;
public BandcampPlaylistInfoItemExtractor(@Nonnull Element searchResult) { public BandcampPlaylistInfoItemExtractor(@Nonnull final Element searchResult) {
this.searchResult = searchResult; this.searchResult = searchResult;
resultInfo = searchResult.getElementsByClass("result-info").first(); resultInfo = searchResult.getElementsByClass("result-info").first();
} }
@ -41,6 +42,8 @@ public class BandcampPlaylistInfoItemExtractor implements PlaylistInfoItemExtrac
.getElementsByTag("img").first(); .getElementsByTag("img").first();
if (img != null) { if (img != null) {
return img.attr("src"); return img.attr("src");
} else return null; } else {
return null;
}
} }
} }

View File

@ -28,13 +28,15 @@ public class BandcampRadioExtractor extends KioskExtractor<StreamInfoItem> {
private JsonObject json = null; private JsonObject json = null;
public BandcampRadioExtractor(final StreamingService streamingService, final ListLinkHandler linkHandler, public BandcampRadioExtractor(final StreamingService streamingService,
final ListLinkHandler linkHandler,
final String kioskId) { final String kioskId) {
super(streamingService, linkHandler, kioskId); super(streamingService, linkHandler, kioskId);
} }
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
try { try {
json = JsonParser.object().from( json = JsonParser.object().from(
getDownloader().get(RADIO_API_URL).responseBody()); getDownloader().get(RADIO_API_URL).responseBody());

View File

@ -23,10 +23,9 @@ public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
@Override @Override
public long getDuration() { public long getDuration() {
/* Duration is only present in the more detailed information that has to be queried separately. /* Duration is only present in the more detailed information that has to be queried
* Therefore, over 300 queries would be needed every time the kiosk is opened if we were to separately. Therefore, over 300 queries would be needed every time the kiosk is opened if we
* display the real value. were to display the real value. */
*/
//return query(show.getInt("id")).getLong("audio_duration"); //return query(show.getInt("id")).getLong("audio_duration");
return 0; return 0;
} }

View File

@ -26,28 +26,31 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.*; import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_API_URL;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl;
public class BandcampRadioStreamExtractor extends BandcampStreamExtractor { public class BandcampRadioStreamExtractor extends BandcampStreamExtractor {
private JsonObject showInfo; private JsonObject showInfo;
public BandcampRadioStreamExtractor(final StreamingService service, final LinkHandler linkHandler) { public BandcampRadioStreamExtractor(final StreamingService service,
final LinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
static JsonObject query(final int id) throws ParsingException { static JsonObject query(final int id) throws ParsingException {
try { try {
return JsonParser.object().from( return JsonParser.object().from(NewPipe.getDownloader()
NewPipe.getDownloader().get(BASE_API_URL + "/bcweekly/1/get?id=" + id).responseBody() .get(BASE_API_URL + "/bcweekly/1/get?id=" + id).responseBody());
);
} catch (final IOException | ReCaptchaException | JsonParserException e) { } catch (final IOException | ReCaptchaException | JsonParserException e) {
throw new ParsingException("could not get show data", e); throw new ParsingException("could not get show data", e);
} }
} }
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
showInfo = query(Integer.parseInt(getId())); showInfo = query(Integer.parseInt(getId()));
} }

View File

@ -14,7 +14,7 @@ import javax.annotation.Nonnull;
public class BandcampRelatedPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor { public class BandcampRelatedPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
private final Element relatedAlbum; private final Element relatedAlbum;
public BandcampRelatedPlaylistInfoItemExtractor(@Nonnull Element relatedAlbum) { public BandcampRelatedPlaylistInfoItemExtractor(@Nonnull final Element relatedAlbum) {
this.relatedAlbum = relatedAlbum; this.relatedAlbum = relatedAlbum;
} }

View File

@ -26,7 +26,8 @@ import java.util.List;
public class BandcampSearchExtractor extends SearchExtractor { public class BandcampSearchExtractor extends SearchExtractor {
public BandcampSearchExtractor(StreamingService service, SearchQueryHandler linkHandler) { public BandcampSearchExtractor(final StreamingService service,
final SearchQueryHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@ -47,7 +48,8 @@ public class BandcampSearchExtractor extends SearchExtractor {
return Collections.emptyList(); return Collections.emptyList();
} }
public InfoItemsPage<InfoItem> getPage(final Page page) throws IOException, ExtractionException { public InfoItemsPage<InfoItem> getPage(final Page page)
throws IOException, ExtractionException {
final String html = getDownloader().get(page.getUrl()).responseBody(); final String html = getDownloader().get(page.getUrl()).responseBody();
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId()); final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
@ -86,8 +88,9 @@ public class BandcampSearchExtractor extends SearchExtractor {
// Count pages // Count pages
final Elements pageLists = d.getElementsByClass("pagelist"); final Elements pageLists = d.getElementsByClass("pagelist");
if (pageLists.isEmpty()) if (pageLists.isEmpty()) {
return new InfoItemsPage<>(collector, null); return new InfoItemsPage<>(collector, null);
}
final Elements pages = pageLists.first().getElementsByTag("li"); final Elements pages = pageLists.first().getElementsByTag("li");
@ -120,7 +123,7 @@ public class BandcampSearchExtractor extends SearchExtractor {
} }
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
} }
} }

View File

@ -2,14 +2,16 @@
package org.schabi.newpipe.extractor.services.bandcamp.extractors; package org.schabi.newpipe.extractor.services.bandcamp.extractors;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.MetaInfo;
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;
@ -17,19 +19,21 @@ 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.playlist.PlaylistInfoItemsCollector; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemsCollector;
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.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
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 org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class BandcampStreamExtractor extends StreamExtractor { public class BandcampStreamExtractor extends StreamExtractor {
@ -43,7 +47,8 @@ public class BandcampStreamExtractor extends StreamExtractor {
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
final String html = downloader.get(getLinkHandler().getUrl()).responseBody(); final String html = downloader.get(getLinkHandler().getUrl()).responseBody();
document = Jsoup.parse(html); document = Jsoup.parse(html);
albumJson = getAlbumInfoJson(html); albumJson = getAlbumInfoJson(html);
@ -94,7 +99,7 @@ public class BandcampStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public String getUploaderName() { public String getUploaderName() throws ParsingException {
return albumJson.getString("artist"); return albumJson.getString("artist");
} }
@ -113,8 +118,11 @@ public class BandcampStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public String getThumbnailUrl() throws ParsingException { public String getThumbnailUrl() throws ParsingException {
if (albumJson.isNull("art_id")) return ""; if (albumJson.isNull("art_id")) {
else return getImageUrl(albumJson.getLong("art_id"), true); return "";
} else {
return getImageUrl(albumJson.getLong("art_id"), true);
}
} }
@Nonnull @Nonnull
@ -170,15 +178,12 @@ public class BandcampStreamExtractor extends StreamExtractor {
@Override @Override
public PlaylistInfoItemsCollector getRelatedItems() { public PlaylistInfoItemsCollector getRelatedItems() {
final PlaylistInfoItemsCollector collector = new PlaylistInfoItemsCollector(getServiceId());
final Elements recommendedAlbums = document.getElementsByClass("recommended-album");
PlaylistInfoItemsCollector collector = new PlaylistInfoItemsCollector(getServiceId()); for (final Element album : recommendedAlbums) {
Elements recommendedAlbums = document.getElementsByClass("recommended-album");
for (Element album : recommendedAlbums) {
collector.commit(new BandcampRelatedPlaylistInfoItemExtractor(album)); collector.commit(new BandcampRelatedPlaylistInfoItemExtractor(album));
} }
return collector; return collector;
} }
@ -186,22 +191,21 @@ public class BandcampStreamExtractor extends StreamExtractor {
@Override @Override
public String getCategory() { public String getCategory() {
// Get first tag from html, which is the artist's Genre // Get first tag from html, which is the artist's Genre
return document return document.getElementsByClass("tralbum-tags").stream()
.getElementsByClass("tralbum-tags").first() .flatMap(element -> element.getElementsByClass("tag").stream())
.getElementsByClass("tag").first().text(); .map(Element::text)
.findFirst()
.orElse("");
} }
@Nonnull @Nonnull
@Override @Override
public String getLicence() { public String getLicence() {
/* Tests resulted in this mapping of ints to licence:
https://cloud.disroot.org/s/ZTWBxbQ9fKRmRWJ/preview (screenshot from a Bandcamp artist's
account) */
int license = current.getInt("license_type"); switch (current.getInt("license_type")) {
/* Tests resulted in this mapping of ints to licence: https://cloud.disroot.org/s/ZTWBxbQ9fKRmRWJ/preview
* (screenshot from a Bandcamp artist's account)
*/
switch (license) {
case 1: case 1:
return "All rights reserved ©"; return "All rights reserved ©";
case 2: case 2:

View File

@ -2,10 +2,13 @@
package org.schabi.newpipe.extractor.services.bandcamp.extractors; package org.schabi.newpipe.extractor.services.bandcamp.extractors;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_API_URL;
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 com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
@ -18,8 +21,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_API_URL;
public class BandcampSuggestionExtractor extends SuggestionExtractor { public class BandcampSuggestionExtractor extends SuggestionExtractor {
private static final String AUTOCOMPLETE_URL = BASE_API_URL + "/fuzzysearch/1/autocomplete?q="; private static final String AUTOCOMPLETE_URL = BASE_API_URL + "/fuzzysearch/1/autocomplete?q=";
@ -32,9 +33,8 @@ public class BandcampSuggestionExtractor extends SuggestionExtractor {
final Downloader downloader = NewPipe.getDownloader(); final Downloader downloader = NewPipe.getDownloader();
try { try {
final JsonObject fuzzyResults = JsonParser.object().from( final JsonObject fuzzyResults = JsonParser.object().from(downloader
downloader.get(AUTOCOMPLETE_URL + URLEncoder.encode(query, "UTF-8")).responseBody() .get(AUTOCOMPLETE_URL + URLEncoder.encode(query, "UTF-8")).responseBody());
);
final JsonArray jsonArray = fuzzyResults.getObject("auto") final JsonArray jsonArray = fuzzyResults.getObject("auto")
.getArray("results"); .getArray("results");
@ -44,7 +44,9 @@ public class BandcampSuggestionExtractor extends SuggestionExtractor {
for (final Object fuzzyResult : jsonArray) { for (final Object fuzzyResult : jsonArray) {
final String res = ((JsonObject) fuzzyResult).getString("name"); final String res = ((JsonObject) fuzzyResult).getString("name");
if (!suggestions.contains(res)) suggestions.add(res); if (!suggestions.contains(res)) {
suggestions.add(res);
}
} }
return suggestions; return suggestions;

View File

@ -9,9 +9,9 @@ import javax.annotation.Nullable;
public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor { public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
private final JsonObject discograph; private final JsonObject discograph;
public BandcampDiscographStreamInfoItemExtractor(final JsonObject discograph, final String uploaderUrl) { public BandcampDiscographStreamInfoItemExtractor(final JsonObject discograph,
final String uploaderUrl) {
super(uploaderUrl); super(uploaderUrl);
this.discograph = discograph; this.discograph = discograph;
} }

View File

@ -18,14 +18,16 @@ public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoI
private String substituteCoverUrl; private String substituteCoverUrl;
private final StreamingService service; private final StreamingService service;
public BandcampPlaylistStreamInfoItemExtractor(final JsonObject track, final String uploaderUrl, public BandcampPlaylistStreamInfoItemExtractor(final JsonObject track,
final String uploaderUrl,
final StreamingService service) { final StreamingService service) {
super(uploaderUrl); super(uploaderUrl);
this.track = track; this.track = track;
this.service = service; this.service = service;
} }
public BandcampPlaylistStreamInfoItemExtractor(final JsonObject track, final String uploaderUrl, public BandcampPlaylistStreamInfoItemExtractor(final JsonObject track,
final String uploaderUrl,
final String substituteCoverUrl) { final String substituteCoverUrl) {
this(track, uploaderUrl, (StreamingService) null); this(track, uploaderUrl, (StreamingService) null);
this.substituteCoverUrl = substituteCoverUrl; this.substituteCoverUrl = substituteCoverUrl;

View File

@ -7,9 +7,11 @@ import javax.annotation.Nullable;
public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor { public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
private final Element resultInfo, searchResult; private final Element resultInfo;
private final Element searchResult;
public BandcampSearchStreamInfoItemExtractor(final Element searchResult, final String uploaderUrl) { public BandcampSearchStreamInfoItemExtractor(final Element searchResult,
final String uploaderUrl) {
super(uploaderUrl); super(uploaderUrl);
this.searchResult = searchResult; this.searchResult = searchResult;
resultInfo = searchResult.getElementsByClass("result-info").first(); resultInfo = searchResult.getElementsByClass("result-info").first();

View File

@ -30,7 +30,8 @@ public class BandcampChannelLinkHandlerFactory extends ListLinkHandlerFactory {
return String.valueOf(bandData.getLong("id")); return String.valueOf(bandData.getLong("id"));
} catch (final IOException | ReCaptchaException | ArrayIndexOutOfBoundsException | JsonParserException e) { } catch (final IOException | ReCaptchaException | ArrayIndexOutOfBoundsException
| JsonParserException e) {
throw new ParsingException("Download failed", e); throw new ParsingException("Download failed", e);
} }
} }
@ -46,7 +47,8 @@ public class BandcampChannelLinkHandlerFactory extends ListLinkHandlerFactory {
.getString("bandcamp_url") .getString("bandcamp_url")
.replace("http://", "https://"); .replace("http://", "https://");
} catch (final NullPointerException e) { } catch (final NullPointerException e) {
throw new ParsingException("JSON does not contain URL (invalid id?) or is otherwise invalid", e); throw new ParsingException(
"JSON does not contain URL (invalid id?) or is otherwise invalid", e);
} }
} }
@ -55,16 +57,18 @@ public class BandcampChannelLinkHandlerFactory extends ListLinkHandlerFactory {
* Accepts only pages that lead to the root of an artist profile. Supports external pages. * Accepts only pages that lead to the root of an artist profile. Supports external pages.
*/ */
@Override @Override
public boolean onAcceptUrl(String url) throws ParsingException { public boolean onAcceptUrl(final String url) throws ParsingException {
url = url.toLowerCase(); final String lowercaseUrl = url.toLowerCase();
// https: | | artist.bandcamp.com | releases // https: | | artist.bandcamp.com | releases
// 0 1 2 3 // 0 1 2 3
String[] splitUrl = url.split("/"); final String[] splitUrl = lowercaseUrl.split("/");
// URL is too short // URL is too short
if (splitUrl.length < 3) return false; if (splitUrl.length < 3) {
return false;
}
// Must have "releases" or "music" as segment after url or none at all // Must have "releases" or "music" as segment after url or none at all
if (splitUrl.length > 3 && !( if (splitUrl.length > 3 && !(
@ -80,7 +84,7 @@ public class BandcampChannelLinkHandlerFactory extends ListLinkHandlerFactory {
} }
// Test whether domain is supported // Test whether domain is supported
return BandcampExtractorHelper.isSupportedDomain(url); return BandcampExtractorHelper.isSupportedDomain(lowercaseUrl);
} }
} }
} }

View File

@ -13,21 +13,25 @@ import java.util.List;
public class BandcampCommentsLinkHandlerFactory extends ListLinkHandlerFactory { public class BandcampCommentsLinkHandlerFactory extends ListLinkHandlerFactory {
@Override @Override
public String getId(String url) throws ParsingException { public String getId(final String url) throws ParsingException {
return url; return url;
} }
@Override @Override
public boolean onAcceptUrl(String url) throws ParsingException { public boolean onAcceptUrl(final String url) throws ParsingException {
// Don't accept URLs that don't point to a track // Don't accept URLs that don't point to a track
if (!url.toLowerCase().matches("https?://.+\\..+/(track|album)/.+")) return false; if (!url.toLowerCase().matches("https?://.+\\..+/(track|album)/.+")) {
return false;
}
// Test whether domain is supported // Test whether domain is supported
return BandcampExtractorHelper.isSupportedDomain(url); return BandcampExtractorHelper.isSupportedDomain(url);
} }
@Override @Override
public String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException { public String getUrl(final String id,
final List<String> contentFilter,
final String sortFilter) throws ParsingException {
return id; return id;
} }
} }

View File

@ -16,7 +16,9 @@ import static org.schabi.newpipe.extractor.services.bandcamp.extractors.Bandcamp
public class BandcampFeaturedLinkHandlerFactory extends ListLinkHandlerFactory { public class BandcampFeaturedLinkHandlerFactory extends ListLinkHandlerFactory {
@Override @Override
public String getUrl(final String id, final List<String> contentFilter, final String sortFilter) { public String getUrl(final String id,
final List<String> contentFilter,
final String sortFilter) {
if (id.equals(KIOSK_FEATURED)) { if (id.equals(KIOSK_FEATURED)) {
return FEATURED_API_URL; // doesn't have a website return FEATURED_API_URL; // doesn't have a website
} else if (id.equals(KIOSK_RADIO)) { } else if (id.equals(KIOSK_RADIO)) {
@ -27,11 +29,11 @@ public class BandcampFeaturedLinkHandlerFactory extends ListLinkHandlerFactory {
} }
@Override @Override
public String getId(String url) { public String getId(final String url) {
url = Utils.replaceHttpWithHttps(url); final String fixedUrl = Utils.replaceHttpWithHttps(url);
if (BandcampExtractorHelper.isRadioUrl(url) || url.equals(RADIO_API_URL)) { if (BandcampExtractorHelper.isRadioUrl(fixedUrl) || fixedUrl.equals(RADIO_API_URL)) {
return KIOSK_RADIO; return KIOSK_RADIO;
} else if (url.equals(FEATURED_API_URL)) { } else if (fixedUrl.equals(FEATURED_API_URL)) {
return KIOSK_FEATURED; return KIOSK_FEATURED;
} else { } else {
return null; return null;
@ -39,8 +41,10 @@ public class BandcampFeaturedLinkHandlerFactory extends ListLinkHandlerFactory {
} }
@Override @Override
public boolean onAcceptUrl(String url) { public boolean onAcceptUrl(final String url) {
url = Utils.replaceHttpWithHttps(url); final String fixedUrl = Utils.replaceHttpWithHttps(url);
return url.equals(FEATURED_API_URL) || (url.equals(RADIO_API_URL) || BandcampExtractorHelper.isRadioUrl(url)); return fixedUrl.equals(FEATURED_API_URL)
|| fixedUrl.equals(RADIO_API_URL)
|| BandcampExtractorHelper.isRadioUrl(fixedUrl);
} }
} }

View File

@ -18,8 +18,9 @@ public class BandcampPlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
} }
@Override @Override
public String getUrl(final String url, final List<String> contentFilter, final String sortFilter) public String getUrl(final String url,
throws ParsingException { final List<String> contentFilter,
final String sortFilter) throws ParsingException {
return url; return url;
} }
@ -30,7 +31,9 @@ public class BandcampPlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
public boolean onAcceptUrl(final String url) throws ParsingException { public boolean onAcceptUrl(final String url) throws ParsingException {
// Exclude URLs which do not lead to an album // Exclude URLs which do not lead to an album
if (!url.toLowerCase().matches("https?://.+\\..+/album/.+")) return false; if (!url.toLowerCase().matches("https?://.+\\..+/album/.+")) {
return false;
}
// Test whether domain is supported // Test whether domain is supported
return BandcampExtractorHelper.isSupportedDomain(url); return BandcampExtractorHelper.isSupportedDomain(url);

View File

@ -15,14 +15,11 @@ public class BandcampSearchQueryHandlerFactory extends SearchQueryHandlerFactory
@Override @Override
public String getUrl(final String query, final List<String> contentFilter, final String sortFilter) public String getUrl(final String query,
throws ParsingException { final List<String> contentFilter,
final String sortFilter) throws ParsingException {
try { try {
return BASE_URL + "/search?q=" + URLEncoder.encode(query, "UTF-8") + "&page=1";
return BASE_URL + "/search?q=" +
URLEncoder.encode(query, "UTF-8")
+ "&page=1";
} catch (final UnsupportedEncodingException e) { } catch (final UnsupportedEncodingException e) {
throw new ParsingException("query \"" + query + "\" could not be encoded", e); throw new ParsingException("query \"" + query + "\" could not be encoded", e);
} }

View File

@ -50,10 +50,14 @@ public class BandcampStreamLinkHandlerFactory extends LinkHandlerFactory {
public boolean onAcceptUrl(final String url) throws ParsingException { public boolean onAcceptUrl(final String url) throws ParsingException {
// Accept Bandcamp radio // Accept Bandcamp radio
if (BandcampExtractorHelper.isRadioUrl(url)) return true; if (BandcampExtractorHelper.isRadioUrl(url)) {
return true;
}
// Don't accept URLs that don't point to a track // Don't accept URLs that don't point to a track
if (!url.toLowerCase().matches("https?://.+\\..+/track/.+")) return false; if (!url.toLowerCase().matches("https?://.+\\..+/track/.+")) {
return false;
}
// Test whether domain is supported // Test whether domain is supported
return BandcampExtractorHelper.isSupportedDomain(url); return BandcampExtractorHelper.isSupportedDomain(url);

View File

@ -1,10 +1,13 @@
package org.schabi.newpipe.extractor.services.media_ccc; package org.schabi.newpipe.extractor.services.media_ccc;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
import static java.util.Arrays.asList;
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.comments.CommentsExtractor; import org.schabi.newpipe.extractor.comments.CommentsExtractor;
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.KioskList; import org.schabi.newpipe.extractor.kiosk.KioskList;
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;
@ -14,16 +17,24 @@ import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
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.media_ccc.extractors.*; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.*; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceKiosk;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCLiveStreamExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCLiveStreamKiosk;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCRecentKiosk;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCStreamExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferencesListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCLiveListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCRecentListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory;
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 org.schabi.newpipe.extractor.suggestion.SuggestionExtractor; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
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.VIDEO;
public class MediaCCCService extends StreamingService { public class MediaCCCService extends StreamingService {
public MediaCCCService(final int id) { public MediaCCCService(final int id) {
super(id, "media.ccc.de", asList(AUDIO, VIDEO)); super(id, "media.ccc.de", asList(AUDIO, VIDEO));
@ -79,42 +90,42 @@ public class MediaCCCService extends StreamingService {
@Override @Override
public KioskList getKioskList() throws ExtractionException { public KioskList getKioskList() throws ExtractionException {
KioskList list = new KioskList(this); final KioskList list = new KioskList(this);
// add kiosks here e.g.: // add kiosks here e.g.:
try { try {
list.addKioskEntry(new KioskList.KioskExtractorFactory() { list.addKioskEntry(
@Override (streamingService, url, kioskId) -> new MediaCCCConferenceKiosk(
public KioskExtractor createNewKiosk(final StreamingService streamingService, MediaCCCService.this,
final String url, final String kioskId) new MediaCCCConferencesListLinkHandlerFactory().fromUrl(url),
throws ExtractionException { kioskId
return new MediaCCCConferenceKiosk(MediaCCCService.this, ),
new MediaCCCConferencesListLinkHandlerFactory().fromUrl(url), kioskId); new MediaCCCConferencesListLinkHandlerFactory(),
} "conferences"
}, new MediaCCCConferencesListLinkHandlerFactory(), "conferences"); );
list.addKioskEntry(new KioskList.KioskExtractorFactory() { list.addKioskEntry(
@Override (streamingService, url, kioskId) -> new MediaCCCRecentKiosk(
public KioskExtractor createNewKiosk(final StreamingService streamingService, MediaCCCService.this,
final String url, final String kioskId) new MediaCCCRecentListLinkHandlerFactory().fromUrl(url),
throws ExtractionException { kioskId
return new MediaCCCRecentKiosk(MediaCCCService.this, ),
new MediaCCCRecentListLinkHandlerFactory().fromUrl(url), kioskId); new MediaCCCRecentListLinkHandlerFactory(),
} "recent"
}, new MediaCCCRecentListLinkHandlerFactory(), "recent"); );
list.addKioskEntry(new KioskList.KioskExtractorFactory() { list.addKioskEntry(
@Override (streamingService, url, kioskId) -> new MediaCCCLiveStreamKiosk(
public KioskExtractor createNewKiosk(final StreamingService streamingService, MediaCCCService.this,
final String url, final String kioskId) new MediaCCCLiveListLinkHandlerFactory().fromUrl(url),
throws ExtractionException { kioskId
return new MediaCCCLiveStreamKiosk(MediaCCCService.this, ),
new MediaCCCLiveListLinkHandlerFactory().fromUrl(url), kioskId); new MediaCCCLiveListLinkHandlerFactory(),
} "live"
}, new MediaCCCLiveListLinkHandlerFactory(), "live"); );
list.setDefaultKiosk("recent"); list.setDefaultKiosk("recent");
} catch (Exception e) { } catch (final Exception e) {
throw new ExtractionException(e); throw new ExtractionException(e);
} }

View File

@ -53,22 +53,22 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
} }
@Override @Override
public String getParentChannelName() throws ParsingException { public String getParentChannelName() {
return ""; return "";
} }
@Override @Override
public String getParentChannelUrl() throws ParsingException { public String getParentChannelUrl() {
return ""; return "";
} }
@Override @Override
public String getParentChannelAvatarUrl() throws ParsingException { public String getParentChannelAvatarUrl() {
return ""; return "";
} }
@Override @Override
public boolean isVerified() throws ParsingException { public boolean isVerified() {
return false; return false;
} }
@ -91,10 +91,11 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
@Override @Override
public void onFetchPage(@Nonnull final Downloader downloader) public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException { throws IOException, ExtractionException {
final String conferenceUrl = MediaCCCConferenceLinkHandlerFactory.CONFERENCE_API_ENDPOINT + getId(); final String conferenceUrl
= MediaCCCConferenceLinkHandlerFactory.CONFERENCE_API_ENDPOINT + getId();
try { try {
conferenceData = JsonParser.object().from(downloader.get(conferenceUrl).responseBody()); conferenceData = JsonParser.object().from(downloader.get(conferenceUrl).responseBody());
} catch (JsonParserException jpe) { } catch (final JsonParserException jpe) {
throw new ExtractionException("Could not parse json returnd by url: " + conferenceUrl); throw new ExtractionException("Could not parse json returnd by url: " + conferenceUrl);
} }
} }

View File

@ -32,8 +32,8 @@ public class MediaCCCConferenceKiosk extends KioskExtractor<ChannelInfoItem> {
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<ChannelInfoItem> getInitialPage() { public InfoItemsPage<ChannelInfoItem> getInitialPage() {
JsonArray conferences = doc.getArray("conferences"); final JsonArray conferences = doc.getArray("conferences");
ChannelInfoItemsCollector collector = new ChannelInfoItemsCollector(getServiceId()); final ChannelInfoItemsCollector collector = new ChannelInfoItemsCollector(getServiceId());
for (int i = 0; i < conferences.size(); i++) { for (int i = 0; i < conferences.size(); i++) {
collector.commit(new MediaCCCConferenceInfoItemExtractor(conferences.getObject(i))); collector.commit(new MediaCCCConferenceInfoItemExtractor(conferences.getObject(i)));
} }
@ -54,7 +54,7 @@ public class MediaCCCConferenceKiosk extends KioskExtractor<ChannelInfoItem> {
.responseBody(); .responseBody();
try { try {
doc = JsonParser.object().from(site); doc = JsonParser.object().from(site);
} catch (JsonParserException jpe) { } catch (final JsonParserException jpe) {
throw new ExtractionException("Could not parse json.", jpe); throw new ExtractionException("Could not parse json.", jpe);
} }
} }

View File

@ -2,50 +2,51 @@ package org.schabi.newpipe.extractor.services.media_ccc.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.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.MetaInfo;
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.LinkHandler; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import javax.annotation.Nonnull;
public class MediaCCCLiveStreamExtractor extends StreamExtractor { public class MediaCCCLiveStreamExtractor extends StreamExtractor {
private JsonArray doc = null;
private JsonObject conference = null; private JsonObject conference = null;
private String group = ""; private String group = "";
private JsonObject room = null; private JsonObject room = null;
public MediaCCCLiveStreamExtractor(StreamingService service, LinkHandler linkHandler) { public MediaCCCLiveStreamExtractor(final StreamingService service,
final LinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
doc = MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization()); throws IOException, ExtractionException {
final JsonArray doc =
MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization());
// find correct room // find correct room
for (int c = 0; c < doc.size(); c++) { for (int c = 0; c < doc.size(); c++) {
final JsonObject conference = doc.getObject(c); conference = doc.getObject(c);
final JsonArray groups = conference.getArray("groups"); final JsonArray groups = conference.getArray("groups");
for (int g = 0; g < groups.size(); g++) { for (int g = 0; g < groups.size(); g++) {
final String group = groups.getObject(g).getString("group"); group = groups.getObject(g).getString("group");
final JsonArray rooms = groups.getObject(g).getArray("rooms"); final JsonArray rooms = groups.getObject(g).getArray("rooms");
for (int r = 0; r < rooms.size(); r++) { for (int r = 0; r < rooms.size(); r++) {
final JsonObject room = rooms.getObject(r); room = rooms.getObject(r);
if (getId().equals(conference.getString("slug") + "/" + room.getString("slug"))) { if (getId().equals(
this.conference = conference; conference.getString("slug") + "/" + room.getString("slug"))) {
this.group = group;
this.room = room;
return; return;
} }
} }
@ -69,7 +70,8 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public Description getDescription() throws ParsingException { public Description getDescription() throws ParsingException {
return new Description(conference.getString("description") + " - " + group, Description.PLAIN_TEXT); return new Description(conference.getString("description")
+ " - " + group, Description.PLAIN_TEXT);
} }
@Override @Override
@ -93,12 +95,11 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
@Override @Override
public String getHlsUrl() { public String getHlsUrl() {
// TODO: There are multiple HLS streams. // TODO: There are multiple HLS streams.
// Make getHlsUrl() and getDashMpdUrl() return lists of VideoStreams, so the user can choose a resolution. // Make getHlsUrl() and getDashMpdUrl() return lists of VideoStreams,
// so the user can choose a resolution.
for (int s = 0; s < room.getArray("streams").size(); s++) { for (int s = 0; s < room.getArray("streams").size(); s++) {
final JsonObject stream = room.getArray("streams").getObject(s); final JsonObject stream = room.getArray("streams").getObject(s);
if (stream.getString("type").equals("video")) { if (stream.getString("type").equals("video")) {
final String resolution = stream.getArray("videoSize").getInt(0) + "x"
+ stream.getArray("videoSize").getInt(1);
if (stream.has("hls")) { if (stream.has("hls")) {
return stream.getObject("urls").getObject("hls").getString("url"); return stream.getObject("urls").getObject("hls").getString("url");
} }
@ -115,7 +116,8 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
if (stream.getString("type").equals("audio")) { if (stream.getString("type").equals("audio")) {
for (final String type : stream.getObject("urls").keySet()) { for (final String type : stream.getObject("urls").keySet()) {
final JsonObject url = stream.getObject("urls").getObject(type); final JsonObject url = stream.getObject("urls").getObject(type);
audioStreams.add(new AudioStream(url.getString("url"), MediaFormat.getFromSuffix(type), -1)); audioStreams.add(new AudioStream(url.getString("url"),
MediaFormat.getFromSuffix(type), -1));
} }
} }
} }

View File

@ -18,19 +18,22 @@ import java.io.IOException;
public class MediaCCCLiveStreamKiosk extends KioskExtractor<StreamInfoItem> { public class MediaCCCLiveStreamKiosk extends KioskExtractor<StreamInfoItem> {
private JsonArray doc; private JsonArray doc;
public MediaCCCLiveStreamKiosk(StreamingService streamingService, ListLinkHandler linkHandler, String kioskId) { public MediaCCCLiveStreamKiosk(final StreamingService streamingService,
final ListLinkHandler linkHandler,
final String kioskId) {
super(streamingService, linkHandler, kioskId); super(streamingService, linkHandler, kioskId);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
doc = MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization()); doc = MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization());
} }
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
for (int c = 0; c < doc.size(); c++) { for (int c = 0; c < doc.size(); c++) {
final JsonObject conference = doc.getObject(c); final JsonObject conference = doc.getObject(c);
final JsonArray groups = conference.getArray("groups"); final JsonArray groups = conference.getArray("groups");
@ -48,7 +51,8 @@ public class MediaCCCLiveStreamKiosk extends KioskExtractor<StreamInfoItem> {
} }
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(Page page) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(final Page page)
throws IOException, ExtractionException {
return InfoItemsPage.emptyPage(); return InfoItemsPage.emptyPage();
} }

View File

@ -14,7 +14,8 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
private final String group; private final String group;
private final JsonObject roomInfo; private final JsonObject roomInfo;
public MediaCCCLiveStreamKioskExtractor(final JsonObject conferenceInfo, final String group, public MediaCCCLiveStreamKioskExtractor(final JsonObject conferenceInfo,
final String group,
final JsonObject roomInfo) { final JsonObject roomInfo) {
this.conferenceInfo = conferenceInfo; this.conferenceInfo = conferenceInfo;
this.group = group; this.group = group;
@ -39,7 +40,7 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
@Override @Override
public StreamType getStreamType() throws ParsingException { public StreamType getStreamType() throws ParsingException {
boolean isVideo = false; boolean isVideo = false;
for (Object stream : roomInfo.getArray("streams")) { for (final Object stream : roomInfo.getArray("streams")) {
if ("video".equals(((JsonObject) stream).getString("type"))) { if ("video".equals(((JsonObject) stream).getString("type"))) {
isVideo = true; isVideo = true;
break; break;
@ -65,7 +66,8 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
@Override @Override
public String getUploaderName() throws ParsingException { public String getUploaderName() throws ParsingException {
return conferenceInfo.getString("conference") + " - " + group + " - " + roomInfo.getString("display"); return conferenceInfo.getString("conference") + " - " + group
+ " - " + roomInfo.getString("display");
} }
@Override @Override

View File

@ -15,15 +15,17 @@ import java.time.format.DateTimeParseException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public final class MediaCCCParsingHelper { public final class MediaCCCParsingHelper {
private static final Pattern LIVE_STREAM_ID_PATTERN = Pattern.compile("\\w+/\\w+"); // {conference_slug}/{room_slug} // {conference_slug}/{room_slug}
private static final Pattern LIVE_STREAM_ID_PATTERN = Pattern.compile("\\w+/\\w+");
private static JsonArray liveStreams = null; private static JsonArray liveStreams = null;
private MediaCCCParsingHelper() { } private MediaCCCParsingHelper() { }
public static OffsetDateTime parseDateFrom(final String textualUploadDate) throws ParsingException { public static OffsetDateTime parseDateFrom(final String textualUploadDate)
throws ParsingException {
try { try {
return OffsetDateTime.parse(textualUploadDate); return OffsetDateTime.parse(textualUploadDate);
} catch (DateTimeParseException e) { } catch (final DateTimeParseException e) {
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e); throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
} }
} }
@ -31,8 +33,8 @@ public final class MediaCCCParsingHelper {
/** /**
* Check whether an id is a live stream id * Check whether an id is a live stream id
* @param id the {@code id} to check * @param id the {@code id} to check
* @return returns {@code true} if the {@code id} is formatted like {@code {conference_slug}/{room_slug}}; * @return returns {@code true} if the {@code id} is formatted like
* {@code false} otherwise * {@code {conference_slug}/{room_slug}}; {@code false} otherwise
*/ */
public static boolean isLiveStreamId(final String id) { public static boolean isLiveStreamId(final String id) {
return LIVE_STREAM_ID_PATTERN.matcher(id).find(); return LIVE_STREAM_ID_PATTERN.matcher(id).find();
@ -40,24 +42,28 @@ public final class MediaCCCParsingHelper {
/** /**
* Get currently available live streams from * Get currently available live streams from
* <a href="https://streaming.media.ccc.de/streams/v2.json">https://streaming.media.ccc.de/streams/v2.json</a>. * <a href="https://streaming.media.ccc.de/streams/v2.json">
* https://streaming.media.ccc.de/streams/v2.json</a>.
* Use this method to cache requests, because they can get quite big. * Use this method to cache requests, because they can get quite big.
* TODO: implement better caching policy (max-age: 3 min) * TODO: implement better caching policy (max-age: 3 min)
* @param downloader The downloader to use for making the request * @param downloader The downloader to use for making the request
* @param localization The localization to be used. Will most likely be ignored. * @param localization The localization to be used. Will most likely be ignored.
* @return {@link JsonArray} containing current conferences and info about their rooms and streams. * @return {@link JsonArray} containing current conferences and info about their rooms and
* @throws ExtractionException if the data could not be fetched or the retrieved data could not be parsed to a {@link JsonArray} * streams.
* @throws ExtractionException if the data could not be fetched or the retrieved data could not
* be parsed to a {@link JsonArray}
*/ */
public static JsonArray getLiveStreams(final Downloader downloader, final Localization localization) public static JsonArray getLiveStreams(final Downloader downloader,
final Localization localization)
throws ExtractionException { throws ExtractionException {
if (liveStreams == null) { if (liveStreams == null) {
try { try {
final String site = downloader.get("https://streaming.media.ccc.de/streams/v2.json", final String site = downloader.get("https://streaming.media.ccc.de/streams/v2.json",
localization).responseBody(); localization).responseBody();
liveStreams = JsonParser.array().from(site); liveStreams = JsonParser.array().from(site);
} catch (IOException | ReCaptchaException e) { } catch (final IOException | ReCaptchaException e) {
throw new ExtractionException("Could not get live stream JSON.", e); throw new ExtractionException("Could not get live stream JSON.", e);
} catch (JsonParserException e) { } catch (final JsonParserException e) {
throw new ExtractionException("Could not parse JSON.", e); throw new ExtractionException("Could not parse JSON.", e);
} }
} }

View File

@ -22,17 +22,20 @@ public class MediaCCCRecentKiosk extends KioskExtractor<StreamInfoItem> {
private JsonObject doc; private JsonObject doc;
public MediaCCCRecentKiosk(StreamingService streamingService, ListLinkHandler linkHandler, String kioskId) { public MediaCCCRecentKiosk(final StreamingService streamingService,
final ListLinkHandler linkHandler,
final String kioskId) {
super(streamingService, linkHandler, kioskId); super(streamingService, linkHandler, kioskId);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull final Downloader downloader)
throws IOException, ExtractionException {
final String site = downloader.get("https://api.media.ccc.de/public/events/recent", final String site = downloader.get("https://api.media.ccc.de/public/events/recent",
getExtractorLocalization()).responseBody(); getExtractorLocalization()).responseBody();
try { try {
doc = JsonParser.object().from(site); doc = JsonParser.object().from(site);
} catch (JsonParserException jpe) { } catch (final JsonParserException jpe) {
throw new ExtractionException("Could not parse json.", jpe); throw new ExtractionException("Could not parse json.", jpe);
} }
} }
@ -48,7 +51,8 @@ public class MediaCCCRecentKiosk extends KioskExtractor<StreamInfoItem> {
streamInfoItem -> streamInfoItem.getUploadDate().offsetDateTime()); streamInfoItem -> streamInfoItem.getUploadDate().offsetDateTime());
comparator = comparator.reversed(); comparator = comparator.reversed();
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId(), comparator); final StreamInfoItemsCollector collector
= new StreamInfoItemsCollector(getServiceId(), comparator);
for (int i = 0; i < events.size(); i++) { for (int i = 0; i < events.size(); i++) {
collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i))); collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i)));
} }
@ -56,7 +60,8 @@ public class MediaCCCRecentKiosk extends KioskExtractor<StreamInfoItem> {
} }
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(Page page) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(final Page page)
throws IOException, ExtractionException {
return InfoItemsPage.emptyPage(); return InfoItemsPage.emptyPage();
} }

View File

@ -46,8 +46,8 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
@Override @Override
public long getDuration() throws ParsingException { public long getDuration() throws ParsingException {
// duration and length have the same value // duration and length have the same value, see
// see https://github.com/voc/voctoweb/blob/master/app/views/public/shared/_event.json.jbuilder // https://github.com/voc/voctoweb/blob/master/app/views/public/shared/_event.json.jbuilder
return event.getInt("duration"); return event.getInt("duration");
} }

View File

@ -1,5 +1,9 @@
package org.schabi.newpipe.extractor.services.media_ccc.extractors; package org.schabi.newpipe.extractor.services.media_ccc.extractors;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.ALL;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.CONFERENCES;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.EVENTS;
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;
@ -13,7 +17,6 @@ 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.MultiInfoItemsCollector; import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
@ -26,10 +29,6 @@ import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.ALL;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.CONFERENCES;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.EVENTS;
public class MediaCCCSearchExtractor extends SearchExtractor { public class MediaCCCSearchExtractor extends SearchExtractor {
private JsonObject doc; private JsonObject doc;
private MediaCCCConferenceKiosk conferenceKiosk; private MediaCCCConferenceKiosk conferenceKiosk;
@ -41,7 +40,7 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
conferenceKiosk = new MediaCCCConferenceKiosk(service, conferenceKiosk = new MediaCCCConferenceKiosk(service,
new MediaCCCConferencesListLinkHandlerFactory().fromId("conferences"), new MediaCCCConferencesListLinkHandlerFactory().fromId("conferences"),
"conferences"); "conferences");
} catch (Exception e) { } catch (final Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -79,7 +78,7 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
if (getLinkHandler().getContentFilters().contains(EVENTS) if (getLinkHandler().getContentFilters().contains(EVENTS)
|| getLinkHandler().getContentFilters().contains(ALL) || getLinkHandler().getContentFilters().contains(ALL)
|| getLinkHandler().getContentFilters().isEmpty()) { || getLinkHandler().getContentFilters().isEmpty()) {
JsonArray events = doc.getArray("events"); final JsonArray events = doc.getArray("events");
for (int i = 0; i < events.size(); i++) { for (int i = 0; i < events.size(); i++) {
// Ensure only uploaded talks are shown in the search results. // Ensure only uploaded talks are shown in the search results.
// If the release date is null, the talk has not been held or uploaded yet // If the release date is null, the talk has not been held or uploaded yet
@ -109,7 +108,7 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
site = downloader.get(url, getExtractorLocalization()).responseBody(); site = downloader.get(url, getExtractorLocalization()).responseBody();
try { try {
doc = JsonParser.object().from(site); doc = JsonParser.object().from(site);
} catch (JsonParserException jpe) { } catch (final JsonParserException jpe) {
throw new ExtractionException("Could not parse JSON.", jpe); throw new ExtractionException("Could not parse JSON.", jpe);
} }
} }
@ -143,7 +142,7 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
} }
@Override @Override
public boolean isVerified() throws ParsingException { public boolean isVerified() {
return false; return false;
} }

View File

@ -4,8 +4,8 @@ import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.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;
@ -15,17 +15,21 @@ import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.annotation.Nonnull;
public class MediaCCCStreamExtractor extends StreamExtractor { public class MediaCCCStreamExtractor extends StreamExtractor {
private JsonObject data; private JsonObject data;
private JsonObject conferenceData; private JsonObject conferenceData;
@ -158,7 +162,7 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
data = JsonParser.object().from(downloader.get(videoUrl).responseBody()); data = JsonParser.object().from(downloader.get(videoUrl).responseBody());
conferenceData = JsonParser.object() conferenceData = JsonParser.object()
.from(downloader.get(data.getString("conference_url")).responseBody()); .from(downloader.get(data.getString("conference_url")).responseBody());
} catch (JsonParserException jpe) { } catch (final JsonParserException jpe) {
throw new ExtractionException("Could not parse json returned by url: " + videoUrl, jpe); throw new ExtractionException("Could not parse json returned by url: " + videoUrl, jpe);
} }
} }

View File

@ -10,7 +10,7 @@ import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor { public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor {
private JsonObject event; private final JsonObject event;
public MediaCCCStreamInfoItemExtractor(final JsonObject event) { public MediaCCCStreamInfoItemExtractor(final JsonObject event) {
this.event = event; this.event = event;

View File

@ -7,9 +7,12 @@ import org.schabi.newpipe.extractor.utils.Parser;
import java.util.List; import java.util.List;
public class MediaCCCConferenceLinkHandlerFactory extends ListLinkHandlerFactory { public class MediaCCCConferenceLinkHandlerFactory extends ListLinkHandlerFactory {
public static final String CONFERENCE_API_ENDPOINT = "https://api.media.ccc.de/public/conferences/"; public static final String CONFERENCE_API_ENDPOINT
= "https://api.media.ccc.de/public/conferences/";
public static final String CONFERENCE_PATH = "https://media.ccc.de/c/"; public static final String CONFERENCE_PATH = "https://media.ccc.de/c/";
private static final String ID_PATTERN = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/conferences/)|(?:media\\.ccc\\.de/[bc]/))([^/?&#]*)"; private static final String ID_PATTERN
= "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/conferences/)"
+ "|(?:media\\.ccc\\.de/[bc]/))([^/?&#]*)";
@Override @Override
public String getUrl(final String id, public String getUrl(final String id,
@ -27,7 +30,7 @@ public class MediaCCCConferenceLinkHandlerFactory extends ListLinkHandlerFactory
public boolean onAcceptUrl(final String url) { public boolean onAcceptUrl(final String url) {
try { try {
return getId(url) != null; return getId(url) != null;
} catch (ParsingException e) { } catch (final ParsingException e) {
return false; return false;
} }
} }

View File

@ -7,20 +7,22 @@ import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class MediaCCCLiveListLinkHandlerFactory extends ListLinkHandlerFactory { public class MediaCCCLiveListLinkHandlerFactory extends ListLinkHandlerFactory {
private static final String streamPattern = "^(?:https?://)?media\\.ccc\\.de/live$"; private static final String STREAM_PATTERN = "^(?:https?://)?media\\.ccc\\.de/live$";
@Override @Override
public String getId(String url) throws ParsingException { public String getId(final String url) throws ParsingException {
return "live"; return "live";
} }
@Override @Override
public boolean onAcceptUrl(String url) throws ParsingException { public boolean onAcceptUrl(final String url) throws ParsingException {
return Pattern.matches(streamPattern, url); return Pattern.matches(STREAM_PATTERN, url);
} }
@Override @Override
public String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException { public String getUrl(final String id,
final List<String> contentFilter,
final String sortFilter) throws ParsingException {
// FIXME: wrong URL; should be https://streaming.media.ccc.de/{conference_slug}/{room_slug} // FIXME: wrong URL; should be https://streaming.media.ccc.de/{conference_slug}/{room_slug}
return "https://media.ccc.de/live"; return "https://media.ccc.de/live";
} }

View File

@ -7,7 +7,9 @@ import org.schabi.newpipe.extractor.utils.Parser;
public class MediaCCCLiveStreamLinkHandlerFactory extends LinkHandlerFactory { public class MediaCCCLiveStreamLinkHandlerFactory extends LinkHandlerFactory {
public static final String VIDEO_API_ENDPOINT = "https://api.media.ccc.de/public/events/"; public static final String VIDEO_API_ENDPOINT = "https://api.media.ccc.de/public/events/";
private static final String VIDEO_PATH = "https://streaming.media.ccc.de/v/"; private static final String VIDEO_PATH = "https://streaming.media.ccc.de/v/";
private static final String ID_PATTERN = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)|(?:media\\.ccc\\.de/v/))([^/?&#]*)"; private static final String ID_PATTERN
= "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)"
+ "|(?:media\\.ccc\\.de/v/))([^/?&#]*)";
@Override @Override
public String getId(final String url) throws ParsingException { public String getId(final String url) throws ParsingException {
@ -23,7 +25,7 @@ public class MediaCCCLiveStreamLinkHandlerFactory extends LinkHandlerFactory {
public boolean onAcceptUrl(final String url) { public boolean onAcceptUrl(final String url) {
try { try {
return getId(url) != null; return getId(url) != null;
} catch (ParsingException e) { } catch (final ParsingException e) {
return false; return false;
} }
} }

View File

@ -6,20 +6,22 @@ import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class MediaCCCRecentListLinkHandlerFactory extends ListLinkHandlerFactory { public class MediaCCCRecentListLinkHandlerFactory extends ListLinkHandlerFactory {
private static final String pattern = "^(https?://)?media\\.ccc\\.de/recent/?$"; private static final String PATTERN = "^(https?://)?media\\.ccc\\.de/recent/?$";
@Override @Override
public String getId(String url) { public String getId(final String url) {
return "recent"; return "recent";
} }
@Override @Override
public boolean onAcceptUrl(String url) { public boolean onAcceptUrl(final String url) {
return Pattern.matches(pattern, url); return Pattern.matches(PATTERN, url);
} }
@Override @Override
public String getUrl(String id, List<String> contentFilter, String sortFilter) { public String getUrl(final String id,
final List<String> contentFilter,
final String sortFilter) {
return "https://media.ccc.de/recent"; return "https://media.ccc.de/recent";
} }
} }

View File

@ -34,7 +34,7 @@ public class MediaCCCSearchQueryHandlerFactory extends SearchQueryHandlerFactory
try { try {
return "https://media.ccc.de/public/events/search?q=" return "https://media.ccc.de/public/events/search?q="
+ URLEncoder.encode(query, UTF_8); + URLEncoder.encode(query, UTF_8);
} catch (UnsupportedEncodingException e) { } catch (final UnsupportedEncodingException e) {
throw new ParsingException("Could not create search string with query: " + query, e); throw new ParsingException("Could not create search string with query: " + query, e);
} }
} }

View File

@ -8,19 +8,21 @@ import org.schabi.newpipe.extractor.utils.Parser;
public class MediaCCCStreamLinkHandlerFactory extends LinkHandlerFactory { public class MediaCCCStreamLinkHandlerFactory extends LinkHandlerFactory {
public static final String VIDEO_API_ENDPOINT = "https://api.media.ccc.de/public/events/"; public static final String VIDEO_API_ENDPOINT = "https://api.media.ccc.de/public/events/";
private static final String VIDEO_PATH = "https://media.ccc.de/v/"; private static final String VIDEO_PATH = "https://media.ccc.de/v/";
private static final String RECORDING_ID_PATTERN = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)|(?:media\\.ccc\\.de/v/))([^/?&#]*)"; private static final String RECORDING_ID_PATTERN
private static final String LIVE_STREAM_API_ENDPOINT = "https://streaming.media.ccc.de/streams/v2.json"; = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)"
+ "|(?:media\\.ccc\\.de/v/))([^/?&#]*)";
private static final String LIVE_STREAM_PATH = "https://streaming.media.ccc.de/"; private static final String LIVE_STREAM_PATH = "https://streaming.media.ccc.de/";
private static final String LIVE_STREAM_ID_PATTERN = "streaming\\.media\\.ccc\\.de\\/(\\w+\\/\\w+)"; private static final String LIVE_STREAM_ID_PATTERN
= "streaming\\.media\\.ccc\\.de\\/(\\w+\\/\\w+)";
@Override @Override
public String getId(final String url) throws ParsingException { public String getId(final String url) throws ParsingException {
String streamId = null; String streamId = null;
try { try {
streamId = Parser.matchGroup1(LIVE_STREAM_ID_PATTERN, url); streamId = Parser.matchGroup1(LIVE_STREAM_ID_PATTERN, url);
} catch (Parser.RegexException ignored) { } catch (final Parser.RegexException ignored) {
} }
if (streamId == null) { if (streamId == null) {
return Parser.matchGroup1(RECORDING_ID_PATTERN, url); return Parser.matchGroup1(RECORDING_ID_PATTERN, url);
} }
@ -39,7 +41,7 @@ public class MediaCCCStreamLinkHandlerFactory extends LinkHandlerFactory {
public boolean onAcceptUrl(final String url) { public boolean onAcceptUrl(final String url) {
try { try {
return getId(url) != null; return getId(url) != null;
} catch (ParsingException e) { } catch (final ParsingException e) {
return false; return false;
} }
} }

View File

@ -5,7 +5,6 @@ import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
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.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;
@ -18,14 +17,15 @@ public class PeertubeInstance {
private final String url; private final String url;
private String name; private String name;
public static final PeertubeInstance defaultInstance = new PeertubeInstance("https://framatube.org", "FramaTube"); public static final PeertubeInstance DEFAULT_INSTANCE
= new PeertubeInstance("https://framatube.org", "FramaTube");
public PeertubeInstance(String url) { public PeertubeInstance(final String url) {
this.url = url; this.url = url;
this.name = "PeerTube"; this.name = "PeerTube";
} }
public PeertubeInstance(String url, String name) { public PeertubeInstance(final String url, final String name) {
this.url = url; this.url = url;
this.name = name; this.name = name;
} }
@ -35,11 +35,9 @@ public class PeertubeInstance {
} }
public void fetchInstanceMetaData() throws Exception { public void fetchInstanceMetaData() throws Exception {
Downloader downloader = NewPipe.getDownloader(); final Response response;
Response response = null;
try { try {
response = downloader.get(url + "/api/v1/config"); response = NewPipe.getDownloader().get(url + "/api/v1/config");
} catch (ReCaptchaException | IOException e) { } catch (ReCaptchaException | IOException e) {
throw new Exception("unable to configure instance " + url, e); throw new Exception("unable to configure instance " + url, e);
} }
@ -49,7 +47,7 @@ public class PeertubeInstance {
} }
try { try {
JsonObject json = JsonParser.object().from(response.responseBody()); final JsonObject json = JsonParser.object().from(response.responseBody());
this.name = JsonUtils.getString(json, "instance.name"); this.name = JsonUtils.getString(json, "instance.name");
} catch (JsonParserException | ParsingException e) { } catch (JsonParserException | ParsingException e) {
throw new Exception("unable to parse instance config", e); throw new Exception("unable to parse instance config", e);

View File

@ -17,7 +17,7 @@ import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
public class PeertubeParsingHelper { public final class PeertubeParsingHelper {
public static final String START_KEY = "start"; public static final String START_KEY = "start";
public static final String COUNT_KEY = "count"; public static final String COUNT_KEY = "count";
public static final int ITEMS_PER_PAGE = 12; public static final int ITEMS_PER_PAGE = 12;
@ -33,10 +33,11 @@ public class PeertubeParsingHelper {
} }
} }
public static OffsetDateTime parseDateFrom(final String textualUploadDate) throws ParsingException { public static OffsetDateTime parseDateFrom(final String textualUploadDate)
throws ParsingException {
try { try {
return OffsetDateTime.ofInstant(Instant.parse(textualUploadDate), ZoneOffset.UTC); return OffsetDateTime.ofInstant(Instant.parse(textualUploadDate), ZoneOffset.UTC);
} catch (DateTimeParseException e) { } catch (final DateTimeParseException e) {
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e); throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
} }
} }
@ -45,25 +46,31 @@ public class PeertubeParsingHelper {
final String prevStart; final String prevStart;
try { try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl); prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (Parser.RegexException e) { } catch (final Parser.RegexException e) {
return null; return null;
} }
if (Utils.isBlank(prevStart)) return null; if (Utils.isBlank(prevStart)) {
return null;
}
final long nextStart; final long nextStart;
try { try {
nextStart = Long.parseLong(prevStart) + ITEMS_PER_PAGE; nextStart = Long.parseLong(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) { } catch (final NumberFormatException e) {
return null; return null;
} }
if (nextStart >= total) { if (nextStart >= total) {
return null; return null;
} else { } else {
return new Page(prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + nextStart)); return new Page(prevPageUrl.replace(
START_KEY + "=" + prevStart, START_KEY + "=" + nextStart));
} }
} }
public static void collectStreamsFrom(final InfoItemsCollector collector, final JsonObject json, final String baseUrl) throws ParsingException { public static void collectStreamsFrom(final InfoItemsCollector collector,
final JsonObject json,
final String baseUrl) throws ParsingException {
collectStreamsFrom(collector, json, baseUrl, false); collectStreamsFrom(collector, json, baseUrl, false);
} }
@ -74,13 +81,15 @@ public class PeertubeParsingHelper {
* @param json the file to retrieve data from * @param json the file to retrieve data from
* @param baseUrl the base Url of the instance * @param baseUrl the base Url of the instance
* @param sepia if we should use PeertubeSepiaStreamInfoItemExtractor * @param sepia if we should use PeertubeSepiaStreamInfoItemExtractor
* @throws ParsingException
*/ */
public static void collectStreamsFrom(final InfoItemsCollector collector, final JsonObject json, final String baseUrl, boolean sepia) throws ParsingException { public static void collectStreamsFrom(final InfoItemsCollector collector,
final JsonObject json,
final String baseUrl,
final boolean sepia) throws ParsingException {
final JsonArray contents; final JsonArray contents;
try { try {
contents = (JsonArray) JsonUtils.getValue(json, "data"); contents = (JsonArray) JsonUtils.getValue(json, "data");
} catch (Exception e) { } catch (final Exception e) {
throw new ParsingException("Unable to extract list info", e); throw new ParsingException("Unable to extract list info", e);
} }
@ -93,7 +102,7 @@ public class PeertubeParsingHelper {
item = item.getObject("video"); item = item.getObject("video");
} }
PeertubeStreamInfoItemExtractor extractor; final PeertubeStreamInfoItemExtractor extractor;
if (sepia) { if (sepia) {
extractor = new PeertubeSepiaStreamInfoItemExtractor(item, baseUrl); extractor = new PeertubeSepiaStreamInfoItemExtractor(item, baseUrl);
} else { } else {

View File

@ -1,35 +1,51 @@
package org.schabi.newpipe.extractor.services.peertube; package org.schabi.newpipe.extractor.services.peertube;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
import static java.util.Arrays.asList;
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.comments.CommentsExtractor; import org.schabi.newpipe.extractor.comments.CommentsExtractor;
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.KioskList; import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.extractor.linkhandler.*; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
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.peertube.extractors.*; import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeAccountExtractor;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.*; import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeChannelExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeCommentsExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubePlaylistExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeSearchExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeStreamExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeSuggestionExtractor;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeTrendingExtractor;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeCommentsLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubePlaylistLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeTrendingLinkHandlerFactory;
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 org.schabi.newpipe.extractor.suggestion.SuggestionExtractor; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.util.List; import java.util.List;
import static java.util.Arrays.asList;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
public class PeertubeService extends StreamingService { public class PeertubeService extends StreamingService {
private PeertubeInstance instance; private PeertubeInstance instance;
public PeertubeService(int id) { public PeertubeService(final int id) {
this(id, PeertubeInstance.defaultInstance); this(id, PeertubeInstance.DEFAULT_INSTANCE);
} }
public PeertubeService(int id, PeertubeInstance instance) { public PeertubeService(final int id, final PeertubeInstance instance) {
super(id, "PeerTube", asList(VIDEO, COMMENTS)); super(id, "PeerTube", asList(VIDEO, COMMENTS));
this.instance = instance; this.instance = instance;
} }
@ -60,13 +76,10 @@ public class PeertubeService extends StreamingService {
} }
@Override @Override
public SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler) { public SearchExtractor getSearchExtractor(final SearchQueryHandler queryHandler) {
final List<String> contentFilters = queryHandler.getContentFilters(); final List<String> contentFilters = queryHandler.getContentFilters();
boolean external = false; return new PeertubeSearchExtractor(this, queryHandler,
if (!contentFilters.isEmpty() && contentFilters.get(0).startsWith("sepia_")) { !contentFilters.isEmpty() && contentFilters.get(0).startsWith("sepia_"));
external = true;
}
return new PeertubeSearchExtractor(this, queryHandler, external);
} }
@Override @Override
@ -80,7 +93,7 @@ public class PeertubeService extends StreamingService {
} }
@Override @Override
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) public ChannelExtractor getChannelExtractor(final ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
if (linkHandler.getUrl().contains("/video-channels/")) { if (linkHandler.getUrl().contains("/video-channels/")) {
@ -91,19 +104,19 @@ public class PeertubeService extends StreamingService {
} }
@Override @Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) public PlaylistExtractor getPlaylistExtractor(final ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return new PeertubePlaylistExtractor(this, linkHandler); return new PeertubePlaylistExtractor(this, linkHandler);
} }
@Override @Override
public StreamExtractor getStreamExtractor(LinkHandler linkHandler) public StreamExtractor getStreamExtractor(final LinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return new PeertubeStreamExtractor(this, linkHandler); return new PeertubeStreamExtractor(this, linkHandler);
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) public CommentsExtractor getCommentsExtractor(final ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return new PeertubeCommentsExtractor(this, linkHandler); return new PeertubeCommentsExtractor(this, linkHandler);
} }
@ -117,34 +130,31 @@ public class PeertubeService extends StreamingService {
return this.instance; return this.instance;
} }
public void setInstance(PeertubeInstance instance) { public void setInstance(final PeertubeInstance instance) {
this.instance = instance; this.instance = instance;
} }
@Override @Override
public KioskList getKioskList() throws ExtractionException { public KioskList getKioskList() throws ExtractionException {
KioskList.KioskExtractorFactory kioskFactory = new KioskList.KioskExtractorFactory() { final KioskList.KioskExtractorFactory kioskFactory = (streamingService, url, id) ->
@Override new PeertubeTrendingExtractor(
public KioskExtractor createNewKiosk(StreamingService streamingService, PeertubeService.this,
String url, new PeertubeTrendingLinkHandlerFactory().fromId(id),
String id) id
throws ExtractionException { );
return new PeertubeTrendingExtractor(PeertubeService.this,
new PeertubeTrendingLinkHandlerFactory().fromId(id), id);
}
};
KioskList list = new KioskList(this); final KioskList list = new KioskList(this);
// add kiosks here e.g.: // add kiosks here e.g.:
final PeertubeTrendingLinkHandlerFactory h = new PeertubeTrendingLinkHandlerFactory(); final PeertubeTrendingLinkHandlerFactory h = new PeertubeTrendingLinkHandlerFactory();
try { try {
list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_TRENDING); list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_TRENDING);
list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_MOST_LIKED); list.addKioskEntry(kioskFactory, h,
PeertubeTrendingLinkHandlerFactory.KIOSK_MOST_LIKED);
list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_RECENT); list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_RECENT);
list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_LOCAL); list.addKioskEntry(kioskFactory, h, PeertubeTrendingLinkHandlerFactory.KIOSK_LOCAL);
list.setDefaultKiosk(PeertubeTrendingLinkHandlerFactory.KIOSK_TRENDING); list.setDefaultKiosk(PeertubeTrendingLinkHandlerFactory.KIOSK_TRENDING);
} catch (Exception e) { } catch (final Exception e) {
throw new ExtractionException(e); throw new ExtractionException(e);
} }

View File

@ -23,7 +23,10 @@ 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.peertube.PeertubeParsingHelper.*; import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.COUNT_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.START_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.collectStreamsFrom;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeAccountExtractor extends ChannelExtractor { public class PeertubeAccountExtractor extends ChannelExtractor {
@ -31,7 +34,8 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
private final String baseUrl; private final String baseUrl;
private static final String ACCOUNTS = "accounts/"; private static final String ACCOUNTS = "accounts/";
public PeertubeAccountExtractor(final StreamingService service, final ListLinkHandler linkHandler) throws ParsingException { public PeertubeAccountExtractor(final StreamingService service,
final ListLinkHandler linkHandler) throws ParsingException {
super(service, linkHandler); super(service, linkHandler);
this.baseUrl = getBaseUrl(); this.baseUrl = getBaseUrl();
} }
@ -41,7 +45,7 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
String value; String value;
try { try {
value = JsonUtils.getString(json, "avatar.path"); value = JsonUtils.getString(json, "avatar.path");
} catch (Exception e) { } catch (final Exception e) {
value = "/client/assets/images/default-avatar.png"; value = "/client/assets/images/default-avatar.png";
} }
return baseUrl + value; return baseUrl + value;
@ -80,7 +84,8 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
subscribersCount += videoChannelJsonObject.getInt("followersCount"); subscribersCount += videoChannelJsonObject.getInt("followersCount");
} }
} catch (final IOException | JsonParserException | ReCaptchaException ignored) { } catch (final IOException | JsonParserException | ReCaptchaException ignored) {
// something went wrong during video channels extraction, only return subscribers of ownerAccount // something went wrong during video channels extraction,
// only return subscribers of ownerAccount
} }
return subscribersCount; return subscribersCount;
} }
@ -89,7 +94,7 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
public String getDescription() { public String getDescription() {
try { try {
return JsonUtils.getString(json, "description"); return JsonUtils.getString(json, "description");
} catch (ParsingException e) { } catch (final ParsingException e) {
return "No description"; return "No description";
} }
} }
@ -117,8 +122,8 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
return getPage(new Page( return getPage(new Page(baseUrl + "/api/v1/" + getId() + "/videos?" + START_KEY + "=0&"
baseUrl + "/api/v1/" + getId() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE)); + COUNT_KEY + "=" + ITEMS_PER_PAGE));
} }
@Override @Override
@ -130,23 +135,24 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
final Response response = getDownloader().get(page.getUrl()); final Response response = getDownloader().get(page.getUrl());
JsonObject json = null; JsonObject pageJson = null;
if (response != null && !Utils.isBlank(response.responseBody())) { if (response != null && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); pageJson = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (final Exception e) {
throw new ParsingException("Could not parse json data for account info", e); throw new ParsingException("Could not parse json data for account info", e);
} }
} }
if (json != null) { if (pageJson != null) {
PeertubeParsingHelper.validate(json); PeertubeParsingHelper.validate(pageJson);
final long total = json.getLong("total"); final long total = pageJson.getLong("total");
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
collectStreamsFrom(collector, json, getBaseUrl()); collectStreamsFrom(collector, pageJson, getBaseUrl());
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPage(page.getUrl(), total)); return new InfoItemsPage<>(collector,
PeertubeParsingHelper.getNextPage(page.getUrl(), total));
} else { } else {
throw new ExtractionException("Unable to get PeerTube account info"); throw new ExtractionException("Unable to get PeerTube account info");
} }
@ -173,10 +179,12 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
private void setInitialData(final String responseBody) throws ExtractionException { private void setInitialData(final String responseBody) throws ExtractionException {
try { try {
json = JsonParser.object().from(responseBody); json = JsonParser.object().from(responseBody);
} catch (JsonParserException e) { } catch (final JsonParserException e) {
throw new ExtractionException("Unable to extract PeerTube account data", e); throw new ExtractionException("Unable to extract PeerTube account data", e);
} }
if (json == null) throw new ExtractionException("Unable to extract PeerTube account data"); if (json == null) {
throw new ExtractionException("Unable to extract PeerTube account data");
}
} }
@Nonnull @Nonnull

View File

@ -21,15 +21,18 @@ 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.peertube.PeertubeParsingHelper.*; import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.COUNT_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.START_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.collectStreamsFrom;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeChannelExtractor extends ChannelExtractor { public class PeertubeChannelExtractor extends ChannelExtractor {
private JsonObject json; private JsonObject json;
private final String baseUrl; private final String baseUrl;
public PeertubeChannelExtractor(final StreamingService service, final ListLinkHandler linkHandler) throws ParsingException { public PeertubeChannelExtractor(final StreamingService service,
final ListLinkHandler linkHandler) throws ParsingException {
super(service, linkHandler); super(service, linkHandler);
this.baseUrl = getBaseUrl(); this.baseUrl = getBaseUrl();
} }
@ -39,7 +42,7 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
String value; String value;
try { try {
value = JsonUtils.getString(json, "avatar.path"); value = JsonUtils.getString(json, "avatar.path");
} catch (Exception e) { } catch (final Exception e) {
value = "/client/assets/images/default-avatar.png"; value = "/client/assets/images/default-avatar.png";
} }
return baseUrl + value; return baseUrl + value;
@ -64,7 +67,7 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
public String getDescription() { public String getDescription() {
try { try {
return JsonUtils.getString(json, "description"); return JsonUtils.getString(json, "description");
} catch (ParsingException e) { } catch (final ParsingException e) {
return "No description"; return "No description";
} }
} }
@ -84,7 +87,7 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
String value; String value;
try { try {
value = JsonUtils.getString(json, "ownerAccount.avatar.path"); value = JsonUtils.getString(json, "ownerAccount.avatar.path");
} catch (Exception e) { } catch (final Exception e) {
value = "/client/assets/images/default-avatar.png"; value = "/client/assets/images/default-avatar.png";
} }
return baseUrl + value; return baseUrl + value;
@ -98,45 +101,48 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
@Nonnull @Nonnull
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
return getPage(new Page( return getPage(new Page(baseUrl + "/api/v1/" + getId() + "/videos?" + START_KEY + "=0&"
baseUrl + "/api/v1/" + getId() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE)); + COUNT_KEY + "=" + ITEMS_PER_PAGE));
} }
@Override @Override
public InfoItemsPage<StreamInfoItem> getPage(final Page page) throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getPage(final Page page)
throws IOException, ExtractionException {
if (page == null || isNullOrEmpty(page.getUrl())) { if (page == null || isNullOrEmpty(page.getUrl())) {
throw new IllegalArgumentException("Page doesn't contain an URL"); throw new IllegalArgumentException("Page doesn't contain an URL");
} }
final Response response = getDownloader().get(page.getUrl()); final Response response = getDownloader().get(page.getUrl());
JsonObject json = null; JsonObject pageJson = null;
if (response != null && !Utils.isBlank(response.responseBody())) { if (response != null && !Utils.isBlank(response.responseBody())) {
try { try {
json = JsonParser.object().from(response.responseBody()); pageJson = JsonParser.object().from(response.responseBody());
} catch (Exception e) { } catch (final Exception e) {
throw new ParsingException("Could not parse json data for channel info", e); throw new ParsingException("Could not parse json data for channel info", e);
} }
} }
if (json != null) { if (pageJson != null) {
PeertubeParsingHelper.validate(json); PeertubeParsingHelper.validate(pageJson);
final long total = json.getLong("total"); final long total = pageJson.getLong("total");
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
collectStreamsFrom(collector, json, getBaseUrl()); collectStreamsFrom(collector, pageJson, getBaseUrl());
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPage(page.getUrl(), total)); return new InfoItemsPage<>(collector,
PeertubeParsingHelper.getNextPage(page.getUrl(), total));
} else { } else {
throw new ExtractionException("Unable to get PeerTube channel info"); throw new ExtractionException("Unable to get PeerTube channel info");
} }
} }
@Override @Override
public void onFetchPage(final Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(final Downloader downloader)
throws IOException, ExtractionException {
final Response response = downloader.get( final Response response = downloader.get(
baseUrl + PeertubeChannelLinkHandlerFactory.API_ENDPOINT + getId()); baseUrl + PeertubeChannelLinkHandlerFactory.API_ENDPOINT + getId());
if (response != null ) { if (response != null) {
setInitialData(response.responseBody()); setInitialData(response.responseBody());
} else { } else {
throw new ExtractionException("Unable to extract PeerTube channel data"); throw new ExtractionException("Unable to extract PeerTube channel data");
@ -146,10 +152,12 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
private void setInitialData(final String responseBody) throws ExtractionException { private void setInitialData(final String responseBody) throws ExtractionException {
try { try {
json = JsonParser.object().from(responseBody); json = JsonParser.object().from(responseBody);
} catch (JsonParserException e) { } catch (final JsonParserException e) {
throw new ExtractionException("Unable to extract PeerTube channel data", e); throw new ExtractionException("Unable to extract PeerTube channel data", e);
} }
if (json == null) throw new ExtractionException("Unable to extract PeerTube channel data"); if (json == null) {
throw new ExtractionException("Unable to extract PeerTube channel data");
}
} }
@Nonnull @Nonnull

View File

@ -18,36 +18,41 @@ import org.schabi.newpipe.extractor.utils.Utils;
import java.io.IOException; import java.io.IOException;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*; import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.COUNT_KEY;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.ITEMS_PER_PAGE;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.START_KEY;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
public class PeertubeCommentsExtractor extends CommentsExtractor { public class PeertubeCommentsExtractor extends CommentsExtractor {
public PeertubeCommentsExtractor(final StreamingService service, final ListLinkHandler uiHandler) { public PeertubeCommentsExtractor(final StreamingService service,
final ListLinkHandler uiHandler) {
super(service, uiHandler); super(service, uiHandler);
} }
@Override @Override
public InfoItemsPage<CommentsInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getInitialPage()
final String pageUrl = getUrl() + "?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE; throws IOException, ExtractionException {
return getPage(new Page(pageUrl)); return getPage(new Page(getUrl() + "?" + START_KEY + "=0&"
+ COUNT_KEY + "=" + ITEMS_PER_PAGE));
} }
private void collectCommentsFrom(final CommentsInfoItemsCollector collector, final JsonObject json) throws ParsingException { private void collectCommentsFrom(final CommentsInfoItemsCollector collector,
final JsonObject json) throws ParsingException {
final JsonArray contents = json.getArray("data"); final JsonArray contents = json.getArray("data");
for (final Object c : contents) { for (final Object c : contents) {
if (c instanceof JsonObject) { if (c instanceof JsonObject) {
final JsonObject item = (JsonObject) c; final JsonObject item = (JsonObject) c;
if (!item.getBoolean("isDeleted")) { if (!item.getBoolean("isDeleted")) {
final PeertubeCommentsInfoItemExtractor extractor = new PeertubeCommentsInfoItemExtractor(item, this); collector.commit(new PeertubeCommentsInfoItemExtractor(item, this));
collector.commit(extractor);
} }
} }
} }
} }
@Override @Override
public InfoItemsPage<CommentsInfoItem> getPage(final Page page) throws IOException, ExtractionException { public InfoItemsPage<CommentsInfoItem> getPage(final Page page)
throws IOException, ExtractionException {
if (page == null || isNullOrEmpty(page.getUrl())) { if (page == null || isNullOrEmpty(page.getUrl())) {
throw new IllegalArgumentException("Page doesn't contain an URL"); throw new IllegalArgumentException("Page doesn't contain an URL");
} }
@ -58,7 +63,7 @@ public class PeertubeCommentsExtractor extends CommentsExtractor {
if (response != null && !Utils.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 (final Exception e) {
throw new ParsingException("Could not parse json data for comments info", e); throw new ParsingException("Could not parse json data for comments info", e);
} }
} }
@ -67,15 +72,18 @@ public class PeertubeCommentsExtractor extends CommentsExtractor {
PeertubeParsingHelper.validate(json); PeertubeParsingHelper.validate(json);
final long total = json.getLong("total"); final long total = json.getLong("total");
final CommentsInfoItemsCollector collector = new CommentsInfoItemsCollector(getServiceId()); final CommentsInfoItemsCollector collector
= new CommentsInfoItemsCollector(getServiceId());
collectCommentsFrom(collector, json); collectCommentsFrom(collector, json);
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPage(page.getUrl(), total)); return new InfoItemsPage<>(collector,
PeertubeParsingHelper.getNextPage(page.getUrl(), total));
} else { } else {
throw new ExtractionException("Unable to get PeerTube kiosk info"); throw new ExtractionException("Unable to get PeerTube kiosk info");
} }
} }
@Override @Override
public void onFetchPage(Downloader downloader) { } public void onFetchPage(final Downloader downloader) {
}
} }

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