Merge branch 'dev' into yt-webm-opus

This commit is contained in:
kapodamy 2019-11-24 11:56:16 -03:00 committed by GitHub
commit c1d39f692d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
280 changed files with 5282 additions and 3452 deletions

View File

@ -13,6 +13,17 @@ If you're using Gradle, you could add NewPipe Extractor as a dependency with the
1. Add `maven { url 'https://jitpack.io' }` to the `repositories` in your `build.gradle`. 1. Add `maven { url 'https://jitpack.io' }` to the `repositories` in your `build.gradle`.
2. Add `compile 'com.github.TeamNewPipe:NewPipeExtractor:v0.11.0'`the `dependencies` in your `build.gradle`. Replace `v0.11.0` with the latest release. 2. Add `compile 'com.github.TeamNewPipe:NewPipeExtractor:v0.11.0'`the `dependencies` in your `build.gradle`. Replace `v0.11.0` with the latest release.
### Testing changes
To test changes quickly you can build the library locally. Using the local Maven repository is a good approach, here's a gist of how to use it:
1. Add `mavenLocal()` in your project `repositories` list (usually as the first entry to give priority above the others).
2. It's _recommended_ that you change the `version` of this library (e.g. `LOCAL_SNAPSHOT`).
3. Run gradle's `ìnstall` task to deploy this library to your local repository (using the wrapper, present in the root of this project: `./gradlew install`)
4. Change the dependency version used in your project to match the one you chose in step 2 (`implementation 'com.github.TeamNewPipe:NewPipeExtractor:LOCAL_SNAPSHOT'`)
> Tip for Android Studio users: After you make changes and run the `install` task, use the menu option `File → "Sync with File System"` to refresh the library in your project.
## Supported sites ## Supported sites
The following sites are currently supported: The following sites are currently supported:

View File

@ -1,15 +1,23 @@
allprojects { allprojects {
apply plugin: 'java-library' apply plugin: 'java-library'
apply plugin: 'maven'
sourceCompatibility = 1.7 sourceCompatibility = 1.7
targetCompatibility = 1.7 targetCompatibility = 1.7
version 'v0.13.0' version 'v0.13.0'
group 'com.github.TeamNewPipe'
repositories { repositories {
jcenter() jcenter()
} }
} }
dependencies {
implementation project(':extractor')
implementation project(':timeago-parser')
}
subprojects { subprojects {
task sourcesJar(type: Jar, dependsOn: classes) { task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources' classifier = 'sources'

View File

@ -1,44 +0,0 @@
package org.schabi.newpipe.extractor;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class DownloadRequest {
private final String requestBody;
private final Map<String, List<String>> requestHeaders;
public static final DownloadRequest emptyRequest = new DownloadRequest(null, null);
public DownloadRequest(String requestBody, Map<String, List<String>> headers) {
super();
this.requestBody = requestBody;
if(null != headers) {
this.requestHeaders = headers;
}else {
this.requestHeaders = Collections.emptyMap();
}
}
public String getRequestBody() {
return requestBody;
}
public Map<String, List<String>> getRequestHeaders() {
return requestHeaders;
}
public void setRequestCookies(List<String> cookies){
requestHeaders.put("Cookie", cookies);
}
public List<String> getRequestCookies(){
if(null == requestHeaders) return Collections.emptyList();
List<String> cookies = requestHeaders.get("Cookie");
if(null == cookies)
return Collections.emptyList();
else
return cookies;
}
}

View File

@ -1,37 +0,0 @@
package org.schabi.newpipe.extractor;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
public class DownloadResponse {
private final String responseBody;
private final Map<String, List<String>> responseHeaders;
public DownloadResponse(String responseBody, Map<String, List<String>> headers) {
super();
this.responseBody = responseBody;
this.responseHeaders = headers;
}
public String getResponseBody() {
return responseBody;
}
public Map<String, List<String>> getResponseHeaders() {
return responseHeaders;
}
@Nonnull
public List<String> getResponseCookies(){
if(null == responseHeaders) return Collections.emptyList();
List<String> cookies = responseHeaders.get("Set-Cookie");
if(null == cookies)
return Collections.emptyList();
else
return cookies;
}
}

View File

@ -1,70 +0,0 @@
package org.schabi.newpipe.extractor;
import java.io.IOException;
import java.util.Map;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.utils.Localization;
/*
* Created by Christian Schabesberger on 28.01.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* Downloader.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public interface Downloader {
/**
* Download the text file at the supplied URL as in download(String), but set
* the HTTP header field "Accept-Language" to the supplied string.
*
* @param siteUrl the URL of the text file to return the contents of
* @param localization the language and country (usually a 2-character code for each)
* @return the contents of the specified text file
* @throws IOException
*/
String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException;
/**
* Download the text file at the supplied URL as in download(String), but set
* the HTTP header field "Accept-Language" to the supplied string.
*
* @param siteUrl the URL of the text file to return the contents of
* @param customProperties set request header properties
* @return the contents of the specified text file
* @throws IOException
*/
String download(String siteUrl, Map<String, String> customProperties) throws IOException, ReCaptchaException;
/**
* Download (via HTTP) the text file located at the supplied URL, and return its
* contents. Primarily intended for downloading web pages.
*
* @param siteUrl the URL of the text file to download
* @return the contents of the specified text file
* @throws IOException
*/
String download(String siteUrl) throws IOException, ReCaptchaException;
DownloadResponse get(String siteUrl, DownloadRequest request)
throws IOException, ReCaptchaException;
DownloadResponse get(String siteUrl) throws IOException, ReCaptchaException;
DownloadResponse post(String siteUrl, DownloadRequest request)
throws IOException, ReCaptchaException;
}

View File

@ -1,9 +1,12 @@
package org.schabi.newpipe.extractor; package org.schabi.newpipe.extractor;
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.utils.Localization; import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -16,21 +19,20 @@ public abstract class Extractor{
* 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;
private final Localization localization;
@Nullable @Nullable private Localization forcedLocalization = null;
@Nullable private ContentCountry forcedContentCountry = null;
private boolean pageFetched = false; private boolean pageFetched = false;
private final Downloader downloader; private final Downloader downloader;
public Extractor(final StreamingService service, final LinkHandler linkHandler, final Localization localization) { public Extractor(final StreamingService service, final LinkHandler linkHandler) {
if(service == null) throw new NullPointerException("service is null"); if(service == null) throw new NullPointerException("service is null");
if(linkHandler == null) throw new NullPointerException("LinkHandler is null"); if(linkHandler == null) throw new NullPointerException("LinkHandler is null");
this.service = service; this.service = service;
this.linkHandler = linkHandler; this.linkHandler = linkHandler;
this.downloader = NewPipe.getDownloader(); this.downloader = NewPipe.getDownloader();
this.localization = localization;
if(downloader == null) throw new NullPointerException("downloader is null"); if(downloader == null) throw new NullPointerException("downloader is null");
} }
@ -105,8 +107,30 @@ public abstract class Extractor{
return downloader; return downloader;
} }
/*//////////////////////////////////////////////////////////////////////////
// Localization
//////////////////////////////////////////////////////////////////////////*/
public void forceLocalization(Localization localization) {
this.forcedLocalization = localization;
}
public void forceContentCountry(ContentCountry contentCountry) {
this.forcedContentCountry = contentCountry;
}
@Nonnull @Nonnull
public Localization getLocalization() { public Localization getExtractorLocalization() {
return localization; return forcedLocalization == null ? getService().getLocalization() : forcedLocalization;
}
@Nonnull
public ContentCountry getExtractorContentCountry() {
return forcedContentCountry == null ? getService().getContentCountry() : forcedContentCountry;
}
@Nonnull
public TimeAgoParser getTimeAgoParser() {
return getService().getTimeAgoParser(getExtractorLocalization());
} }
} }

View File

@ -2,7 +2,6 @@ package org.schabi.newpipe.extractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -14,8 +13,8 @@ import java.util.List;
*/ */
public abstract class ListExtractor<R extends InfoItem> extends Extractor { public abstract class ListExtractor<R extends InfoItem> extends Extractor {
public ListExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public ListExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
/** /**

View File

@ -20,24 +20,42 @@ 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 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.utils.Localization; import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List; import java.util.List;
/** /**
* Provides access to streaming services supported by NewPipe. * Provides access to streaming services supported by NewPipe.
*/ */
public class NewPipe { public class NewPipe {
private static Downloader downloader = null; private static Downloader downloader;
private static Localization localization = null; private static Localization preferredLocalization;
private static ContentCountry preferredContentCountry;
private NewPipe() { private NewPipe() {
} }
public static void init(Downloader d) {
downloader = d;
preferredLocalization = Localization.DEFAULT;
preferredContentCountry = ContentCountry.DEFAULT;
}
public static void init(Downloader d, Localization l) { public static void init(Downloader d, Localization l) {
downloader = d; downloader = d;
localization = l; preferredLocalization = l;
preferredContentCountry = l.getCountryCode().isEmpty() ? ContentCountry.DEFAULT : new ContentCountry(l.getCountryCode());
}
public static void init(Downloader d, Localization l, ContentCountry c) {
downloader = d;
preferredLocalization = l;
preferredContentCountry = c;
} }
public static Downloader getDownloader() { public static Downloader getDownloader() {
@ -99,11 +117,41 @@ public class NewPipe {
} }
} }
public static void setLocalization(Localization localization) { /*//////////////////////////////////////////////////////////////////////////
NewPipe.localization = localization; // Localization
//////////////////////////////////////////////////////////////////////////*/
public static void setupLocalization(Localization preferredLocalization) {
setupLocalization(preferredLocalization, null);
} }
public static void setupLocalization(Localization preferredLocalization, @Nullable ContentCountry preferredContentCountry) {
NewPipe.preferredLocalization = preferredLocalization;
if (preferredContentCountry != null) {
NewPipe.preferredContentCountry = preferredContentCountry;
} else {
NewPipe.preferredContentCountry = preferredLocalization.getCountryCode().isEmpty()
? ContentCountry.DEFAULT
: new ContentCountry(preferredLocalization.getCountryCode());
}
}
@Nonnull
public static Localization getPreferredLocalization() { public static Localization getPreferredLocalization() {
return localization; return preferredLocalization == null ? Localization.DEFAULT : preferredLocalization;
}
public static void setPreferredLocalization(Localization preferredLocalization) {
NewPipe.preferredLocalization = preferredLocalization;
}
@Nonnull
public static ContentCountry getPreferredContentCountry() {
return preferredContentCountry == null ? ContentCountry.DEFAULT : preferredContentCountry;
}
public static void setPreferredContentCountry(ContentCountry preferredContentCountry) {
NewPipe.preferredContentCountry = preferredContentCountry;
} }
} }

View File

@ -1,24 +1,23 @@
package org.schabi.newpipe.extractor; package org.schabi.newpipe.extractor;
import java.util.Collections;
import java.util.List;
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.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
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.*;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager;
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.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.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.util.Collections;
import java.util.List;
/* /*
* Copyright (C) Christian Schabesberger 2018 <chris.schabesberger@mailbox.org> * Copyright (C) Christian Schabesberger 2018 <chris.schabesberger@mailbox.org>
@ -112,9 +111,9 @@ public abstract class StreamingService {
return serviceId + ":" + serviceInfo.getName(); return serviceId + ":" + serviceInfo.getName();
} }
//////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Url Id handler // Url Id handler
//////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////*/
/** /**
* Must return a new instance of an implementation of LinkHandlerFactory for streams. * Must return a new instance of an implementation of LinkHandlerFactory for streams.
@ -143,25 +142,22 @@ public abstract class StreamingService {
public abstract SearchQueryHandlerFactory getSearchQHFactory(); public abstract SearchQueryHandlerFactory getSearchQHFactory();
public abstract ListLinkHandlerFactory getCommentsLHFactory(); public abstract ListLinkHandlerFactory getCommentsLHFactory();
/*//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// // Extractors
// Extractor //////////////////////////////////////////////////////////////////////////*/
////////////////////////////////////////////
/** /**
* Must create a new instance of a SearchExtractor implementation. * Must create a new instance of a SearchExtractor implementation.
* @param queryHandler specifies the keyword lock for, and the filters which should be applied. * @param queryHandler specifies the keyword lock for, and the filters which should be applied.
* @param localization specifies the language/country for the extractor.
* @return a new SearchExtractor instance * @return a new SearchExtractor instance
*/ */
public abstract SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler, Localization localization); public abstract SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler);
/** /**
* Must create a new instance of a SuggestionExtractor implementation. * Must create a new instance of a SuggestionExtractor implementation.
* @param localization specifies the language/country for the extractor.
* @return a new SuggestionExtractor instance * @return a new SuggestionExtractor instance
*/ */
public abstract SuggestionExtractor getSuggestionExtractor(Localization localization); public abstract SuggestionExtractor getSuggestionExtractor();
/** /**
* Outdated or obsolete. null can be returned. * Outdated or obsolete. null can be returned.
@ -179,113 +175,72 @@ 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.
* @param localization specifies the language used for the request.
* @return a new ChannelExtractor * @return a new ChannelExtractor
* @throws ExtractionException * @throws ExtractionException
*/ */
public abstract ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler, public abstract ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) throws ExtractionException;
Localization localization) 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.
* @param localization specifies the language used for the request.
* @return a new PlaylistExtractor * @return a new PlaylistExtractor
* @throws ExtractionException * @throws ExtractionException
*/ */
public abstract PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler, public abstract PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) throws ExtractionException;
Localization localization) 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.
* @param localization specifies the language used for the request.
* @return a new StreamExtractor * @return a new StreamExtractor
* @throws ExtractionException * @throws ExtractionException
*/ */
public abstract StreamExtractor getStreamExtractor(LinkHandler linkHandler, public abstract StreamExtractor getStreamExtractor(LinkHandler linkHandler) throws ExtractionException;
Localization localization) throws ExtractionException;
public abstract CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler,
Localization localization) throws ExtractionException;
////////////////////////////////////////////
// Extractor with default localization
////////////////////////////////////////////
public SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler) { public abstract CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) throws ExtractionException;
return getSearchExtractor(queryHandler, NewPipe.getPreferredLocalization());
}
public SuggestionExtractor getSuggestionExtractor() { /*//////////////////////////////////////////////////////////////////////////
return getSuggestionExtractor(NewPipe.getPreferredLocalization()); // Extractors without link handler
} //////////////////////////////////////////////////////////////////////////*/
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) throws ExtractionException {
return getChannelExtractor(linkHandler, NewPipe.getPreferredLocalization());
}
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) throws ExtractionException {
return getPlaylistExtractor(linkHandler, NewPipe.getPreferredLocalization());
}
public StreamExtractor getStreamExtractor(LinkHandler linkHandler) throws ExtractionException {
return getStreamExtractor(linkHandler, NewPipe.getPreferredLocalization());
}
public CommentsExtractor getCommentsExtractor(ListLinkHandler urlIdHandler) throws ExtractionException {
return getCommentsExtractor(urlIdHandler, NewPipe.getPreferredLocalization());
}
////////////////////////////////////////////
// Extractor without link handler
////////////////////////////////////////////
public SearchExtractor getSearchExtractor(String query, public SearchExtractor getSearchExtractor(String query,
List<String> contentFilter, List<String> contentFilter,
String sortFilter, String sortFilter) throws ExtractionException {
Localization localization) throws ExtractionException {
return getSearchExtractor(getSearchQHFactory() return getSearchExtractor(getSearchQHFactory()
.fromQuery(query, .fromQuery(query, contentFilter, sortFilter));
contentFilter,
sortFilter),
localization);
} }
public ChannelExtractor getChannelExtractor(String id, public ChannelExtractor getChannelExtractor(String id,
List<String> contentFilter, List<String> contentFilter,
String sortFilter, String sortFilter) throws ExtractionException {
Localization localization) throws ExtractionException { return getChannelExtractor(getChannelLHFactory()
return getChannelExtractor(getChannelLHFactory().fromQuery(id, contentFilter, sortFilter), localization); .fromQuery(id, contentFilter, sortFilter));
} }
public PlaylistExtractor getPlaylistExtractor(String id, public PlaylistExtractor getPlaylistExtractor(String id,
List<String> contentFilter, List<String> contentFilter,
String sortFilter, String sortFilter) throws ExtractionException {
Localization localization) throws ExtractionException {
return getPlaylistExtractor(getPlaylistLHFactory() return getPlaylistExtractor(getPlaylistLHFactory()
.fromQuery(id, .fromQuery(id, contentFilter, sortFilter));
contentFilter,
sortFilter),
localization);
} }
//////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Short extractor without localization // Short extractors overloads
//////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////*/
public SearchExtractor getSearchExtractor(String query) throws ExtractionException { public SearchExtractor getSearchExtractor(String query) throws ExtractionException {
return getSearchExtractor(getSearchQHFactory().fromQuery(query), NewPipe.getPreferredLocalization()); return getSearchExtractor(getSearchQHFactory().fromQuery(query));
} }
public ChannelExtractor getChannelExtractor(String url) throws ExtractionException { public ChannelExtractor getChannelExtractor(String url) throws ExtractionException {
return getChannelExtractor(getChannelLHFactory().fromUrl(url), NewPipe.getPreferredLocalization()); return getChannelExtractor(getChannelLHFactory().fromUrl(url));
} }
public PlaylistExtractor getPlaylistExtractor(String url) throws ExtractionException { public PlaylistExtractor getPlaylistExtractor(String url) throws ExtractionException {
return getPlaylistExtractor(getPlaylistLHFactory().fromUrl(url), NewPipe.getPreferredLocalization()); return getPlaylistExtractor(getPlaylistLHFactory().fromUrl(url));
} }
public StreamExtractor getStreamExtractor(String url) throws ExtractionException { public StreamExtractor getStreamExtractor(String url) throws ExtractionException {
return getStreamExtractor(getStreamLHFactory().fromUrl(url), NewPipe.getPreferredLocalization()); return getStreamExtractor(getStreamLHFactory().fromUrl(url));
} }
public CommentsExtractor getCommentsExtractor(String url) throws ExtractionException { public CommentsExtractor getCommentsExtractor(String url) throws ExtractionException {
@ -293,9 +248,12 @@ public abstract class StreamingService {
if(null == llhf) { if(null == llhf) {
return null; return null;
} }
return getCommentsExtractor(llhf.fromUrl(url), NewPipe.getPreferredLocalization()); return getCommentsExtractor(llhf.fromUrl(url));
} }
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
/** /**
* Figures out where the link is pointing to (a channel, a video, a playlist, etc.) * Figures out where the link is pointing to (a channel, a video, a playlist, etc.)
@ -318,4 +276,95 @@ public abstract class StreamingService {
return LinkType.NONE; return LinkType.NONE;
} }
} }
/*//////////////////////////////////////////////////////////////////////////
// Localization
//////////////////////////////////////////////////////////////////////////*/
/**
* Returns a list of localizations that this service supports.
*/
public List<Localization> getSupportedLocalizations() {
return Collections.singletonList(Localization.DEFAULT);
}
/**
* Returns a list of countries that this service supports.<br>
*/
public List<ContentCountry> getSupportedCountries() {
return Collections.singletonList(ContentCountry.DEFAULT);
}
/**
* Returns the localization that should be used in this service. It will get which localization
* the user prefer (using {@link NewPipe#getPreferredLocalization()}), then it will:
* <ul>
* <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>Fallback to the {@link Localization#DEFAULT default} localization.</li>
* </ul>
*/
public Localization getLocalization() {
final Localization preferredLocalization = NewPipe.getPreferredLocalization();
// Check the localization's language and country
if (getSupportedLocalizations().contains(preferredLocalization)) {
return preferredLocalization;
}
// Fallback to the first supported language that matches the preferred language
for (Localization supportedLanguage : getSupportedLocalizations()) {
if (supportedLanguage.getLanguageCode().equals(preferredLocalization.getLanguageCode())) {
return supportedLanguage;
}
}
return Localization.DEFAULT;
}
/**
* Returns the country that should be used to fetch content in this service. It will get which country
* the user prefer (using {@link NewPipe#getPreferredContentCountry()}), then it will:
* <ul>
* <li>Check if the country is supported by this service.</li>
* <li>If not, fallback to the {@link ContentCountry#DEFAULT default} country.</li>
* </ul>
*/
public ContentCountry getContentCountry() {
final ContentCountry preferredContentCountry = NewPipe.getPreferredContentCountry();
if (getSupportedCountries().contains(preferredContentCountry)) {
return preferredContentCountry;
}
return ContentCountry.DEFAULT;
}
/**
* Get an instance of the time ago parser using the patterns related to the passed localization.<br>
* <br>
* Just like {@link #getLocalization()}, it will also try to fallback to a less specific localization if
* the exact one is not available/supported.
*
* @throws IllegalArgumentException if the localization is not supported (parsing patterns are not present).
*/
public TimeAgoParser getTimeAgoParser(Localization localization) {
final TimeAgoParser targetParser = TimeAgoPatternsManager.getTimeAgoParserFor(localization);
if (targetParser != null) {
return targetParser;
}
if (!localization.getCountryCode().isEmpty()) {
final Localization lessSpecificLocalization = new Localization(localization.getLanguageCode());
final TimeAgoParser lessSpecificParser = TimeAgoPatternsManager.getTimeAgoParserFor(lessSpecificLocalization);
if (lessSpecificParser != null) {
return lessSpecificParser;
}
}
throw new IllegalArgumentException("Localization is not supported (\"" + localization.toString() + "\")");
}
} }

View File

@ -1,48 +0,0 @@
package org.schabi.newpipe.extractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException;
import java.util.List;
/*
* Created by Christian Schabesberger on 28.09.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* SuggestionExtractor.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public abstract class SuggestionExtractor {
private final int serviceId;
private final Localization localization;
public SuggestionExtractor(int serviceId, Localization localization) {
this.serviceId = serviceId;
this.localization = localization;
}
public abstract List<String> suggestionList(String query) throws IOException, ExtractionException;
public int getServiceId() {
return serviceId;
}
protected Localization getLocalization() {
return localization;
}
}

View File

@ -3,9 +3,8 @@ package org.schabi.newpipe.extractor.channel;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
/* /*
* Created by Christian Schabesberger on 25.07.16. * Created by Christian Schabesberger on 25.07.16.
@ -29,8 +28,8 @@ import org.schabi.newpipe.extractor.utils.Localization;
public abstract class ChannelExtractor extends ListExtractor<StreamInfoItem> { public abstract class ChannelExtractor extends ListExtractor<StreamInfoItem> {
public ChannelExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public ChannelExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
public abstract String getAvatarUrl() throws ParsingException; public abstract String getAvatarUrl() throws ParsingException;

View File

@ -6,10 +6,10 @@ import org.schabi.newpipe.extractor.NewPipe;
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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.ExtractorHelper; import org.schabi.newpipe.extractor.utils.ExtractorHelper;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException; import java.io.IOException;

View File

@ -3,12 +3,11 @@ package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.utils.Localization;
public abstract class CommentsExtractor extends ListExtractor<CommentsInfoItem> { public abstract class CommentsExtractor extends ListExtractor<CommentsInfoItem> {
public CommentsExtractor(StreamingService service, ListLinkHandler uiHandler, Localization localization) { public CommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
super(service, uiHandler, localization); super(service, uiHandler);
// TODO Auto-generated constructor stub // TODO Auto-generated constructor stub
} }

View File

@ -1,6 +1,9 @@
package org.schabi.newpipe.extractor.comments; package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import javax.annotation.Nullable;
public class CommentsInfoItem extends InfoItem { public class CommentsInfoItem extends InfoItem {
@ -9,20 +12,28 @@ public class CommentsInfoItem extends InfoItem{
private String authorName; private String authorName;
private String authorThumbnail; private String authorThumbnail;
private String authorEndpoint; private String authorEndpoint;
private String publishedTime; private String textualPublishedTime;
private Integer likeCount; @Nullable private DateWrapper publishedTime;
private int likeCount;
public CommentsInfoItem(int serviceId, String url, String name) { public CommentsInfoItem(int serviceId, String url, String name) {
super(InfoType.COMMENT, serviceId, url, name); super(InfoType.COMMENT, serviceId, url, name);
// TODO Auto-generated constructor stub }
public String getCommentId() {
return commentId;
}
public void setCommentId(String commentId) {
this.commentId = commentId;
} }
public String getCommentText() { public String getCommentText() {
return commentText; return commentText;
} }
public void setCommentText(String contentText) { public void setCommentText(String commentText) {
this.commentText = contentText; this.commentText = commentText;
} }
public String getAuthorName() { public String getAuthorName() {
@ -49,28 +60,28 @@ public class CommentsInfoItem extends InfoItem{
this.authorEndpoint = authorEndpoint; this.authorEndpoint = authorEndpoint;
} }
public String getPublishedTime() { public String getTextualPublishedTime() {
return textualPublishedTime;
}
public void setTextualPublishedTime(String textualPublishedTime) {
this.textualPublishedTime = textualPublishedTime;
}
@Nullable
public DateWrapper getPublishedTime() {
return publishedTime; return publishedTime;
} }
public void setPublishedTime(String publishedTime) { public void setPublishedTime(@Nullable DateWrapper publishedTime) {
this.publishedTime = publishedTime; this.publishedTime = publishedTime;
} }
public Integer getLikeCount() { public int getLikeCount() {
return likeCount; return likeCount;
} }
public void setLikeCount(Integer likeCount) { public void setLikeCount(int likeCount) {
this.likeCount = likeCount; this.likeCount = likeCount;
} }
public String getCommentId() {
return commentId;
}
public void setCommentId(String commentId) {
this.commentId = commentId;
}
} }

View File

@ -2,6 +2,9 @@ package org.schabi.newpipe.extractor.comments;
import org.schabi.newpipe.extractor.InfoItemExtractor; import org.schabi.newpipe.extractor.InfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import javax.annotation.Nullable;
public interface CommentsInfoItemExtractor extends InfoItemExtractor { public interface CommentsInfoItemExtractor extends InfoItemExtractor {
String getCommentId() throws ParsingException; String getCommentId() throws ParsingException;
@ -9,6 +12,8 @@ public interface CommentsInfoItemExtractor extends InfoItemExtractor {
String getAuthorName() throws ParsingException; String getAuthorName() throws ParsingException;
String getAuthorThumbnail() throws ParsingException; String getAuthorThumbnail() throws ParsingException;
String getAuthorEndpoint() throws ParsingException; String getAuthorEndpoint() throws ParsingException;
String getPublishedTime() throws ParsingException; String getTextualPublishedTime() throws ParsingException;
Integer getLikeCount() throws ParsingException; @Nullable
DateWrapper getPublishedTime() throws ParsingException;
int getLikeCount() throws ParsingException;
} }

View File

@ -1,12 +1,12 @@
package org.schabi.newpipe.extractor.comments; package org.schabi.newpipe.extractor.comments;
import java.util.List;
import java.util.Vector;
import org.schabi.newpipe.extractor.InfoItem; 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.List;
import java.util.Vector;
public class CommentsInfoItemsCollector extends InfoItemsCollector<CommentsInfoItem, CommentsInfoItemExtractor> { public class CommentsInfoItemsCollector extends InfoItemsCollector<CommentsInfoItem, CommentsInfoItemExtractor> {
public CommentsInfoItemsCollector(int serviceId) { public CommentsInfoItemsCollector(int serviceId) {
@ -49,6 +49,11 @@ public class CommentsInfoItemsCollector extends InfoItemsCollector<CommentsInfoI
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try {
resultItem.setTextualPublishedTime(extractor.getTextualPublishedTime());
} catch (Exception e) {
addError(e);
}
try { try {
resultItem.setPublishedTime(extractor.getPublishedTime()); resultItem.setPublishedTime(extractor.getPublishedTime());
} catch (Exception e) { } catch (Exception e) {

View File

@ -0,0 +1,144 @@
package org.schabi.newpipe.extractor.downloader;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* A base for downloader implementations that NewPipe will use
* to download needed resources during extraction.
*/
public abstract class Downloader {
/**
* Do a GET request to get the resource that the url is pointing to.<br>
* <br>
* This method calls {@link #get(String, Map, Localization)} with the default preferred 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
* @return the result of the GET request
*/
public Response get(String url) throws IOException, ReCaptchaException {
return get(url, null, NewPipe.getPreferredLocalization());
}
/**
* Do a GET request to get the resource that the url is pointing to.<br>
* <br>
* It will set the {@code Accept-Language} header to the language of the localization parameter.
*
* @param url the URL that is pointing to the wanted resource
* @param localization the source of the value of the {@code Accept-Language} header
* @return the result of the GET request
*/
public Response get(String url, @Nullable Localization localization) throws IOException, ReCaptchaException {
return get(url, null, localization);
}
/**
* Do a GET request with the specified headers.
*
* @param url the URL that is pointing to the wanted resource
* @param headers a list of headers that will be used in the request.
* Any default headers <b>should</b> be overridden by these.
* @return the result of the GET request
*/
public Response get(String url, @Nullable Map<String, List<String>> headers) throws IOException, ReCaptchaException {
return get(url, headers, NewPipe.getPreferredLocalization());
}
/**
* Do a GET request with the specified headers.<br>
* <br>
* It will set the {@code Accept-Language} header to the language of the localization parameter.
*
* @param url the URL that is pointing to the wanted resource
* @param headers a list of headers that will be used in the request.
* Any default headers <b>should</b> be overridden by these.
* @param localization the source of the value of the {@code Accept-Language} header
* @return the result of the GET request
*/
public Response get(String url, @Nullable Map<String, List<String>> headers, @Nullable Localization localization)
throws IOException, ReCaptchaException {
return execute(Request.newBuilder()
.get(url)
.headers(headers)
.localization(localization)
.build());
}
/**
* Do a HEAD request.
*
* @param url the URL that is pointing to the wanted resource
* @return the result of the HEAD request
*/
public Response head(String url) throws IOException, ReCaptchaException {
return head(url, null);
}
/**
* Do a HEAD request with the specified headers.
*
* @param url the URL that is pointing to the wanted resource
* @param headers a list of headers that will be used in the request.
* Any default headers <b>should</b> be overridden by these.
* @return the result of the HEAD request
*/
public Response head(String url, @Nullable Map<String, List<String>> headers)
throws IOException, ReCaptchaException {
return execute(Request.newBuilder()
.head(url)
.headers(headers)
.build());
}
/**
* Do a POST request with the specified headers, sending the data array.
*
* @param url the URL that is pointing to the wanted resource
* @param headers a list of headers that will be used in the request.
* Any default headers <b>should</b> be overridden by these.
* @param dataToSend byte array that will be sent when doing the request.
* @return the result of the GET request
*/
public Response post(String url, @Nullable Map<String, List<String>> headers, @Nullable byte[] dataToSend)
throws IOException, ReCaptchaException {
return post(url, headers, dataToSend, NewPipe.getPreferredLocalization());
}
/**
* Do a POST request with the specified headers, sending the data array.
* <br>
* It will set the {@code Accept-Language} header to the language of the localization parameter.
*
* @param url the URL that is pointing to the wanted resource
* @param headers a list of headers that will be used in the request.
* Any default headers <b>should</b> be overridden by these.
* @param dataToSend byte array that will be sent when doing the request.
* @param localization the source of the value of the {@code Accept-Language} header
* @return the result of the GET request
*/
public Response post(String url, @Nullable Map<String, List<String>> headers, @Nullable byte[] dataToSend, @Nullable Localization localization)
throws IOException, ReCaptchaException {
return execute(Request.newBuilder()
.post(url, dataToSend)
.headers(headers)
.localization(localization)
.build());
}
/**
* Do a request using the specified {@link Request} object.
*
* @return the result of the request
*/
public abstract Response execute(@Nonnull Request request) throws IOException, ReCaptchaException;
}

View File

@ -0,0 +1,244 @@
package org.schabi.newpipe.extractor.downloader;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
/**
* An object that holds request information used when {@link Downloader#execute(Request) executing} a request.
*/
public class Request {
private final String httpMethod;
private final String url;
private final Map<String, List<String>> headers;
@Nullable private final byte[] dataToSend;
@Nullable private final Localization localization;
public Request(String httpMethod, String url, Map<String, List<String>> headers, @Nullable byte[] dataToSend,
@Nullable Localization localization, 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.url = url;
this.dataToSend = dataToSend;
this.localization = localization;
Map<String, List<String>> headersToSet = null;
if (headers == null) headers = Collections.emptyMap();
if (automaticLocalizationHeader && localization != null) {
headersToSet = new LinkedHashMap<>(headersFromLocalization(localization));
headersToSet.putAll(headers);
}
this.headers = Collections.unmodifiableMap(headersToSet == null ? headers : headersToSet);
}
private Request(Builder builder) {
this(builder.httpMethod, builder.url, builder.headers, builder.dataToSend,
builder.localization, builder.automaticLocalizationHeader);
}
/**
* A http method (i.e. {@code GET, POST, HEAD}).
*/
public String httpMethod() {
return httpMethod;
}
/**
* The URL that is pointing to the wanted resource.
*/
public String url() {
return url;
}
/**
* 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.
*/
public Map<String, List<String>> headers() {
return headers;
}
/**
* An optional byte array that will be sent when doing the request, very commonly used in
* {@code POST} requests.<br>
* <br>
* The implementation should make note of some recommended headers
* (for example, {@code Content-Length} in a post request).
*/
@Nullable
public byte[] dataToSend() {
return dataToSend;
}
/**
* A localization object that should be used when executing a request.<br>
* <br>
* Usually the {@code Accept-Language} will be set to this value (a helper
* method to do this easily: {@link Request#headersFromLocalization(Localization)}).
*/
@Nullable
public Localization localization() {
return localization;
}
public static Builder newBuilder() {
return new Builder();
}
public static final class Builder {
private String httpMethod;
private String url;
private Map<String, List<String>> headers = new LinkedHashMap<>();
private byte[] dataToSend;
private Localization localization;
private boolean automaticLocalizationHeader = true;
public Builder() {
}
/**
* A http method (i.e. {@code GET, POST, HEAD}).
*/
public Builder httpMethod(String httpMethod) {
this.httpMethod = httpMethod;
return this;
}
/**
* The URL that is pointing to the wanted resource.
*/
public Builder url(String url) {
this.url = url;
return this;
}
/**
* 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.
*/
public Builder headers(@Nullable Map<String, List<String>> headers) {
if (headers == null) {
this.headers.clear();
return this;
}
this.headers.clear();
this.headers.putAll(headers);
return this;
}
/**
* An optional byte array that will be sent when doing the request, very commonly used in
* {@code POST} requests.<br>
* <br>
* The implementation should make note of some recommended headers
* (for example, {@code Content-Length} in a post request).
*/
public Builder dataToSend(byte[] dataToSend) {
this.dataToSend = dataToSend;
return this;
}
/**
* A localization object that should be used when executing a request.<br>
* <br>
* Usually the {@code Accept-Language} will be set to this value (a helper
* method to do this easily: {@link Request#headersFromLocalization(Localization)}).
*/
public Builder localization(Localization localization) {
this.localization = localization;
return this;
}
/**
* If localization headers should automatically be included in the request.
*/
public Builder automaticLocalizationHeader(boolean automaticLocalizationHeader) {
this.automaticLocalizationHeader = automaticLocalizationHeader;
return this;
}
public Request build() {
return new Request(this);
}
/*//////////////////////////////////////////////////////////////////////////
// Http Methods Utils
//////////////////////////////////////////////////////////////////////////*/
public Builder get(String url) {
this.httpMethod = "GET";
this.url = url;
return this;
}
public Builder head(String url) {
this.httpMethod = "HEAD";
this.url = url;
return this;
}
public Builder post(String url, @Nullable byte[] dataToSend) {
this.httpMethod = "POST";
this.url = url;
this.dataToSend = dataToSend;
return this;
}
/*//////////////////////////////////////////////////////////////////////////
// Additional Headers Utils
//////////////////////////////////////////////////////////////////////////*/
public Builder setHeaders(String headerName, List<String> headerValueList) {
this.headers.remove(headerName);
this.headers.put(headerName, headerValueList);
return this;
}
public Builder addHeaders(String headerName, List<String> headerValueList) {
@Nullable List<String> currentHeaderValueList = this.headers.get(headerName);
if (currentHeaderValueList == null) {
currentHeaderValueList = new ArrayList<>();
}
currentHeaderValueList.addAll(headerValueList);
this.headers.put(headerName, headerValueList);
return this;
}
public Builder setHeader(String headerName, String headerValue) {
return setHeaders(headerName, Collections.singletonList(headerValue));
}
public Builder addHeader(String headerName, String headerValue) {
return addHeaders(headerName, Collections.singletonList(headerValue));
}
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@SuppressWarnings("WeakerAccess")
@Nonnull
public static Map<String, List<String>> headersFromLocalization(@Nullable Localization localization) {
if (localization == null) return Collections.emptyMap();
final Map<String, List<String>> headers = new LinkedHashMap<>();
if (!localization.getCountryCode().isEmpty()) {
headers.put("Accept-Language", Collections.singletonList(localization.getLocalizationCode() +
", " + localization.getLanguageCode() + ";q=0.9"));
} else {
headers.put("Accept-Language", Collections.singletonList(localization.getLanguageCode()));
}
return headers;
}
}

View File

@ -0,0 +1,66 @@
package org.schabi.newpipe.extractor.downloader;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* A Data class used to hold the results from requests made by the Downloader implementation.
*/
public class Response {
private final int responseCode;
private final String responseMessage;
private final Map<String, List<String>> responseHeaders;
private final String responseBody;
public Response(int responseCode, String responseMessage, Map<String, List<String>> responseHeaders, @Nullable String responseBody) {
this.responseCode = responseCode;
this.responseMessage = responseMessage;
this.responseHeaders = responseHeaders != null ? responseHeaders : Collections.<String, List<String>>emptyMap();
this.responseBody = responseBody == null ? "" : responseBody;
}
public int responseCode() {
return responseCode;
}
public String responseMessage() {
return responseMessage;
}
public Map<String, List<String>> responseHeaders() {
return responseHeaders;
}
@Nonnull
public String responseBody() {
return responseBody;
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
/**
* 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}).
*
* @param name the name of the header
* @return the first value assigned to this header
*/
@Nullable
public String getHeader(String name) {
for (Map.Entry<String, List<String>> headerEntry : responseHeaders.entrySet()) {
if (headerEntry.getKey().equalsIgnoreCase(name)) {
if (headerEntry.getValue().size() > 0) {
return headerEntry.getValue().get(0);
}
}
}
return null;
}
}

View File

@ -24,9 +24,8 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -35,9 +34,8 @@ public abstract class KioskExtractor<T extends InfoItem> extends ListExtractor<T
public KioskExtractor(StreamingService streamingService, public KioskExtractor(StreamingService streamingService,
ListLinkHandler linkHandler, ListLinkHandler linkHandler,
String kioskId, String kioskId) {
Localization localization) { super(streamingService, linkHandler);
super(streamingService, linkHandler, localization);
this.id = kioskId; this.id = kioskId;
} }

View File

@ -20,11 +20,14 @@ package org.schabi.newpipe.extractor.kiosk;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/ */
import org.schabi.newpipe.extractor.*; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.extractor.NewPipe;
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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
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.utils.ExtractorHelper; import org.schabi.newpipe.extractor.utils.ExtractorHelper;
import java.io.IOException; import java.io.IOException;
@ -37,7 +40,8 @@ public class KioskInfo extends ListInfo<StreamInfoItem> {
public static ListExtractor.InfoItemsPage<StreamInfoItem> getMoreItems(StreamingService service, public static ListExtractor.InfoItemsPage<StreamInfoItem> getMoreItems(StreamingService service,
String url, String url,
String pageUrl) throws IOException, ExtractionException { String pageUrl)
throws IOException, ExtractionException {
KioskList kl = service.getKioskList(); KioskList kl = service.getKioskList();
KioskExtractor extractor = kl.getExtractorByUrl(url, pageUrl); KioskExtractor extractor = kl.getExtractorByUrl(url, pageUrl);
return extractor.getPage(pageUrl); return extractor.getPage(pageUrl);
@ -47,8 +51,7 @@ public class KioskInfo extends ListInfo<StreamInfoItem> {
return getInfo(NewPipe.getServiceByUrl(url), url); return getInfo(NewPipe.getServiceByUrl(url), url);
} }
public static KioskInfo getInfo(StreamingService service, public static KioskInfo getInfo(StreamingService service, String url) throws IOException, ExtractionException {
String url) throws IOException, ExtractionException {
KioskList kl = service.getKioskList(); KioskList kl = service.getKioskList();
KioskExtractor extractor = kl.getExtractorByUrl(url, null); KioskExtractor extractor = kl.getExtractorByUrl(url, null);
extractor.fetchPage(); extractor.fetchPage();

View File

@ -2,28 +2,33 @@ package org.schabi.newpipe.extractor.kiosk;
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.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class KioskList { public class KioskList {
public interface KioskExtractorFactory { public interface KioskExtractorFactory {
KioskExtractor createNewKiosk(final StreamingService streamingService, KioskExtractor createNewKiosk(final StreamingService streamingService,
final String url, final String url,
final String kioskId, final String kioskId)
final Localization localization)
throws ExtractionException, IOException; throws ExtractionException, IOException;
} }
private final int service_id; private final StreamingService service;
private final HashMap<String, KioskEntry> kioskList = new HashMap<>(); private final HashMap<String, KioskEntry> kioskList = new HashMap<>();
private String defaultKiosk = null; private String defaultKiosk = null;
@Nullable private Localization forcedLocalization;
@Nullable private ContentCountry forcedContentCountry;
private class KioskEntry { private class KioskEntry {
public KioskEntry(KioskExtractorFactory ef, ListLinkHandlerFactory h) { public KioskEntry(KioskExtractorFactory ef, ListLinkHandlerFactory h) {
extractorFactory = ef; extractorFactory = ef;
@ -33,8 +38,8 @@ public class KioskList {
final ListLinkHandlerFactory handlerFactory; final ListLinkHandlerFactory handlerFactory;
} }
public KioskList(int service_id) { public KioskList(StreamingService service) {
this.service_id = service_id; this.service = service;
} }
public void addKioskEntry(KioskExtractorFactory extractorFactory, ListLinkHandlerFactory handlerFactory, String id) public void addKioskEntry(KioskExtractorFactory extractorFactory, ListLinkHandlerFactory handlerFactory, String id)
@ -89,8 +94,13 @@ public class KioskList {
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 {
return ke.extractorFactory.createNewKiosk(NewPipe.getService(service_id), final KioskExtractor kioskExtractor = ke.extractorFactory.createNewKiosk(service,
ke.handlerFactory.fromId(kioskId).getUrl(), kioskId, localization); ke.handlerFactory.fromId(kioskId).getUrl(), kioskId);
if (forcedLocalization != null) kioskExtractor.forceLocalization(forcedLocalization);
if (forcedContentCountry != null) kioskExtractor.forceContentCountry(forcedContentCountry);
return kioskExtractor;
} }
} }
@ -117,4 +127,12 @@ public class KioskList {
public ListLinkHandlerFactory getListLinkHandlerFactoryByType(String type) { public ListLinkHandlerFactory getListLinkHandlerFactoryByType(String type) {
return kioskList.get(type).handlerFactory; return kioskList.get(type).handlerFactory;
} }
public void forceLocalization(@Nullable Localization localization) {
this.forcedLocalization = localization;
}
public void forceContentCountry(@Nullable ContentCountry contentCountry) {
this.forcedContentCountry = contentCountry;
}
} }

View File

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

View File

@ -0,0 +1,39 @@
package org.schabi.newpipe.extractor.localization;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.Serializable;
import java.util.Calendar;
/**
* A wrapper class that provides a field to describe if the date is precise or just an approximation.
*/
public class DateWrapper implements Serializable {
@NonNull private final Calendar date;
private final boolean isApproximation;
public DateWrapper(@NonNull Calendar date) {
this(date, false);
}
public DateWrapper(@NonNull Calendar date, boolean isApproximation) {
this.date = date;
this.isApproximation = isApproximation;
}
/**
* @return the wrapped date.
*/
@NonNull
public Calendar date() {
return date;
}
/**
* @return if the date is considered is precise or just an approximation (e.g. service only returns an approximation
* like 2 weeks ago instead of a precise date).
*/
public boolean isApproximation() {
return isApproximation;
}
}

View File

@ -0,0 +1,99 @@
package org.schabi.newpipe.extractor.localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.*;
public class Localization implements Serializable {
public static final Localization DEFAULT = new Localization("en", "GB");
@Nonnull private final String languageCode;
@Nullable private final String countryCode;
/**
* @param localizationCodeList a list of localization code, formatted like {@link #getLocalizationCode()}
*/
public static List<Localization> listFrom(String... localizationCodeList) {
final List<Localization> toReturn = new ArrayList<>();
for (String localizationCode : localizationCodeList) {
toReturn.add(fromLocalizationCode(localizationCode));
}
return Collections.unmodifiableList(toReturn);
}
/**
* @param localizationCode a localization code, formatted like {@link #getLocalizationCode()}
*/
public static Localization fromLocalizationCode(String localizationCode) {
final int indexSeparator = localizationCode.indexOf("-");
final String languageCode, countryCode;
if (indexSeparator != -1) {
languageCode = localizationCode.substring(0, indexSeparator);
countryCode = localizationCode.substring(indexSeparator + 1);
} else {
languageCode = localizationCode;
countryCode = null;
}
return new Localization(languageCode, countryCode);
}
public Localization(@Nonnull String languageCode, @Nullable String countryCode) {
this.languageCode = languageCode;
this.countryCode = countryCode;
}
public Localization(@Nonnull String languageCode) {
this(languageCode, null);
}
public String getLanguageCode() {
return languageCode;
}
@Nonnull
public String getCountryCode() {
return countryCode == null ? "" : countryCode;
}
public Locale asLocale() {
return new Locale(getLanguageCode(), getCountryCode());
}
public static Localization fromLocale(@Nonnull Locale locale) {
return new Localization(locale.getLanguage(), locale.getCountry());
}
/**
* Return a formatted string in the form of: {@code language-Country}, or
* just {@code language} if country is {@code null}.
*/
public String getLocalizationCode() {
return languageCode + (countryCode == null ? "" : "-" + countryCode);
}
@Override
public String toString() {
return "Localization[" + getLocalizationCode() + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Localization)) return false;
Localization that = (Localization) o;
if (!languageCode.equals(that.languageCode)) return false;
return countryCode != null ? countryCode.equals(that.countryCode) : that.countryCode == null;
}
@Override
public int hashCode() {
int result = languageCode.hashCode();
result = 31 * result + (countryCode != null ? countryCode.hashCode() : 0);
return result;
}
}

View File

@ -0,0 +1,174 @@
package org.schabi.newpipe.extractor.localization;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
import org.schabi.newpipe.extractor.timeago.TimeAgoUnit;
import org.schabi.newpipe.extractor.utils.Parser;
import java.util.Calendar;
import java.util.Collection;
import java.util.Map;
import java.util.regex.Pattern;
/**
* A helper class that is meant to be used by services that need to parse upload dates in the
* format '2 days ago' or similar.
*/
public class TimeAgoParser {
private final PatternsHolder patternsHolder;
private final Calendar consistentNow;
/**
* Creates a helper to parse upload dates in the format '2 days ago'.
* <p>
* Instantiate a new {@link TimeAgoParser} every time you extract a new batch of items.
* </p>
* @param patternsHolder An object that holds the "time ago" patterns, special cases, and the language word separator.
*/
public TimeAgoParser(PatternsHolder patternsHolder) {
this.patternsHolder = patternsHolder;
consistentNow = Calendar.getInstance();
}
/**
* Parses a textual date in the format '2 days ago' into a Calendar representation which is then wrapped in a
* {@link DateWrapper} object.
* <p>
* Beginning with days ago, the date is considered as an approximation.
*
* @param textualDate The original date as provided by the streaming service
* @return The parsed time (can be approximated)
* @throws ParsingException if the time unit could not be recognized
*/
public DateWrapper parse(String textualDate) throws ParsingException {
for (Map.Entry<TimeAgoUnit, Map<String, Integer>> caseUnitEntry : patternsHolder.specialCases().entrySet()) {
final TimeAgoUnit timeAgoUnit = caseUnitEntry.getKey();
for (Map.Entry<String, Integer> caseMapToAmountEntry : caseUnitEntry.getValue().entrySet()) {
final String caseText = caseMapToAmountEntry.getKey();
final Integer caseAmount = caseMapToAmountEntry.getValue();
if (textualDateMatches(textualDate, caseText)) {
return getResultFor(caseAmount, timeAgoUnit);
}
}
}
int timeAgoAmount;
try {
timeAgoAmount = parseTimeAgoAmount(textualDate);
} catch (NumberFormatException e) {
// If there is no valid number in the textual date,
// assume it is 1 (as in 'a second ago').
timeAgoAmount = 1;
}
final TimeAgoUnit timeAgoUnit = parseTimeAgoUnit(textualDate);
return getResultFor(timeAgoAmount, timeAgoUnit);
}
private int parseTimeAgoAmount(String textualDate) throws NumberFormatException {
String timeValueStr = textualDate.replaceAll("\\D+", "");
return Integer.parseInt(timeValueStr);
}
private TimeAgoUnit parseTimeAgoUnit(String textualDate) throws ParsingException {
for (Map.Entry<TimeAgoUnit, Collection<String>> entry : patternsHolder.asMap().entrySet()) {
final TimeAgoUnit timeAgoUnit = entry.getKey();
for (String agoPhrase : entry.getValue()) {
if (textualDateMatches(textualDate, agoPhrase)) {
return timeAgoUnit;
}
}
}
throw new ParsingException("Unable to parse the date: " + textualDate);
}
private boolean textualDateMatches(String textualDate, String agoPhrase) {
if (textualDate.equals(agoPhrase)) {
return true;
}
if (patternsHolder.wordSeparator().isEmpty()) {
return textualDate.toLowerCase().contains(agoPhrase.toLowerCase());
} else {
final String escapedPhrase = Pattern.quote(agoPhrase.toLowerCase());
final String escapedSeparator;
if (patternsHolder.wordSeparator().equals(" ")) {
// 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]";
} else {
escapedSeparator = Pattern.quote(patternsHolder.wordSeparator());
}
// (^|separator)pattern($|separator)
// Check if the pattern is surrounded by separators or start/end of the string.
final String pattern =
"(^|" + escapedSeparator + ")" + escapedPhrase + "($|" + escapedSeparator + ")";
return Parser.isMatch(pattern, textualDate.toLowerCase());
}
}
private DateWrapper getResultFor(int timeAgoAmount, TimeAgoUnit timeAgoUnit) {
final Calendar calendarTime = getNow();
boolean isApproximation = false;
switch (timeAgoUnit) {
case SECONDS:
calendarTime.add(Calendar.SECOND, -timeAgoAmount);
break;
case MINUTES:
calendarTime.add(Calendar.MINUTE, -timeAgoAmount);
break;
case HOURS:
calendarTime.add(Calendar.HOUR_OF_DAY, -timeAgoAmount);
break;
case DAYS:
calendarTime.add(Calendar.DAY_OF_MONTH, -timeAgoAmount);
isApproximation = true;
break;
case WEEKS:
calendarTime.add(Calendar.WEEK_OF_YEAR, -timeAgoAmount);
isApproximation = true;
break;
case MONTHS:
calendarTime.add(Calendar.MONTH, -timeAgoAmount);
isApproximation = true;
break;
case YEARS:
calendarTime.add(Calendar.YEAR, -timeAgoAmount);
// Prevent `PrettyTime` from showing '12 months ago'.
calendarTime.add(Calendar.DAY_OF_MONTH, -1);
isApproximation = true;
break;
}
if (isApproximation) {
markApproximatedTime(calendarTime);
}
return new DateWrapper(calendarTime, isApproximation);
}
private Calendar getNow() {
return (Calendar) consistentNow.clone();
}
/**
* Marks the time as approximated by setting minutes, seconds and milliseconds to 0.
* @param calendarTime Time to be marked as approximated
*/
private void markApproximatedTime(Calendar calendarTime) {
calendarTime.set(Calendar.MINUTE, 0);
calendarTime.set(Calendar.SECOND, 0);
calendarTime.set(Calendar.MILLISECOND, 0);
}
}

View File

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

View File

@ -5,12 +5,11 @@ import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> { public abstract class PlaylistExtractor extends ListExtractor<StreamInfoItem> {
public PlaylistExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public PlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
public abstract String getThumbnailUrl() throws ParsingException; public abstract String getThumbnailUrl() throws ParsingException;

View File

@ -9,7 +9,6 @@ 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 org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException; import java.io.IOException;

View File

@ -6,7 +6,8 @@ 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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull;
public abstract class SearchExtractor extends ListExtractor<InfoItem> { public abstract class SearchExtractor extends ListExtractor<InfoItem> {
@ -18,10 +19,8 @@ public abstract class SearchExtractor extends ListExtractor<InfoItem> {
private final InfoItemsSearchCollector collector; private final InfoItemsSearchCollector collector;
public SearchExtractor(StreamingService service, public SearchExtractor(StreamingService service, SearchQueryHandler linkHandler) {
SearchQueryHandler linkHandler, super(service, linkHandler);
Localization localization) {
super(service, linkHandler, localization);
collector = new InfoItemsSearchCollector(service.getServiceId()); collector = new InfoItemsSearchCollector(service.getServiceId());
} }
@ -40,6 +39,7 @@ public abstract class SearchExtractor extends ListExtractor<InfoItem> {
return (SearchQueryHandler) super.getLinkHandler(); return (SearchQueryHandler) super.getLinkHandler();
} }
@Nonnull
@Override @Override
public String getName() { public String getName() {
return getLinkHandler().getSearchString(); return getLinkHandler().getSearchString();

View File

@ -7,7 +7,6 @@ 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;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException; import java.io.IOException;

View File

@ -1,24 +1,12 @@
package org.schabi.newpipe.extractor.services.media_ccc; package org.schabi.newpipe.extractor.services.media_ccc;
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;
import java.io.IOException;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.SuggestionExtractor;
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.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.*;
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.media_ccc.extractors.MediaCCCConferenceExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceExtractor;
@ -31,7 +19,13 @@ import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearc
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.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.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.io.IOException;
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(int id) { public MediaCCCService(int id) {
@ -39,8 +33,8 @@ public class MediaCCCService extends StreamingService {
} }
@Override @Override
public SearchExtractor getSearchExtractor(SearchQueryHandler query, Localization localization) { public SearchExtractor getSearchExtractor(SearchQueryHandler query) {
return new MediaCCCSearchExtractor(this, query, localization); return new MediaCCCSearchExtractor(this, query);
} }
@Override @Override
@ -64,28 +58,28 @@ public class MediaCCCService extends StreamingService {
} }
@Override @Override
public StreamExtractor getStreamExtractor(LinkHandler linkHandler, Localization localization) { public StreamExtractor getStreamExtractor(LinkHandler linkHandler) {
return new MediaCCCStreamExtractor(this, linkHandler, localization); return new MediaCCCStreamExtractor(this, linkHandler);
} }
@Override @Override
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler, Localization localization) { public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) {
return new MediaCCCConferenceExtractor(this, linkHandler, localization); return new MediaCCCConferenceExtractor(this, linkHandler);
} }
@Override @Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler, Localization localization) { public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) {
return null; return null;
} }
@Override @Override
public SuggestionExtractor getSuggestionExtractor(Localization localization) { public SuggestionExtractor getSuggestionExtractor() {
return null; return null;
} }
@Override @Override
public KioskList getKioskList() throws ExtractionException { public KioskList getKioskList() throws ExtractionException {
KioskList list = new KioskList(getServiceId()); KioskList list = new KioskList(this);
// add kiosks here e.g.: // add kiosks here e.g.:
try { try {
@ -93,10 +87,9 @@ public class MediaCCCService extends StreamingService {
@Override @Override
public KioskExtractor createNewKiosk(StreamingService streamingService, public KioskExtractor createNewKiosk(StreamingService streamingService,
String url, String url,
String kioskId, String kioskId) throws ExtractionException, IOException {
Localization localization) throws ExtractionException, IOException {
return new MediaCCCConferenceKiosk(MediaCCCService.this, return new MediaCCCConferenceKiosk(MediaCCCService.this,
new MediaCCCConferencesListLinkHandlerFactory().fromUrl(url), kioskId, localization); new MediaCCCConferencesListLinkHandlerFactory().fromUrl(url), kioskId);
} }
}, new MediaCCCConferencesListLinkHandlerFactory(), "conferences"); }, new MediaCCCConferencesListLinkHandlerFactory(), "conferences");
list.setDefaultKiosk("conferences"); list.setDefaultKiosk("conferences");
@ -118,7 +111,7 @@ public class MediaCCCService extends StreamingService {
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler, Localization localization) public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return null; return null;
} }

View File

@ -4,16 +4,15 @@ 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.Downloader;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCStreamInfoItemExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCStreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -22,8 +21,8 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
private JsonObject conferenceData; private JsonObject conferenceData;
public MediaCCCConferenceExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public MediaCCCConferenceExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Override @Override
@ -75,7 +74,7 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
try { try {
conferenceData = JsonParser.object().from(downloader.download(getUrl())); conferenceData = JsonParser.object().from(downloader.get(getUrl()).responseBody());
} catch (JsonParserException jpe) { } catch (JsonParserException jpe) {
throw new ExtractionException("Could not parse json returnd by url: " + getUrl()); throw new ExtractionException("Could not parse json returnd by url: " + getUrl());
} }
@ -87,6 +86,7 @@ public class MediaCCCConferenceExtractor extends ChannelExtractor {
return conferenceData.getString("title"); return conferenceData.getString("title");
} }
@Nonnull
@Override @Override
public String getOriginalUrl() throws ParsingException { public String getOriginalUrl() throws ParsingException {
return "https://media.ccc.de/c/" + conferenceData.getString("acronym"); return "https://media.ccc.de/c/" + conferenceData.getString("acronym");

View File

@ -4,16 +4,15 @@ 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.Downloader;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector; import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector;
import org.schabi.newpipe.extractor.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.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCConferenceInfoItemExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCConferenceInfoItemExtractor;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -24,9 +23,8 @@ public class MediaCCCConferenceKiosk extends KioskExtractor<ChannelInfoItem> {
public MediaCCCConferenceKiosk(StreamingService streamingService, public MediaCCCConferenceKiosk(StreamingService streamingService,
ListLinkHandler linkHandler, ListLinkHandler linkHandler,
String kioskId, String kioskId) {
Localization localization) { super(streamingService, linkHandler, kioskId);
super(streamingService, linkHandler, kioskId, localization);
} }
@Nonnull @Nonnull
@ -53,7 +51,7 @@ public class MediaCCCConferenceKiosk extends KioskExtractor<ChannelInfoItem> {
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
String site = downloader.download(getLinkHandler().getUrl()); String site = downloader.get(getLinkHandler().getUrl(), getExtractorLocalization()).responseBody();
try { try {
doc = JsonParser.object().from(site); doc = JsonParser.object().from(site);
} catch (JsonParserException jpe) { } catch (JsonParserException jpe) {

View File

@ -0,0 +1,27 @@
package org.schabi.newpipe.extractor.services.media_ccc.extractors;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class MediaCCCParsingHelper {
private MediaCCCParsingHelper() {
}
public static Calendar parseDateFrom(String textualUploadDate) throws ParsingException {
Date date;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(textualUploadDate);
} catch (ParseException e) {
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
}
final Calendar uploadDate = Calendar.getInstance();
uploadDate.setTime(date);
return uploadDate;
}
}

View File

@ -4,11 +4,11 @@ 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.Downloader;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; 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.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.SearchQueryHandler; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
@ -16,26 +16,24 @@ import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCStreamInfoItemExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCStreamInfoItemExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferencesListLinkHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferencesListLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Localization;
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 static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.ALL;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory.*;
public class MediaCCCSearchExtractor extends SearchExtractor { public class MediaCCCSearchExtractor extends SearchExtractor {
private JsonObject doc; private JsonObject doc;
private MediaCCCConferenceKiosk conferenceKiosk; private MediaCCCConferenceKiosk conferenceKiosk;
public MediaCCCSearchExtractor(StreamingService service, SearchQueryHandler linkHandler, Localization localization) { public MediaCCCSearchExtractor(StreamingService service, SearchQueryHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
try { try {
conferenceKiosk = new MediaCCCConferenceKiosk(service, conferenceKiosk = new MediaCCCConferenceKiosk(service,
new MediaCCCConferencesListLinkHandlerFactory().fromId("conferences"), new MediaCCCConferencesListLinkHandlerFactory().fromId("conferences"),
"conferences", "conferences");
localization);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -88,7 +86,7 @@ public class MediaCCCSearchExtractor extends SearchExtractor {
|| getLinkHandler().getContentFilters().isEmpty()) { || getLinkHandler().getContentFilters().isEmpty()) {
final String site; final String site;
final String url = getUrl(); final String url = getUrl();
site = downloader.download(url, getLocalization()); site = downloader.get(url, getExtractorLocalization()).responseBody();
try { try {
doc = JsonParser.object().from(site); doc = JsonParser.object().from(site);
} catch (JsonParserException jpe) { } catch (JsonParserException jpe) {

View File

@ -4,15 +4,14 @@ 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.Downloader;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.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.*; import org.schabi.newpipe.extractor.stream.*;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.extractor.utils.Parser;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -24,16 +23,22 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
private JsonObject data; private JsonObject data;
private JsonObject conferenceData; private JsonObject conferenceData;
public MediaCCCStreamExtractor(StreamingService service, LinkHandler linkHandler, Localization localization) { public MediaCCCStreamExtractor(StreamingService service, LinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Nonnull @Nonnull
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
return data.getString("release_date"); return data.getString("release_date");
} }
@Nonnull
@Override
public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(MediaCCCParsingHelper.parseDateFrom(getTextualUploadDate()));
}
@Nonnull @Nonnull
@Override @Override
public String getThumbnailUrl() throws ParsingException { public String getThumbnailUrl() throws ParsingException {
@ -200,9 +205,9 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
try { try {
data = JsonParser.object().from( data = JsonParser.object().from(
downloader.download(getLinkHandler().getUrl())); downloader.get(getLinkHandler().getUrl()).responseBody());
conferenceData = JsonParser.object() conferenceData = JsonParser.object()
.from(downloader.download(getUploaderUrl())); .from(downloader.get(getUploaderUrl()).responseBody());
} catch (JsonParserException jpe) { } catch (JsonParserException jpe) {
throw new ExtractionException("Could not parse json returned by url: " + getLinkHandler().getUrl(), jpe); throw new ExtractionException("Could not parse json returned by url: " + getLinkHandler().getUrl(), jpe);
} }
@ -215,6 +220,7 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
return data.getString("title"); return data.getString("title");
} }
@Nonnull
@Override @Override
public String getOriginalUrl() throws ParsingException { public String getOriginalUrl() throws ParsingException {
return data.getString("frontend_link"); return data.getString("frontend_link");

View File

@ -1,8 +1,8 @@
package org.schabi.newpipe.extractor.services.media_ccc.extractors; package org.schabi.newpipe.extractor.services.media_ccc.extractors;
import org.schabi.newpipe.extractor.SuggestionExtractor; 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.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,8 +10,8 @@ import java.util.List;
public class MediaCCCSuggestionExtractor extends SuggestionExtractor { public class MediaCCCSuggestionExtractor extends SuggestionExtractor {
public MediaCCCSuggestionExtractor(int serviceId, Localization localization) { public MediaCCCSuggestionExtractor(StreamingService service) {
super(serviceId, localization); super(service);
} }
@Override @Override

View File

@ -2,9 +2,13 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nullable;
public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor { public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor {
JsonObject event; JsonObject event;
@ -44,11 +48,18 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
return event.getString("conference_url"); return event.getString("conference_url");
} }
@Nullable
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
return event.getString("release_date"); return event.getString("release_date");
} }
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(MediaCCCParsingHelper.parseDateFrom(getTextualUploadDate()));
}
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
return event.getString("title"); return event.getString("title");

View File

@ -4,15 +4,14 @@ 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.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
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.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -25,8 +24,8 @@ public class SoundcloudChannelExtractor extends ChannelExtractor {
private StreamInfoItemsCollector streamInfoItemsCollector = null; private StreamInfoItemsCollector streamInfoItemsCollector = null;
private String nextPageUrl = null; private String nextPageUrl = null;
public SoundcloudChannelExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public SoundcloudChannelExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Override @Override
@ -36,7 +35,7 @@ public class SoundcloudChannelExtractor extends ChannelExtractor {
String apiUrl = "https://api-v2.soundcloud.com/users/" + userId + String apiUrl = "https://api-v2.soundcloud.com/users/" + userId +
"?client_id=" + SoundcloudParsingHelper.clientId(); "?client_id=" + SoundcloudParsingHelper.clientId();
String response = downloader.download(apiUrl); String response = downloader.get(apiUrl, getExtractorLocalization()).responseBody();
try { try {
user = JsonParser.object().from(response); user = JsonParser.object().from(response);
} catch (JsonParserException e) { } catch (JsonParserException e) {

View File

@ -1,18 +1,15 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import org.schabi.newpipe.extractor.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
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.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
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.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> { public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
private StreamInfoItemsCollector collector = null; private StreamInfoItemsCollector collector = null;
@ -20,9 +17,8 @@ public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
public SoundcloudChartsExtractor(StreamingService service, public SoundcloudChartsExtractor(StreamingService service,
ListLinkHandler linkHandler, ListLinkHandler linkHandler,
String kioskId, String kioskId) {
Localization localization) { super(service, linkHandler, kioskId);
super(service, linkHandler, kioskId, localization);
} }
@Override @Override

View File

@ -7,9 +7,12 @@ 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.schabi.newpipe.extractor.Downloader; import org.jsoup.select.Elements;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector; import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
@ -21,57 +24,76 @@ import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.*;
import java.util.HashMap;
import static java.util.Collections.singletonList;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps; import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
public class SoundcloudParsingHelper { public class SoundcloudParsingHelper {
private static final String HARDCODED_CLIENT_ID = "LHzSAKe8eP9Yy3FgBugfBapRPLncO6Ng"; // Updated on 22/10/19
private static String clientId; private static String clientId;
private SoundcloudParsingHelper() { private SoundcloudParsingHelper() {
} }
public static String clientId() throws ReCaptchaException, IOException, RegexException { public static String clientId() throws ExtractionException, IOException {
if (clientId != null && !clientId.isEmpty()) return clientId; if (clientId != null && !clientId.isEmpty()) return clientId;
Downloader dl = NewPipe.getDownloader(); Downloader dl = NewPipe.getDownloader();
String response = dl.download("https://soundcloud.com"); clientId = HARDCODED_CLIENT_ID;
if (checkIfHardcodedClientIdIsValid(dl)) {
Document doc = Jsoup.parse(response); return clientId;
Element jsElement = doc.select("script[src^=https://a-v2.sndcdn.com/assets/app]").first(); }
final Response download = dl.get("https://soundcloud.com");
final String responseBody = download.responseBody();
final String clientIdPattern = ",client_id:\"(.*?)\""; final String clientIdPattern = ",client_id:\"(.*?)\"";
try { Document doc = Jsoup.parse(responseBody);
final HashMap<String, String> headers = new HashMap<>(); final Elements possibleScripts = doc.select("script[src*=\"sndcdn.com/assets/\"][src$=\".js\"]");
headers.put("Range", "bytes=0-16384"); // The one containing the client id will likely be the last one
String js = dl.download(jsElement.attr("src"), headers); Collections.reverse(possibleScripts);
return clientId = Parser.matchGroup1(clientIdPattern, js); final HashMap<String, List<String>> headers = new HashMap<>();
} catch (IOException | RegexException ignored) { headers.put("Range", singletonList("bytes=0-16384"));
// Ignore it and proceed to download the whole js file
for (Element element : possibleScripts) {
final String srcUrl = element.attr("src");
if (srcUrl != null && !srcUrl.isEmpty()) {
try {
return clientId = Parser.matchGroup1(clientIdPattern, dl.get(srcUrl, headers).responseBody());
} catch (RegexException ignored) {
// Ignore it and proceed to try searching other script
}
}
} }
String js = dl.download(jsElement.attr("src")); // Officially give up
return clientId = Parser.matchGroup1(clientIdPattern, js); throw new ExtractionException("Couldn't extract client id");
} }
public static String toDateString(String time) throws ParsingException { static boolean checkIfHardcodedClientIdIsValid(Downloader dl) throws IOException, ReCaptchaException {
try { final String apiUrl = "https://api.soundcloud.com/connect?client_id=" + HARDCODED_CLIENT_ID;
// Should return 200 to indicate that the client id is valid, a 401 is returned otherwise.
return dl.head(apiUrl).responseCode() == 200;
}
static Calendar parseDate(String textualUploadDate) throws ParsingException {
Date date; Date date;
// Have two date formats, one for the 'api.soundc...' and the other 'api-v2.soundc...'.
try { try {
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(time); date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(textualUploadDate);
} catch (Exception e) { } catch (ParseException e1) {
date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(time); try {
date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse(textualUploadDate);
} catch (ParseException e2) {
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"" + ", " + e1.getMessage(), e2);
}
} }
SimpleDateFormat newDateFormat = new SimpleDateFormat("yyyy-MM-dd"); final Calendar uploadDate = Calendar.getInstance();
return newDateFormat.format(date); uploadDate.setTime(date);
} catch (ParseException e) { return uploadDate;
throw new ParsingException(e.getMessage(), e);
}
} }
/** /**
@ -79,13 +101,14 @@ public class SoundcloudParsingHelper {
* *
* See https://developers.soundcloud.com/docs/api/reference#resolve * See https://developers.soundcloud.com/docs/api/reference#resolve
*/ */
public static JsonObject resolveFor(Downloader downloader, String url) throws IOException, ReCaptchaException, ParsingException { public static JsonObject resolveFor(Downloader downloader, String url) throws IOException, ExtractionException {
String apiUrl = "https://api.soundcloud.com/resolve" String apiUrl = "https://api.soundcloud.com/resolve"
+ "?url=" + URLEncoder.encode(url, "UTF-8") + "?url=" + URLEncoder.encode(url, "UTF-8")
+ "&client_id=" + clientId(); + "&client_id=" + clientId();
try { try {
return JsonParser.object().from(downloader.download(apiUrl)); final String response = downloader.get(apiUrl, SoundCloud.getLocalization()).responseBody();
return JsonParser.object().from(response);
} catch (JsonParserException e) { } catch (JsonParserException e) {
throw new ParsingException("Could not parse json response", e); throw new ParsingException("Could not parse json response", e);
} }
@ -98,8 +121,8 @@ public class SoundcloudParsingHelper {
*/ */
public static String resolveUrlWithEmbedPlayer(String apiUrl) throws IOException, ReCaptchaException, ParsingException { public static String resolveUrlWithEmbedPlayer(String apiUrl) throws IOException, ReCaptchaException, ParsingException {
String response = NewPipe.getDownloader().download("https://w.soundcloud.com/player/?url=" String response = NewPipe.getDownloader().get("https://w.soundcloud.com/player/?url="
+ URLEncoder.encode(apiUrl, "UTF-8")); + URLEncoder.encode(apiUrl, "UTF-8"), SoundCloud.getLocalization()).responseBody();
return Jsoup.parse(response).select("link[rel=\"canonical\"]").first().attr("abs:href"); return Jsoup.parse(response).select("link[rel=\"canonical\"]").first().attr("abs:href");
} }
@ -111,8 +134,8 @@ public class SoundcloudParsingHelper {
*/ */
public static String resolveIdWithEmbedPlayer(String url) throws IOException, ReCaptchaException, ParsingException { public static String resolveIdWithEmbedPlayer(String url) throws IOException, ReCaptchaException, ParsingException {
String response = NewPipe.getDownloader().download("https://w.soundcloud.com/player/?url=" String response = NewPipe.getDownloader().get("https://w.soundcloud.com/player/?url="
+ URLEncoder.encode(url, "UTF-8")); + URLEncoder.encode(url, "UTF-8"), SoundCloud.getLocalization()).responseBody();
// handle playlists / sets different and get playlist id via uir field in JSON // handle playlists / sets different and get playlist id via uir field in JSON
if (url.contains("sets") && !url.endsWith("sets") && !url.endsWith("sets/")) if (url.contains("sets") && !url.endsWith("sets") && !url.endsWith("sets/"))
return Parser.matchGroup1("\"uri\":\\s*\"https:\\/\\/api\\.soundcloud\\.com\\/playlists\\/((\\d)*?)\"", response); return Parser.matchGroup1("\"uri\":\\s*\"https:\\/\\/api\\.soundcloud\\.com\\/playlists\\/((\\d)*?)\"", response);
@ -143,7 +166,7 @@ public class SoundcloudParsingHelper {
* @return the next streams url, empty if don't have * @return the next streams url, empty if don't have
*/ */
public static String getUsersFromApi(ChannelInfoItemsCollector collector, String apiUrl) throws IOException, ReCaptchaException, ParsingException { public static String getUsersFromApi(ChannelInfoItemsCollector collector, String apiUrl) throws IOException, ReCaptchaException, ParsingException {
String response = NewPipe.getDownloader().download(apiUrl); String response = NewPipe.getDownloader().get(apiUrl, SoundCloud.getLocalization()).responseBody();
JsonObject responseObject; JsonObject responseObject;
try { try {
responseObject = JsonParser.object().from(response); responseObject = JsonParser.object().from(response);
@ -194,7 +217,7 @@ public class SoundcloudParsingHelper {
* @return the next streams url, empty if don't have * @return the next streams url, empty if don't have
*/ */
public static String getStreamsFromApi(StreamInfoItemsCollector collector, String apiUrl, boolean charts) throws IOException, ReCaptchaException, ParsingException { public static String getStreamsFromApi(StreamInfoItemsCollector collector, String apiUrl, boolean charts) throws IOException, ReCaptchaException, ParsingException {
String response = NewPipe.getDownloader().download(apiUrl); String response = NewPipe.getDownloader().get(apiUrl, SoundCloud.getLocalization()).responseBody();
JsonObject responseObject; JsonObject responseObject;
try { try {
responseObject = JsonParser.object().from(response); responseObject = JsonParser.object().from(response);

View File

@ -3,15 +3,14 @@ package org.schabi.newpipe.extractor.services.soundcloud;
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.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -24,8 +23,8 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
private StreamInfoItemsCollector streamInfoItemsCollector = null; private StreamInfoItemsCollector streamInfoItemsCollector = null;
private String nextPageUrl = null; private String nextPageUrl = null;
public SoundcloudPlaylistExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public SoundcloudPlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Override @Override
@ -36,7 +35,7 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
"?client_id=" + SoundcloudParsingHelper.clientId() + "?client_id=" + SoundcloudParsingHelper.clientId() +
"&representation=compact"; "&representation=compact";
String response = downloader.download(apiUrl); String response = downloader.get(apiUrl, getExtractorLocalization()).responseBody();
try { try {
playlist = JsonParser.object().from(response); playlist = JsonParser.object().from(response);
} catch (JsonParserException e) { } catch (JsonParserException e) {

View File

@ -5,12 +5,12 @@ 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.*; import org.schabi.newpipe.extractor.*;
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.SearchQueryHandler;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector; import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -25,10 +25,8 @@ public class SoundcloudSearchExtractor extends SearchExtractor {
private JsonArray searchCollection; private JsonArray searchCollection;
public SoundcloudSearchExtractor(StreamingService service, public SoundcloudSearchExtractor(StreamingService service, SearchQueryHandler linkHandler) {
SearchQueryHandler linkHandler, super(service, linkHandler);
Localization localization) {
super(service, linkHandler, localization);
} }
@Override @Override
@ -51,7 +49,8 @@ public class SoundcloudSearchExtractor extends SearchExtractor {
public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
final Downloader dl = getDownloader(); final Downloader dl = getDownloader();
try { try {
searchCollection = JsonParser.object().from(dl.download(pageUrl)).getArray("collection"); final String response = dl.get(pageUrl, getExtractorLocalization()).responseBody();
searchCollection = JsonParser.object().from(response).getArray("collection");
} catch (JsonParserException e) { } catch (JsonParserException e) {
throw new ParsingException("Could not parse json response", e); throw new ParsingException("Could not parse json response", e);
} }
@ -64,7 +63,8 @@ public class SoundcloudSearchExtractor extends SearchExtractor {
final Downloader dl = getDownloader(); final Downloader dl = getDownloader();
final String url = getUrl(); final String url = getUrl();
try { try {
searchCollection = JsonParser.object().from(dl.download(url)).getArray("collection"); final String response = dl.get(url, getExtractorLocalization()).responseBody();
searchCollection = JsonParser.object().from(response).getArray("collection");
} catch (JsonParserException e) { } catch (JsonParserException e) {
throw new ParsingException("Could not parse json response", e); throw new ParsingException("Could not parse json response", e);
} }

View File

@ -1,5 +1,6 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
@ -48,10 +49,10 @@ public class SoundcloudSearchQueryHandlerFactory extends SearchQueryHandlerFacto
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new ParsingException("Could not encode query", e); throw new ParsingException("Could not encode query", e);
} catch (IOException e) {
throw new ParsingException("Could not get client id", e);
} catch (ReCaptchaException e) { } catch (ReCaptchaException e) {
throw new ParsingException("ReCaptcha required", e); throw new ParsingException("ReCaptcha required", e);
} catch (IOException | ExtractionException e) {
throw new ParsingException("Could not get client id", e);
} }
} }

View File

@ -1,26 +1,19 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import static java.util.Collections.singletonList;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.SuggestionExtractor;
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.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.*;
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.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.utils.Localization;
import static java.util.Collections.singletonList;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
public class SoundcloudService extends StreamingService { public class SoundcloudService extends StreamingService {
@ -28,11 +21,6 @@ public class SoundcloudService extends StreamingService {
super(id, "SoundCloud", singletonList(AUDIO)); super(id, "SoundCloud", singletonList(AUDIO));
} }
@Override
public SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler, Localization localization) {
return new SoundcloudSearchExtractor(this, queryHandler, localization);
}
@Override @Override
public SearchQueryHandlerFactory getSearchQHFactory() { public SearchQueryHandlerFactory getSearchQHFactory() {
return new SoundcloudSearchQueryHandlerFactory(); return new SoundcloudSearchQueryHandlerFactory();
@ -55,23 +43,28 @@ public class SoundcloudService extends StreamingService {
@Override @Override
public StreamExtractor getStreamExtractor(LinkHandler LinkHandler, Localization localization) { public StreamExtractor getStreamExtractor(LinkHandler LinkHandler) {
return new SoundcloudStreamExtractor(this, LinkHandler, localization); return new SoundcloudStreamExtractor(this, LinkHandler);
} }
@Override @Override
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler, Localization localization) { public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) {
return new SoundcloudChannelExtractor(this, linkHandler, localization); return new SoundcloudChannelExtractor(this, linkHandler);
} }
@Override @Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler, Localization localization) { public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) {
return new SoundcloudPlaylistExtractor(this, linkHandler, localization); return new SoundcloudPlaylistExtractor(this, linkHandler);
} }
@Override @Override
public SuggestionExtractor getSuggestionExtractor(Localization localization) { public SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler) {
return new SoundcloudSuggestionExtractor(getServiceId(), localization); return new SoundcloudSearchExtractor(this, queryHandler);
}
@Override
public SoundcloudSuggestionExtractor getSuggestionExtractor() {
return new SoundcloudSuggestionExtractor(this);
} }
@Override @Override
@ -80,15 +73,14 @@ public class SoundcloudService extends StreamingService {
@Override @Override
public KioskExtractor createNewKiosk(StreamingService streamingService, public KioskExtractor createNewKiosk(StreamingService streamingService,
String url, String url,
String id, String id)
Localization local)
throws ExtractionException { throws ExtractionException {
return new SoundcloudChartsExtractor(SoundcloudService.this, return new SoundcloudChartsExtractor(SoundcloudService.this,
new SoundcloudChartsLinkHandlerFactory().fromUrl(url), id, local); new SoundcloudChartsLinkHandlerFactory().fromUrl(url), id);
} }
}; };
KioskList list = new KioskList(getServiceId()); KioskList list = new KioskList(this);
// add kiosks here e.g.: // add kiosks here e.g.:
final SoundcloudChartsLinkHandlerFactory h = new SoundcloudChartsLinkHandlerFactory(); final SoundcloudChartsLinkHandlerFactory h = new SoundcloudChartsLinkHandlerFactory();
@ -103,7 +95,6 @@ public class SoundcloudService extends StreamingService {
return list; return list;
} }
@Override @Override
public SubscriptionExtractor getSubscriptionExtractor() { public SubscriptionExtractor getSubscriptionExtractor() {
return new SoundcloudSubscriptionExtractor(this); return new SoundcloudSubscriptionExtractor(this);
@ -115,7 +106,7 @@ public class SoundcloudService extends StreamingService {
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler, Localization localization) public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler)
throws ExtractionException { throws ExtractionException {
return null; return null;
} }

View File

@ -4,12 +4,13 @@ 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.*; import org.schabi.newpipe.extractor.*;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
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.*; import org.schabi.newpipe.extractor.stream.*;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -22,8 +23,8 @@ import java.util.List;
public class SoundcloudStreamExtractor extends StreamExtractor { public class SoundcloudStreamExtractor extends StreamExtractor {
private JsonObject track; private JsonObject track;
public SoundcloudStreamExtractor(StreamingService service, LinkHandler linkHandler, Localization localization) { public SoundcloudStreamExtractor(StreamingService service, LinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Override @Override
@ -50,8 +51,14 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() {
return SoundcloudParsingHelper.toDateString(track.getString("created_at")); return track.getString("created_at");
}
@Nonnull
@Override
public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(getTextualUploadDate()));
} }
@Nonnull @Nonnull
@ -139,7 +146,7 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
String apiUrl = "https://api.soundcloud.com/i1/tracks/" + urlEncode(getId()) + "/streams" String apiUrl = "https://api.soundcloud.com/i1/tracks/" + urlEncode(getId()) + "/streams"
+ "?client_id=" + urlEncode(SoundcloudParsingHelper.clientId()); + "?client_id=" + urlEncode(SoundcloudParsingHelper.clientId());
String response = dl.download(apiUrl); String response = dl.get(apiUrl, getExtractorLocalization()).responseBody();
JsonObject responseObject; JsonObject responseObject;
try { try {
responseObject = JsonParser.object().from(response); responseObject = JsonParser.object().from(response);

View File

@ -2,6 +2,7 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
@ -41,8 +42,17 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
} }
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() {
return SoundcloudParsingHelper.toDateString(itemObject.getString("created_at")); return itemObject.getString("created_at");
}
@Override
public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(getTextualUploadDate()));
}
private String getCreatedAt() {
return itemObject.getString("created_at");
} }
@Override @Override

View File

@ -4,12 +4,12 @@ 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.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.SuggestionExtractor; 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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -20,8 +20,8 @@ public class SoundcloudSuggestionExtractor extends SuggestionExtractor {
public static final String CHARSET_UTF_8 = "UTF-8"; public static final String CHARSET_UTF_8 = "UTF-8";
public SoundcloudSuggestionExtractor(int serviceId, Localization localization) { public SoundcloudSuggestionExtractor(StreamingService service) {
super(serviceId, localization); super(service);
} }
@Override @Override
@ -35,7 +35,7 @@ public class SoundcloudSuggestionExtractor extends SuggestionExtractor {
+ "&client_id=" + SoundcloudParsingHelper.clientId() + "&client_id=" + SoundcloudParsingHelper.clientId()
+ "&limit=10"; + "&limit=10";
String response = dl.download(url); String response = dl.get(url, getExtractorLocalization()).responseBody();
try { try {
JsonArray collection = JsonParser.object().from(response).getArray("collection"); JsonArray collection = JsonParser.object().from(response).getArray("collection");
for (Object suggestion : collection) { for (Object suggestion : collection) {

View File

@ -1,43 +1,26 @@
package org.schabi.newpipe.extractor.services.youtube; package org.schabi.newpipe.extractor.services.youtube;
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.COMMENTS;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.LIVE;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.SuggestionExtractor;
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.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.*;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.localization.Localization;
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.youtube.extractors.YoutubeChannelExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.*;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeCommentsExtractor; import org.schabi.newpipe.extractor.services.youtube.linkHandler.*;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSearchExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSubscriptionExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSuggestionExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeTrendingExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeCommentsLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeTrendingLinkHandlerFactory;
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.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.util.List;
import static java.util.Arrays.asList;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.*;
/* /*
* Created by Christian Schabesberger on 23.08.15. * Created by Christian Schabesberger on 23.08.15.
@ -65,11 +48,6 @@ public class YoutubeService extends StreamingService {
super(id, "YouTube", asList(AUDIO, VIDEO, LIVE, COMMENTS)); super(id, "YouTube", asList(AUDIO, VIDEO, LIVE, COMMENTS));
} }
@Override
public SearchExtractor getSearchExtractor(SearchQueryHandler query, Localization localization) {
return new YoutubeSearchExtractor(this, query, localization);
}
@Override @Override
public LinkHandlerFactory getStreamLHFactory() { public LinkHandlerFactory getStreamLHFactory() {
return YoutubeStreamLinkHandlerFactory.getInstance(); return YoutubeStreamLinkHandlerFactory.getInstance();
@ -91,28 +69,33 @@ public class YoutubeService extends StreamingService {
} }
@Override @Override
public StreamExtractor getStreamExtractor(LinkHandler linkHandler, Localization localization) { public StreamExtractor getStreamExtractor(LinkHandler linkHandler) {
return new YoutubeStreamExtractor(this, linkHandler, localization); return new YoutubeStreamExtractor(this, linkHandler);
} }
@Override @Override
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler, Localization localization) { public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) {
return new YoutubeChannelExtractor(this, linkHandler, localization); return new YoutubeChannelExtractor(this, linkHandler);
} }
@Override @Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler, Localization localization) { public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) {
return new YoutubePlaylistExtractor(this, linkHandler, localization); return new YoutubePlaylistExtractor(this, linkHandler);
} }
@Override @Override
public SuggestionExtractor getSuggestionExtractor(Localization localization) { public SearchExtractor getSearchExtractor(SearchQueryHandler query) {
return new YoutubeSuggestionExtractor(getServiceId(), localization); return new YoutubeSearchExtractor(this, query);
}
@Override
public SuggestionExtractor getSuggestionExtractor() {
return new YoutubeSuggestionExtractor(this);
} }
@Override @Override
public KioskList getKioskList() throws ExtractionException { public KioskList getKioskList() throws ExtractionException {
KioskList list = new KioskList(getServiceId()); KioskList list = new KioskList(this);
// add kiosks here e.g.: // add kiosks here e.g.:
try { try {
@ -120,11 +103,10 @@ public class YoutubeService extends StreamingService {
@Override @Override
public KioskExtractor createNewKiosk(StreamingService streamingService, public KioskExtractor createNewKiosk(StreamingService streamingService,
String url, String url,
String id, String id)
Localization local)
throws ExtractionException { throws ExtractionException {
return new YoutubeTrendingExtractor(YoutubeService.this, return new YoutubeTrendingExtractor(YoutubeService.this,
new YoutubeTrendingLinkHandlerFactory().fromUrl(url), id, local); new YoutubeTrendingLinkHandlerFactory().fromUrl(url), id);
} }
}, new YoutubeTrendingLinkHandlerFactory(), "Trending"); }, new YoutubeTrendingLinkHandlerFactory(), "Trending");
list.setDefaultKiosk("Trending"); list.setDefaultKiosk("Trending");
@ -146,9 +128,53 @@ public class YoutubeService extends StreamingService {
} }
@Override @Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler urlIdHandler, Localization localization) public CommentsExtractor getCommentsExtractor(ListLinkHandler urlIdHandler)
throws ExtractionException { throws ExtractionException {
return new YoutubeCommentsExtractor(this, urlIdHandler, localization); return new YoutubeCommentsExtractor(this, urlIdHandler);
} }
/*//////////////////////////////////////////////////////////////////////////
// Localization
//////////////////////////////////////////////////////////////////////////*/
// https://www.youtube.com/picker_ajax?action_language_json=1
private static final List<Localization> SUPPORTED_LANGUAGES = Localization.listFrom(
"en-GB"
/*"af", "am", "ar", "az", "be", "bg", "bn", "bs", "ca", "cs", "da", "de",
"el", "en", "en-GB", "es", "es-419", "es-US", "et", "eu", "fa", "fi", "fil", "fr",
"fr-CA", "gl", "gu", "hi", "hr", "hu", "hy", "id", "is", "it", "iw", "ja",
"ka", "kk", "km", "kn", "ko", "ky", "lo", "lt", "lv", "mk", "ml", "mn",
"mr", "ms", "my", "ne", "nl", "no", "pa", "pl", "pt", "pt-PT", "ro", "ru",
"si", "sk", "sl", "sq", "sr", "sr-Latn", "sv", "sw", "ta", "te", "th", "tr",
"uk", "ur", "uz", "vi", "zh-CN", "zh-HK", "zh-TW", "zu"*/
);
// https://www.youtube.com/picker_ajax?action_country_json=1
private static final List<ContentCountry> SUPPORTED_COUNTRIES = ContentCountry.listFrom(
"AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", "BA",
"BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV",
"BW", "BY", "BZ", "CA", "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU",
"CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES",
"ET", "FI", "FJ", "FK", "FM", "FO", "FR", "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", "GM",
"GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID", "IE",
"IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM",
"KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY",
"MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT",
"MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU",
"NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", "QA",
"RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM",
"SN", "SO", "SR", "SS", "ST", "SV", "SX", "SY", "SZ", "TC", "TD", "TF", "TG", "TH", "TJ", "TK", "TL",
"TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE",
"VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW"
);
@Override
public List<Localization> getSupportedLocalizations() {
return SUPPORTED_LANGUAGES;
}
@Override
public List<ContentCountry> getSupportedCountries() {
return SUPPORTED_COUNTRIES;
}
} }

View File

@ -1,29 +1,27 @@
package org.schabi.newpipe.extractor.services.youtube.extractors; package org.schabi.newpipe.extractor.services.youtube.extractors;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.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.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.DonationLinkHelper;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
/* /*
* Created by Christian Schabesberger on 25.07.16. * Created by Christian Schabesberger on 25.07.16.
@ -49,19 +47,19 @@ import java.util.ArrayList;
public class YoutubeChannelExtractor extends ChannelExtractor { public class YoutubeChannelExtractor extends ChannelExtractor {
/*package-private*/ static final String CHANNEL_URL_BASE = "https://www.youtube.com/channel/"; /*package-private*/ static final String CHANNEL_URL_BASE = "https://www.youtube.com/channel/";
private static final String CHANNEL_FEED_BASE = "https://www.youtube.com/feeds/videos.xml?channel_id="; private static final String CHANNEL_FEED_BASE = "https://www.youtube.com/feeds/videos.xml?channel_id=";
private static final String CHANNEL_URL_PARAMETERS = "/videos?view=0&flow=list&sort=dd&live_view=10000&gl=US&hl=en"; private static final String CHANNEL_URL_PARAMETERS = "/videos?view=0&flow=list&sort=dd&live_view=10000";
private Document doc; private Document doc;
public YoutubeChannelExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public YoutubeChannelExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
String channelUrl = super.getUrl() + CHANNEL_URL_PARAMETERS; String channelUrl = super.getUrl() + CHANNEL_URL_PARAMETERS;
String pageContent = downloader.download(channelUrl); final Response response = downloader.get(channelUrl, getExtractorLocalization());
doc = Jsoup.parse(pageContent, channelUrl); doc = YoutubeParsingHelper.parseAndCheckPage(channelUrl, response);
} }
@Override @Override
@ -186,7 +184,8 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
JsonObject ajaxJson; JsonObject ajaxJson;
try { try {
ajaxJson = JsonParser.object().from(NewPipe.getDownloader().download(pageUrl)); final String response = getDownloader().get(pageUrl, getExtractorLocalization()).responseBody();
ajaxJson = JsonParser.object().from(response);
} catch (JsonParserException pe) { } catch (JsonParserException pe) {
throw new ParsingException("Could not parse json data for next streams", pe); throw new ParsingException("Could not parse json data for next streams", pe);
} }
@ -226,9 +225,11 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
final String uploaderName = getName(); final String uploaderName = getName();
final String uploaderUrl = getUrl(); final String uploaderUrl = getUrl();
final TimeAgoParser timeAgoParser = getTimeAgoParser();
for (final Element li : element.children()) { for (final Element li : element.children()) {
if (li.select("div[class=\"feed-item-dismissable\"]").first() != null) { if (li.select("div[class=\"feed-item-dismissable\"]").first() != null) {
collector.commit(new YoutubeStreamInfoItemExtractor(li) { collector.commit(new YoutubeStreamInfoItemExtractor(li, timeAgoParser) {
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
try { try {

View File

@ -1,36 +1,31 @@
package org.schabi.newpipe.extractor.services.youtube.extractors; package org.schabi.newpipe.extractor.services.youtube.extractors;
import java.io.IOException; import com.grack.nanojson.JsonArray;
import java.io.UnsupportedEncodingException; import com.grack.nanojson.JsonObject;
import java.net.URLEncoder; import com.grack.nanojson.JsonParser;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.schabi.newpipe.extractor.DownloadRequest;
import org.schabi.newpipe.extractor.DownloadResponse;
import org.schabi.newpipe.extractor.Downloader;
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.comments.CommentsExtractor; import org.schabi.newpipe.extractor.comments.CommentsExtractor;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemExtractor; import org.schabi.newpipe.extractor.comments.CommentsInfoItemExtractor;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemsCollector; import org.schabi.newpipe.extractor.comments.CommentsInfoItemsCollector;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.utils.JsonUtils; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import com.grack.nanojson.JsonArray; import javax.annotation.Nonnull;
import com.grack.nanojson.JsonObject; import java.io.IOException;
import com.grack.nanojson.JsonParser; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Pattern;
import static java.util.Collections.singletonList;
public class YoutubeCommentsExtractor extends CommentsExtractor { public class YoutubeCommentsExtractor extends CommentsExtractor {
@ -44,8 +39,8 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
private String title; private String title;
private InfoItemsPage<CommentsInfoItem> initPage; private InfoItemsPage<CommentsInfoItem> initPage;
public YoutubeCommentsExtractor(StreamingService service, ListLinkHandler uiHandler, Localization localization) { public YoutubeCommentsExtractor(StreamingService service, ListLinkHandler uiHandler) {
super(service, uiHandler, localization); super(service, uiHandler);
} }
@Override @Override
@ -130,7 +125,7 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
for(Object c: comments) { for(Object c: comments) {
if(c instanceof JsonObject) { if(c instanceof JsonObject) {
CommentsInfoItemExtractor extractor = new YoutubeCommentsInfoItemExtractor((JsonObject) c, getUrl()); CommentsInfoItemExtractor extractor = new YoutubeCommentsInfoItemExtractor((JsonObject) c, getUrl(), getTimeAgoParser());
collector.commit(extractor); collector.commit(extractor);
} }
} }
@ -147,12 +142,11 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
} }
@Override @Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
Map<String, List<String>> requestHeaders = new HashMap<>(); final Map<String, List<String>> requestHeaders = new HashMap<>();
requestHeaders.put("User-Agent", Arrays.asList(USER_AGENT)); requestHeaders.put("User-Agent", singletonList(USER_AGENT));
DownloadRequest request = new DownloadRequest(null, requestHeaders); final Response response = downloader.get(getUrl(), requestHeaders, getExtractorLocalization());
DownloadResponse response = downloader.get(getUrl(), request); String responseBody = response.responseBody();
String responseBody = response.getResponseBody();
ytClientVersion = findValue(responseBody, "INNERTUBE_CONTEXT_CLIENT_VERSION\":\"", "\""); ytClientVersion = findValue(responseBody, "INNERTUBE_CONTEXT_CLIENT_VERSION\":\"", "\"");
ytClientName = Parser.matchGroup1(YT_CLIENT_NAME_PATTERN, responseBody); ytClientName = Parser.matchGroup1(YT_CLIENT_NAME_PATTERN, responseBody);
String commentsTokenInside = findValue(responseBody, "commentSectionRenderer", "}"); String commentsTokenInside = findValue(responseBody, "commentSectionRenderer", "}");
@ -160,6 +154,7 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
initPage = getPage(getNextPageUrl(commentsToken)); initPage = getPage(getNextPageUrl(commentsToken));
} }
@Nonnull
@Override @Override
public String getName() throws ParsingException { public String getName() throws ParsingException {
return title; return title;
@ -168,13 +163,11 @@ public class YoutubeCommentsExtractor extends CommentsExtractor {
private String makeAjaxRequest(String siteUrl) throws IOException, ReCaptchaException { private String makeAjaxRequest(String siteUrl) throws IOException, ReCaptchaException {
Map<String, List<String>> requestHeaders = new HashMap<>(); Map<String, List<String>> requestHeaders = new HashMap<>();
requestHeaders.put("Accept", Arrays.asList("*/*")); requestHeaders.put("Accept", singletonList("*/*"));
requestHeaders.put("User-Agent", Arrays.asList(USER_AGENT)); requestHeaders.put("User-Agent", singletonList(USER_AGENT));
requestHeaders.put("X-YouTube-Client-Version", Arrays.asList(ytClientVersion)); requestHeaders.put("X-YouTube-Client-Version", singletonList(ytClientVersion));
requestHeaders.put("X-YouTube-Client-Name", Arrays.asList(ytClientName)); requestHeaders.put("X-YouTube-Client-Name", singletonList(ytClientName));
DownloadRequest request = new DownloadRequest(null, requestHeaders); return getDownloader().get(siteUrl, requestHeaders, getExtractorLocalization()).responseBody();
return NewPipe.getDownloader().get(siteUrl, request).getResponseBody();
} }
private String getDataString(Map<String, String> params) throws UnsupportedEncodingException { private String getDataString(Map<String, String> params) throws UnsupportedEncodingException {

View File

@ -1,21 +1,26 @@
package org.schabi.newpipe.extractor.services.youtube.extractors; package org.schabi.newpipe.extractor.services.youtube.extractors;
import org.schabi.newpipe.extractor.comments.CommentsInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Utils;
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.comments.CommentsInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nullable;
public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtractor { public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
private final JsonObject json; private final JsonObject json;
private final String url; private final String url;
private final TimeAgoParser timeAgoParser;
public YoutubeCommentsInfoItemExtractor(JsonObject json, String url) { public YoutubeCommentsInfoItemExtractor(JsonObject json, String url, TimeAgoParser timeAgoParser) {
this.json = json; this.json = json;
this.url = url; this.url = url;
this.timeAgoParser = timeAgoParser;
} }
@Override @Override
@ -43,7 +48,7 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
} }
@Override @Override
public String getPublishedTime() throws ParsingException { public String getTextualPublishedTime() throws ParsingException {
try { try {
return YoutubeCommentsExtractor.getYoutubeText(JsonUtils.getObject(json, "publishedTimeText")); return YoutubeCommentsExtractor.getYoutubeText(JsonUtils.getObject(json, "publishedTimeText"));
} catch (Exception e) { } catch (Exception e) {
@ -51,8 +56,19 @@ public class YoutubeCommentsInfoItemExtractor implements CommentsInfoItemExtract
} }
} }
@Nullable
@Override @Override
public Integer getLikeCount() throws ParsingException { public DateWrapper getPublishedTime() throws ParsingException {
String textualPublishedTime = getTextualPublishedTime();
if (timeAgoParser != null && textualPublishedTime != null && !textualPublishedTime.isEmpty()) {
return timeAgoParser.parse(textualPublishedTime);
} else {
return null;
}
}
@Override
public int getLikeCount() throws ParsingException {
try { try {
return JsonUtils.getNumber(json, "likeCount").intValue(); return JsonUtils.getNumber(json, "likeCount").intValue();
} catch (Exception e) { } catch (Exception e) {

View File

@ -6,18 +6,19 @@ 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.schabi.newpipe.extractor.Downloader;
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.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -29,14 +30,15 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
private Document doc; private Document doc;
public YoutubePlaylistExtractor(StreamingService service, ListLinkHandler linkHandler, Localization localization) { public YoutubePlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
String pageContent = downloader.download(getUrl()); final String url = getUrl();
doc = Jsoup.parse(pageContent, getUrl()); final Response response = downloader.get(url, getExtractorLocalization());
doc = YoutubeParsingHelper.parseAndCheckPage(url, response);
} }
@Override @Override
@ -139,7 +141,8 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
JsonObject pageJson; JsonObject pageJson;
try { try {
pageJson = JsonParser.object().from(getDownloader().download(pageUrl)); final String responseBody = getDownloader().get(pageUrl, getExtractorLocalization()).responseBody();
pageJson = JsonParser.object().from(responseBody);
} catch (JsonParserException pe) { } catch (JsonParserException pe) {
throw new ParsingException("Could not parse ajax json", pe); throw new ParsingException("Could not parse ajax json", pe);
} }
@ -185,12 +188,14 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
} }
final LinkHandlerFactory streamLinkHandlerFactory = getService().getStreamLHFactory(); final LinkHandlerFactory streamLinkHandlerFactory = getService().getStreamLHFactory();
final TimeAgoParser timeAgoParser = getTimeAgoParser();
for (final Element li : element.children()) { for (final Element li : element.children()) {
if(isDeletedItem(li)) { if(isDeletedItem(li)) {
continue; continue;
} }
collector.commit(new YoutubeStreamInfoItemExtractor(li) { collector.commit(new YoutubeStreamInfoItemExtractor(li, timeAgoParser) {
public Element uploaderLink; public Element uploaderLink;
@Override @Override
@ -256,7 +261,7 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor {
} }
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
return ""; return "";
} }

View File

@ -3,15 +3,17 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
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.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector; import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -44,26 +46,21 @@ public class YoutubeSearchExtractor extends SearchExtractor {
private Document doc; private Document doc;
public YoutubeSearchExtractor(StreamingService service, public YoutubeSearchExtractor(StreamingService service, SearchQueryHandler linkHandler) {
SearchQueryHandler linkHandler, super(service, linkHandler);
Localization localization) {
super(service, linkHandler, localization);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
final String site;
final String url = getUrl(); final String url = getUrl();
//String url = builder.build().toString(); final Response response = downloader.get(url, getExtractorLocalization());
//if we've been passed a valid language code, append it to the URL doc = YoutubeParsingHelper.parseAndCheckPage(url, response);
site = downloader.download(url, getLocalization());
doc = Jsoup.parse(site, url);
} }
@Nonnull
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
return super.getUrl() + "&gl="+ getLocalization().getCountry(); return super.getUrl() + "&gl=" + getExtractorContentCountry().getCountryCode();
} }
@Override @Override
@ -89,8 +86,8 @@ public class YoutubeSearchExtractor extends SearchExtractor {
@Override @Override
public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException { public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
String site = getDownloader().download(pageUrl); final String response = getDownloader().get(pageUrl, getExtractorLocalization()).responseBody();
doc = Jsoup.parse(site, pageUrl); doc = Jsoup.parse(response, pageUrl);
return new InfoItemsPage<>(collectItems(doc), getNextPageUrlFromCurrentUrl(pageUrl)); return new InfoItemsPage<>(collectItems(doc), getNextPageUrlFromCurrentUrl(pageUrl));
} }
@ -111,6 +108,7 @@ public class YoutubeSearchExtractor extends SearchExtractor {
InfoItemsSearchCollector collector = getInfoItemSearchCollector(); InfoItemsSearchCollector collector = getInfoItemSearchCollector();
Element list = doc.select("ol[class=\"item-section\"]").first(); Element list = doc.select("ol[class=\"item-section\"]").first();
final TimeAgoParser timeAgoParser = getTimeAgoParser();
for (Element item : list.children()) { for (Element item : list.children()) {
/* First we need to determine which kind of item we are working with. /* First we need to determine which kind of item we are working with.
@ -131,7 +129,7 @@ public class YoutubeSearchExtractor extends SearchExtractor {
// video item type // video item type
} else if ((el = item.select("div[class*=\"yt-lockup-video\"]").first()) != null) { } else if ((el = item.select("div[class*=\"yt-lockup-video\"]").first()) != null) {
collector.commit(new YoutubeStreamInfoItemExtractor(el)); collector.commit(new YoutubeStreamInfoItemExtractor(el, timeAgoParser));
} else if ((el = item.select("div[class*=\"yt-lockup-channel\"]").first()) != null) { } else if ((el = item.select("div[class*=\"yt-lockup-channel\"]").first()) != null) {
collector.commit(new YoutubeChannelInfoItemExtractor(el)); collector.commit(new YoutubeChannelInfoItemExtractor(el));
} else if ((el = item.select("div[class*=\"yt-lockup-playlist\"]").first()) != null && } else if ((el = item.select("div[class*=\"yt-lockup-playlist\"]").first()) != null &&

View File

@ -11,15 +11,23 @@ import org.jsoup.select.Elements;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function; import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
import org.schabi.newpipe.extractor.*; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Request;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.youtube.ItagItem; import org.schabi.newpipe.extractor.services.youtube.ItagItem;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.extractor.stream.*;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
@ -86,8 +94,8 @@ public class YoutubeStreamExtractor extends StreamExtractor {
private boolean isAgeRestricted; private boolean isAgeRestricted;
public YoutubeStreamExtractor(StreamingService service, LinkHandler linkHandler, Localization localization) { public YoutubeStreamExtractor(StreamingService service, LinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -113,10 +121,12 @@ public class YoutubeStreamExtractor extends StreamExtractor {
return name; return name;
} }
@Nonnull
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
assertPageFetched(); if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return null;
}
try { try {
return doc.select("meta[itemprop=datePublished]").attr(CONTENT); return doc.select("meta[itemprop=datePublished]").attr(CONTENT);
} catch (Exception e) {//todo: add fallback method } catch (Exception e) {//todo: add fallback method
@ -124,6 +134,17 @@ public class YoutubeStreamExtractor extends StreamExtractor {
} }
} }
@Override
public DateWrapper getUploadDate() throws ParsingException {
final String textualUploadDate = getTextualUploadDate();
if (textualUploadDate == null) {
return null;
}
return new DateWrapper(YoutubeParsingHelper.parseDateFrom(textualUploadDate));
}
@Nonnull @Nonnull
@Override @Override
public String getThumbnailUrl() throws ParsingException { public String getThumbnailUrl() throws ParsingException {
@ -284,12 +305,65 @@ public class YoutubeStreamExtractor extends StreamExtractor {
public long getViewCount() throws ParsingException { public long getViewCount() throws ParsingException {
assertPageFetched(); assertPageFetched();
try { try {
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return getLiveStreamWatchingCount();
}
return Long.parseLong(doc.select("meta[itemprop=interactionCount]").attr(CONTENT)); return Long.parseLong(doc.select("meta[itemprop=interactionCount]").attr(CONTENT));
} catch (Exception e) {//todo: find fallback method } catch (Exception e) {//todo: find fallback method
throw new ParsingException("Could not get number of views", e); throw new ParsingException("Could not get number of views", e);
} }
} }
private long getLiveStreamWatchingCount() throws ExtractionException, IOException, JsonParserException {
// https://www.youtube.com/youtubei/v1/updated_metadata?alt=json&key=
String innerTubeKey = null, clientVersion = null;
if (playerArgs != null && !playerArgs.isEmpty()) {
innerTubeKey = playerArgs.getString("innertube_api_key");
clientVersion = playerArgs.getString("innertube_context_client_version");
} else if (!videoInfoPage.isEmpty()) {
innerTubeKey = videoInfoPage.get("innertube_api_key");
clientVersion = videoInfoPage.get("innertube_context_client_version");
}
if (innerTubeKey == null || innerTubeKey.isEmpty()) {
throw new ExtractionException("Couldn't get innerTube key");
}
if (clientVersion == null || clientVersion.isEmpty()) {
throw new ExtractionException("Couldn't get innerTube client version");
}
final String metadataUrl = "https://www.youtube.com/youtubei/v1/updated_metadata?alt=json&key=" + innerTubeKey;
final byte[] dataBody = ("{\"context\":{\"client\":{\"clientName\":1,\"clientVersion\":\"" + clientVersion + "\"}}" +
",\"videoId\":\"" + getId() + "\"}").getBytes("UTF-8");
final Response response = getDownloader().execute(Request.newBuilder()
.post(metadataUrl, dataBody)
.addHeader("Content-Type", "application/json")
.build());
final JsonObject jsonObject = JsonParser.object().from(response.responseBody());
for (Object actionEntry : jsonObject.getArray("actions")) {
if (!(actionEntry instanceof JsonObject)) continue;
final JsonObject entry = (JsonObject) actionEntry;
final JsonObject updateViewershipAction = entry.getObject("updateViewershipAction", null);
if (updateViewershipAction == null) continue;
final JsonArray viewCountRuns = JsonUtils.getArray(updateViewershipAction, "viewership.videoViewCountRenderer.viewCount.runs");
if (viewCountRuns.isEmpty()) continue;
final JsonObject textObject = viewCountRuns.getObject(0);
if (!textObject.has("text")) {
throw new ExtractionException("Response don't have \"text\" element");
}
return Long.parseLong(Utils.removeNonDigitCharacters(textObject.getString("text")));
}
throw new ExtractionException("Could not find correct results in response");
}
@Override @Override
public long getLikeCount() throws ParsingException { public long getLikeCount() throws ParsingException {
assertPageFetched(); assertPageFetched();
@ -531,13 +605,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
assertPageFetched(); assertPageFetched();
try { try {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
final TimeAgoParser timeAgoParser = getTimeAgoParser();
Elements watch = doc.select("div[class=\"watch-sidebar-section\"]"); Elements watch = doc.select("div[class=\"watch-sidebar-section\"]");
if (watch.size() < 1) { if (watch.size() < 1) {
return null;// prevent the snackbar notification "report error" on age-restricted videos return null;// prevent the snackbar notification "report error" on age-restricted videos
} }
collector.commit(extractVideoPreviewInfo(watch.first().select("li").first())); collector.commit(extractVideoPreviewInfo(watch.first().select("li").first(), timeAgoParser));
return collector.getItems().get(0); return collector.getItems().get(0);
} catch (Exception e) { } catch (Exception e) {
throw new ParsingException("Could not get next video", e); throw new ParsingException("Could not get next video", e);
@ -549,12 +624,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
assertPageFetched(); assertPageFetched();
try { try {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
final TimeAgoParser timeAgoParser = getTimeAgoParser();
Element ul = doc.select("ul[id=\"watch-related\"]").first(); Element ul = doc.select("ul[id=\"watch-related\"]").first();
if (ul != null) { if (ul != null) {
for (Element li : ul.children()) { for (Element li : ul.children()) {
// first check if we have a playlist. If so leave them out // first check if we have a playlist. If so leave them out
if (li.select("a[class*=\"content-link\"]").first() != null) { if (li.select("a[class*=\"content-link\"]").first() != null) {
collector.commit(extractVideoPreviewInfo(li)); collector.commit(extractVideoPreviewInfo(li, timeAgoParser));
} }
} }
} }
@ -611,31 +688,24 @@ public class YoutubeStreamExtractor extends StreamExtractor {
private String pageHtml = null; private String pageHtml = null;
private String getPageHtml(Downloader downloader) throws IOException, ExtractionException {
final String verifiedUrl = getUrl() + VERIFIED_URL_PARAMS;
if (pageHtml == null) {
pageHtml = downloader.download(verifiedUrl);
}
return pageHtml;
}
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
final String pageContent = getPageHtml(downloader); final String verifiedUrl = getUrl() + VERIFIED_URL_PARAMS;
doc = Jsoup.parse(pageContent, getUrl()); final Response response = downloader.get(verifiedUrl, getExtractorLocalization());
pageHtml = response.responseBody();
doc = YoutubeParsingHelper.parseAndCheckPage(verifiedUrl, response);
final String playerUrl; final String playerUrl;
// Check if the video is age restricted // Check if the video is age restricted
if (pageContent.contains("<meta property=\"og:restrictions:age")) { if (!doc.select("meta[property=\"og:restrictions:age\"").isEmpty()) {
// do this if it is age gated
final EmbeddedInfo info = getEmbeddedInfo(); final EmbeddedInfo info = getEmbeddedInfo();
final String videoInfoUrl = getVideoInfoUrl(getId(), info.sts); final String videoInfoUrl = getVideoInfoUrl(getId(), info.sts);
final String infoPageResponse = downloader.download(videoInfoUrl); final String infoPageResponse = downloader.get(videoInfoUrl, getExtractorLocalization()).responseBody();
videoInfoPage.putAll(Parser.compatParseMap(infoPageResponse)); videoInfoPage.putAll(Parser.compatParseMap(infoPageResponse));
playerUrl = info.url; playerUrl = info.url;
isAgeRestricted = true; isAgeRestricted = true;
} else { } else {
final JsonObject ytPlayerConfig = getPlayerConfig(pageContent); final JsonObject ytPlayerConfig = getPlayerConfig();
playerArgs = getPlayerArgs(ytPlayerConfig); playerArgs = getPlayerArgs(ytPlayerConfig);
playerUrl = getPlayerUrl(ytPlayerConfig); playerUrl = getPlayerUrl(ytPlayerConfig);
isAgeRestricted = false; isAgeRestricted = false;
@ -651,9 +721,9 @@ public class YoutubeStreamExtractor extends StreamExtractor {
} }
} }
private JsonObject getPlayerConfig(String pageContent) throws ParsingException { private JsonObject getPlayerConfig() throws ParsingException {
try { try {
String ytPlayerConfigRaw = Parser.matchGroup1("ytplayer.config\\s*=\\s*(\\{.*?\\});", pageContent); String ytPlayerConfigRaw = Parser.matchGroup1("ytplayer.config\\s*=\\s*(\\{.*?\\});", pageHtml);
return JsonParser.object().from(ytPlayerConfigRaw); return JsonParser.object().from(ytPlayerConfigRaw);
} catch (Parser.RegexException e) { } catch (Parser.RegexException e) {
String errorReason = getErrorMessage(); String errorReason = getErrorMessage();
@ -719,7 +789,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
try { try {
final Downloader downloader = NewPipe.getDownloader(); final Downloader downloader = NewPipe.getDownloader();
final String embedUrl = "https://www.youtube.com/embed/" + getId(); final String embedUrl = "https://www.youtube.com/embed/" + getId();
final String embedPageContent = downloader.download(embedUrl); final String embedPageContent = downloader.get(embedUrl, getExtractorLocalization()).responseBody();
// Get player url // Get player url
final String assetsPattern = "\"assets\":.+?\"js\":\\s*(\"[^\"]+\")"; final String assetsPattern = "\"assets\":.+?\"js\":\\s*(\"[^\"]+\")";
@ -754,7 +824,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
playerUrl = "https://youtube.com" + playerUrl; playerUrl = "https://youtube.com" + playerUrl;
} }
final String playerCode = downloader.download(playerUrl); final String playerCode = downloader.get(playerUrl, getExtractorLocalization()).responseBody();
final String decryptionFunctionName = getDecryptionFuncName(playerCode); final String decryptionFunctionName = getDecryptionFuncName(playerCode);
final String functionPattern = "(" final String functionPattern = "("
@ -823,13 +893,6 @@ public class YoutubeStreamExtractor extends StreamExtractor {
// If the video is age restricted getPlayerConfig will fail // If the video is age restricted getPlayerConfig will fail
if(isAgeRestricted) return Collections.emptyList(); if(isAgeRestricted) return Collections.emptyList();
final JsonObject playerConfig;
try {
playerConfig = getPlayerConfig(getPageHtml(NewPipe.getDownloader()));
} catch (IOException | ExtractionException e) {
throw new SubtitlesException("Unable to download player configs", e);
}
final JsonObject captions; final JsonObject captions;
if (!playerResponse.has("captions")) { if (!playerResponse.has("captions")) {
// Captions does not exist // Captions does not exist
@ -944,8 +1007,8 @@ public class YoutubeStreamExtractor extends StreamExtractor {
* Provides information about links to other videos on the video page, such as related videos. * Provides information about links to other videos on the video page, such as related videos.
* This is encapsulated in a StreamInfoItem object, which is a subset of the fields in a full StreamInfo. * This is encapsulated in a StreamInfoItem object, which is a subset of the fields in a full StreamInfo.
*/ */
private StreamInfoItemExtractor extractVideoPreviewInfo(final Element li) { private StreamInfoItemExtractor extractVideoPreviewInfo(final Element li, final TimeAgoParser timeAgoParser) {
return new YoutubeStreamInfoItemExtractor(li) { return new YoutubeStreamInfoItemExtractor(li, timeAgoParser) {
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
@ -972,23 +1035,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
} }
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
return ""; return "";
} }
@Override
public long getViewCount() throws ParsingException {
try {
if (getStreamType() == StreamType.LIVE_STREAM) return -1;
return Long.parseLong(Utils.removeNonDigitCharacters(
li.select("span.view-count").first().text()));
} catch (Exception e) {
//related videos sometimes have no view count
return 0;
}
}
@Override @Override
public String getThumbnailUrl() throws ParsingException { public String getThumbnailUrl() throws ParsingException {
Element img = li.select("img").first(); Element img = li.select("img").first();

View File

@ -1,12 +1,20 @@
package org.schabi.newpipe.extractor.services.youtube.extractors; package org.schabi.newpipe.extractor.services.youtube.extractors;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nullable;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/* /*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* YoutubeStreamInfoItemExtractor.java is part of NewPipe. * YoutubeStreamInfoItemExtractor.java is part of NewPipe.
@ -28,9 +36,18 @@ import org.schabi.newpipe.extractor.utils.Utils;
public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor { public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
private final Element item; private final Element item;
private final TimeAgoParser timeAgoParser;
public YoutubeStreamInfoItemExtractor(Element item) { private String cachedUploadDate;
/**
* Creates an extractor of StreamInfoItems from a YouTube page.
* @param item The page element
* @param timeAgoParser A parser of the textual dates or {@code null}.
*/
public YoutubeStreamInfoItemExtractor(Element item, @Nullable TimeAgoParser timeAgoParser) {
this.item = item; this.item = item;
this.timeAgoParser = timeAgoParser;
} }
@Override @Override
@ -125,42 +142,94 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
} }
} }
@Nullable
@Override @Override
public String getUploadDate() throws ParsingException { public String getTextualUploadDate() throws ParsingException {
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return null;
}
if (cachedUploadDate != null) {
return cachedUploadDate;
}
try { try {
if (isVideoReminder()) {
final Calendar calendar = getDateFromReminder();
if (calendar != null) {
return cachedUploadDate = new SimpleDateFormat("yyyy-MM-dd HH:mm")
.format(calendar.getTime());
}
}
Element meta = item.select("div[class=\"yt-lockup-meta\"]").first(); Element meta = item.select("div[class=\"yt-lockup-meta\"]").first();
if (meta == null) return ""; if (meta == null) return "";
Element li = meta.select("li").first(); final Elements li = meta.select("li");
if(li == null) return ""; if (li.isEmpty()) return "";
return meta.select("li").first().text(); return cachedUploadDate = li.first().text();
} catch (Exception e) { } catch (Exception e) {
throw new ParsingException("Could not get upload date", e); throw new ParsingException("Could not get upload date", e);
} }
} }
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
return null;
}
if (isVideoReminder()) {
return new DateWrapper(getDateFromReminder());
}
String textualUploadDate = getTextualUploadDate();
if (timeAgoParser != null && textualUploadDate != null && !textualUploadDate.isEmpty()) {
return timeAgoParser.parse(textualUploadDate);
} else {
return null;
}
}
@Override @Override
public long getViewCount() throws ParsingException { public long getViewCount() throws ParsingException {
String input; String input;
try {
// TODO: Return the actual live stream's watcher count
// -1 for no view count
if (getStreamType() == StreamType.LIVE_STREAM) return -1;
Element meta = item.select("div[class=\"yt-lockup-meta\"]").first(); final Element spanViewCount = item.select("span.view-count").first();
if (spanViewCount != null) {
input = spanViewCount.text();
} else if (getStreamType().equals(StreamType.LIVE_STREAM)) {
Element meta = item.select("ul.yt-lockup-meta-info").first();
if (meta == null) return 0;
final Elements li = meta.select("li");
if (li.isEmpty()) return 0;
input = li.first().text();
} else {
try {
Element meta = item.select("div.yt-lockup-meta").first();
if (meta == null) return -1; if (meta == null) return -1;
// This case can happen if google releases a special video // This case can happen if google releases a special video
if (meta.select("li").size() < 2) return -1; if (meta.select("li").size() < 2) return -1;
input = meta.select("li").get(1).text(); input = meta.select("li").get(1).text();
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
throw new ParsingException("Could not parse yt-lockup-meta although available: " + getUrl(), e); throw new ParsingException("Could not parse yt-lockup-meta although available: " + getUrl(), e);
} }
}
if (input == null) {
throw new ParsingException("Input is null");
}
try { try {
return Long.parseLong(Utils.removeNonDigitCharacters(input)); return Long.parseLong(Utils.removeNonDigitCharacters(input));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// if this happens the video probably has no views // if this happens the video probably has no views
@ -191,6 +260,32 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
} }
} }
private boolean isVideoReminder() {
return !item.select("span.yt-uix-livereminder").isEmpty();
}
private Calendar getDateFromReminder() throws ParsingException {
final Element timeFuture = item.select("span.yt-badge.localized-date").first();
if (timeFuture == null) {
throw new ParsingException("Span timeFuture is null");
}
final String timestamp = timeFuture.attr("data-timestamp");
if (!timestamp.isEmpty()) {
try {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(Long.parseLong(timestamp) * 1000L));
return calendar;
} catch (Exception e) {
throw new ParsingException("Could not parse = \"" + timestamp + "\"");
}
}
throw new ParsingException("Could not parse date from reminder element: \"" + timeFuture + "\"");
}
/** /**
* Generic method that checks if the element contains any clues that it's a livestream item * Generic method that checks if the element contains any clues that it's a livestream item
*/ */

View File

@ -3,12 +3,12 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
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.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.SuggestionExtractor; 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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -39,8 +39,8 @@ public class YoutubeSuggestionExtractor extends SuggestionExtractor {
public static final String CHARSET_UTF_8 = "UTF-8"; public static final String CHARSET_UTF_8 = "UTF-8";
public YoutubeSuggestionExtractor(int serviceId, Localization localization) { public YoutubeSuggestionExtractor(StreamingService service) {
super(serviceId, localization); super(service);
} }
@Override @Override
@ -52,10 +52,10 @@ public class YoutubeSuggestionExtractor extends SuggestionExtractor {
+ "?client=" + "youtube" //"firefox" for JSON, 'toolbar' for xml + "?client=" + "youtube" //"firefox" for JSON, 'toolbar' for xml
+ "&jsonp=" + "JP" + "&jsonp=" + "JP"
+ "&ds=" + "yt" + "&ds=" + "yt"
+ "&hl=" + URLEncoder.encode(getLocalization().getCountry(), CHARSET_UTF_8) + "&gl=" + URLEncoder.encode(getExtractorContentCountry().getCountryCode(), CHARSET_UTF_8)
+ "&q=" + URLEncoder.encode(query, CHARSET_UTF_8); + "&q=" + URLEncoder.encode(query, CHARSET_UTF_8);
String response = dl.download(url); String response = dl.get(url, getExtractorLocalization()).responseBody();
// trim JSONP part "JP(...)" // trim JSONP part "JP(...)"
response = response.substring(3, response.length()-1); response = response.substring(3, response.length()-1);
try { try {

View File

@ -20,19 +20,20 @@ package org.schabi.newpipe.extractor.services.youtube.extractors;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/ */
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.Downloader;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
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.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Localization;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
@ -43,21 +44,17 @@ public class YoutubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
public YoutubeTrendingExtractor(StreamingService service, public YoutubeTrendingExtractor(StreamingService service,
ListLinkHandler linkHandler, ListLinkHandler linkHandler,
String kioskId, String kioskId) {
Localization localization) { super(service, linkHandler, kioskId);
super(service, linkHandler, kioskId, localization);
} }
@Override @Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
final String contentCountry = getLocalization().getCountry(); final String url = getUrl() +
String url = getUrl(); "?gl=" + getExtractorContentCountry().getCountryCode();
if(contentCountry != null && !contentCountry.isEmpty()) {
url += "?gl=" + contentCountry;
}
String pageContent = downloader.download(url); final Response response = downloader.get(url, getExtractorLocalization());
doc = Jsoup.parse(pageContent, url); doc = YoutubeParsingHelper.parseAndCheckPage(url, response);
} }
@Override @Override
@ -88,10 +85,13 @@ public class YoutubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ParsingException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws ParsingException {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
Elements uls = doc.select("ul[class*=\"expanded-shelf-content-list\"]"); Elements uls = doc.select("ul[class*=\"expanded-shelf-content-list\"]");
final TimeAgoParser timeAgoParser = getTimeAgoParser();
for(Element ul : uls) { for(Element ul : uls) {
for(final Element li : ul.children()) { for(final Element li : ul.children()) {
final Element el = li.select("div[class*=\"yt-lockup-dismissable\"]").first(); final Element el = li.select("div[class*=\"yt-lockup-dismissable\"]").first();
collector.commit(new YoutubeStreamInfoItemExtractor(li) { collector.commit(new YoutubeStreamInfoItemExtractor(li, timeAgoParser) {
@Override @Override
public String getUrl() throws ParsingException { public String getUrl() throws ParsingException {
try { try {

View File

@ -1,9 +1,17 @@
package org.schabi.newpipe.extractor.services.youtube.linkHandler; package org.schabi.newpipe.extractor.services.youtube.linkHandler;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
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 java.net.URL; import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/* /*
* Created by Christian Schabesberger on 02.03.16. * Created by Christian Schabesberger on 02.03.16.
@ -30,6 +38,23 @@ public class YoutubeParsingHelper {
private YoutubeParsingHelper() { private YoutubeParsingHelper() {
} }
private static final String[] RECAPTCHA_DETECTION_SELECTORS = {
"form[action*=\"/das_captcha\"]",
"input[name*=\"action_recaptcha_verify\"]"
};
public static Document parseAndCheckPage(final String url, final Response response) throws ReCaptchaException {
final Document document = Jsoup.parse(response.responseBody(), url);
for (String detectionSelector : RECAPTCHA_DETECTION_SELECTORS) {
if (!document.select(detectionSelector).isEmpty()) {
throw new ReCaptchaException("reCAPTCHA challenge requested (detected with selector: \"" + detectionSelector + "\")", url);
}
}
return document;
}
public static boolean isYoutubeURL(URL url) { public static boolean isYoutubeURL(URL url) {
String host = url.getHost(); String host = url.getHost();
return host.equalsIgnoreCase("youtube.com") || host.equalsIgnoreCase("www.youtube.com") return host.equalsIgnoreCase("youtube.com") || host.equalsIgnoreCase("www.youtube.com")
@ -92,4 +117,17 @@ public class YoutubeParsingHelper {
+ Long.parseLong(minutes)) * 60) + Long.parseLong(minutes)) * 60)
+ Long.parseLong(seconds); + Long.parseLong(seconds);
} }
public static Calendar parseDateFrom(String textualUploadDate) throws ParsingException {
Date date;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(textualUploadDate);
} catch (ParseException e) {
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
}
final Calendar uploadDate = Calendar.getInstance();
uploadDate.setTime(date);
return uploadDate;
}
} }

View File

@ -26,13 +26,12 @@ 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.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.utils.Parser; import org.schabi.newpipe.extractor.utils.Parser;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -43,18 +42,36 @@ public abstract class StreamExtractor extends Extractor {
public static final int NO_AGE_LIMIT = 0; public static final int NO_AGE_LIMIT = 0;
public StreamExtractor(StreamingService service, LinkHandler linkHandler, Localization localization) { public StreamExtractor(StreamingService service, LinkHandler linkHandler) {
super(service, linkHandler, localization); super(service, linkHandler);
} }
/** /**
* The day on which the stream got uploaded/created. The return information should be in the format * The original textual date provided by the service. Should be used as a fallback if
* dd.mm.yyyy, however it NewPipe will not crash if its not. * {@link #getUploadDate()} isn't provided by the service, or it fails for some reason.
* @return The day on which the stream got uploaded. *
* @throws ParsingException * <p>If the stream is a live stream, {@code null} should be returned.</p>
*
* @return The original textual date provided by the service, or {@code null}.
* @throws ParsingException if there is an error in the extraction
* @see #getUploadDate()
*/ */
@Nonnull @Nullable
public abstract String getUploadDate() throws ParsingException; public abstract String getTextualUploadDate() throws ParsingException;
/**
* A more general {@code Calendar} instance set to the date provided by the service.<br>
* Implementations usually will just parse the date returned from the {@link #getTextualUploadDate()}.
*
* <p>If the stream is a live stream, {@code null} should be returned.</p>
*
* @return The date this item was uploaded, or {@code null}.
* @throws ParsingException if there is an error in the extraction
* or the extracted date couldn't be parsed.
* @see #getTextualUploadDate()
*/
@Nullable
public abstract DateWrapper getUploadDate() throws ParsingException;
/** /**
* This will return the url to the thumbnail of the stream. Try to return the medium resolution here. * This will return the url to the thumbnail of the stream. Try to return the medium resolution here.

View File

@ -1,18 +1,19 @@
package org.schabi.newpipe.extractor.stream; package org.schabi.newpipe.extractor.stream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
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.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.utils.DashMpdParser; import org.schabi.newpipe.extractor.utils.DashMpdParser;
import org.schabi.newpipe.extractor.utils.ExtractorHelper; import org.schabi.newpipe.extractor.utils.ExtractorHelper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/* /*
* Created by Christian Schabesberger on 26.08.15. * Created by Christian Schabesberger on 26.08.15.
* *
@ -228,6 +229,11 @@ public class StreamInfo extends Info {
} catch (Exception e) { } catch (Exception e) {
streamInfo.addError(e); streamInfo.addError(e);
} }
try {
streamInfo.setTextualUploadDate(extractor.getTextualUploadDate());
} catch (Exception e) {
streamInfo.addError(e);
}
try { try {
streamInfo.setUploadDate(extractor.getUploadDate()); streamInfo.setUploadDate(extractor.getUploadDate());
} catch (Exception e) { } catch (Exception e) {
@ -271,7 +277,8 @@ public class StreamInfo extends Info {
private StreamType streamType; private StreamType streamType;
private String thumbnailUrl = ""; private String thumbnailUrl = "";
private String uploadDate = ""; private String textualUploadDate;
private DateWrapper uploadDate;
private long duration = -1; private long duration = -1;
private int ageLimit = -1; private int ageLimit = -1;
private String description; private String description;
@ -327,11 +334,19 @@ public class StreamInfo extends Info {
this.thumbnailUrl = thumbnailUrl; this.thumbnailUrl = thumbnailUrl;
} }
public String getUploadDate() { public String getTextualUploadDate() {
return textualUploadDate;
}
public void setTextualUploadDate(String textualUploadDate) {
this.textualUploadDate = textualUploadDate;
}
public DateWrapper getUploadDate() {
return uploadDate; return uploadDate;
} }
public void setUploadDate(String uploadDate) { public void setUploadDate(DateWrapper uploadDate) {
this.uploadDate = uploadDate; this.uploadDate = uploadDate;
} }

View File

@ -21,6 +21,9 @@ package org.schabi.newpipe.extractor.stream;
*/ */
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import javax.annotation.Nullable;
/** /**
* Info object for previews of unopened videos, eg search results, related videos * Info object for previews of unopened videos, eg search results, related videos
@ -29,7 +32,8 @@ public class StreamInfoItem extends InfoItem {
private final StreamType streamType; private final StreamType streamType;
private String uploaderName; private String uploaderName;
private String uploadDate; private String textualUploadDate;
@Nullable private DateWrapper uploadDate;
private long viewCount = -1; private long viewCount = -1;
private long duration = -1; private long duration = -1;
@ -52,14 +56,6 @@ public class StreamInfoItem extends InfoItem {
this.uploaderName = uploader_name; this.uploaderName = uploader_name;
} }
public String getUploadDate() {
return uploadDate;
}
public void setUploadDate(String upload_date) {
this.uploadDate = upload_date;
}
public long getViewCount() { public long getViewCount() {
return viewCount; return viewCount;
} }
@ -84,12 +80,30 @@ public class StreamInfoItem extends InfoItem {
this.uploaderUrl = uploaderUrl; this.uploaderUrl = uploaderUrl;
} }
@Nullable
public String getTextualUploadDate() {
return textualUploadDate;
}
public void setTextualUploadDate(String uploadDate) {
this.textualUploadDate = uploadDate;
}
@Nullable
public DateWrapper getUploadDate() {
return uploadDate;
}
public void setUploadDate(@Nullable DateWrapper uploadDate) {
this.uploadDate = uploadDate;
}
@Override @Override
public String toString() { public String toString() {
return "StreamInfoItem{" + return "StreamInfoItem{" +
"streamType=" + streamType + "streamType=" + streamType +
", uploaderName='" + uploaderName + '\'' + ", uploaderName='" + uploaderName + '\'' +
", uploadDate='" + uploadDate + '\'' + ", textualUploadDate='" + textualUploadDate + '\'' +
", viewCount=" + viewCount + ", viewCount=" + viewCount +
", duration=" + duration + ", duration=" + duration +
", uploaderUrl='" + uploaderUrl + '\'' + ", uploaderUrl='" + uploaderUrl + '\'' +

View File

@ -2,6 +2,9 @@ package org.schabi.newpipe.extractor.stream;
import org.schabi.newpipe.extractor.InfoItemExtractor; import org.schabi.newpipe.extractor.InfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import javax.annotation.Nullable;
/* /*
* Created by Christian Schabesberger on 28.02.16. * Created by Christian Schabesberger on 28.02.16.
@ -64,10 +67,30 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
String getUploaderUrl() throws ParsingException; String getUploaderUrl() throws ParsingException;
/** /**
* Extract the uploader name * The original textual date provided by the service. Should be used as a fallback if
* @return the uploader name * {@link #getUploadDate()} isn't provided by the service, or it fails for some reason.
* @throws ParsingException thrown if there is an error in the extraction *
* @return The original textual date provided by the service or {@code null} if not provided.
* @throws ParsingException if there is an error in the extraction
* @see #getUploadDate()
*/ */
String getUploadDate() throws ParsingException; @Nullable
String getTextualUploadDate() throws ParsingException;
/**
* Extracts the upload date and time of this item and parses it.
* <p>
* If the service doesn't provide an exact time, an approximation can be returned.
* <br>
* If the service doesn't provide any date at all, then {@code null} should be returned.
* </p>
*
* @return The date and time (can be approximated) this item was uploaded or {@code null}.
* @throws ParsingException if there is an error in the extraction
* or the extracted date couldn't be parsed.
* @see #getTextualUploadDate()
*/
@Nullable
DateWrapper getUploadDate() throws ParsingException;
} }

View File

@ -61,10 +61,15 @@ public class StreamInfoItemsCollector extends InfoItemsCollector<StreamInfoItem,
addError(e); addError(e);
} }
try { try {
resultItem.setUploadDate(extractor.getUploadDate()); resultItem.setTextualUploadDate(extractor.getTextualUploadDate());
} catch (Exception e) { } catch (Exception e) {
addError(e); addError(e);
} }
try {
resultItem.setUploadDate(extractor.getUploadDate());
} catch (ParsingException e) {
addError(e);
}
try { try {
resultItem.setViewCount(extractor.getViewCount()); resultItem.setViewCount(extractor.getViewCount());
} catch (Exception e) { } catch (Exception e) {

View File

@ -0,0 +1,51 @@
package org.schabi.newpipe.extractor.suggestion;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
public abstract class SuggestionExtractor {
private final StreamingService service;
@Nullable private Localization forcedLocalization;
@Nullable private ContentCountry forcedContentCountry;
public SuggestionExtractor(StreamingService service) {
this.service = service;
}
public abstract List<String> suggestionList(String query) throws IOException, ExtractionException;
public int getServiceId() {
return service.getServiceId();
}
public StreamingService getService() {
return service;
}
// TODO: Create a more general Extractor class
public void forceLocalization(@Nullable Localization localization) {
this.forcedLocalization = localization;
}
public void forceContentCountry(@Nullable ContentCountry contentCountry) {
this.forcedContentCountry = contentCountry;
}
@Nonnull
public Localization getExtractorLocalization() {
return forcedLocalization == null ? getService().getLocalization() : forcedLocalization;
}
@Nonnull
public ContentCountry getExtractorContentCountry() {
return forcedContentCountry == null ? getService().getContentCountry() : forcedContentCountry;
}
}

View File

@ -1,6 +1,6 @@
package org.schabi.newpipe.extractor.utils; package org.schabi.newpipe.extractor.utils;
import org.schabi.newpipe.extractor.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
@ -120,7 +120,7 @@ public class DashMpdParser {
String dashDoc; String dashDoc;
Downloader downloader = NewPipe.getDownloader(); Downloader downloader = NewPipe.getDownloader();
try { try {
dashDoc = downloader.download(streamInfo.getDashMpdUrl()); dashDoc = downloader.get(streamInfo.getDashMpdUrl()).responseBody();
} catch (IOException ioe) { } catch (IOException ioe) {
throw new DashMpdParsingException("Could not get dash mpd: " + streamInfo.getDashMpdUrl(), ioe); throw new DashMpdParsingException("Could not get dash mpd: " + streamInfo.getDashMpdUrl(), ioe);
} }

View File

@ -1,19 +0,0 @@
package org.schabi.newpipe.extractor.utils;
public class Localization {
private final String country;
private final String language;
public Localization(String country, String language) {
this.country = country;
this.language = language;
}
public String getCountry() {
return country;
}
public String getLanguage() {
return language;
}
}

View File

@ -1,224 +0,0 @@
package org.schabi.newpipe;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import org.schabi.newpipe.extractor.DownloadRequest;
import org.schabi.newpipe.extractor.DownloadResponse;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.utils.Localization;
/*
* Created by Christian Schabesberger on 28.01.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* Downloader.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class Downloader implements org.schabi.newpipe.extractor.Downloader {
private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0";
private static String mCookies = "";
private static Downloader instance = null;
private Downloader() {
}
public static Downloader getInstance() {
if (instance == null) {
synchronized (Downloader.class) {
if (instance == null) {
instance = new Downloader();
}
}
}
return instance;
}
public static synchronized void setCookies(String cookies) {
Downloader.mCookies = cookies;
}
public static synchronized String getCookies() {
return Downloader.mCookies;
}
/**
* Download the text file at the supplied URL as in download(String), but set
* the HTTP header field "Accept-Language" to the supplied string.
*
* @param siteUrl the URL of the text file to return the contents of
* @param localization the language and country (usually a 2-character code for both values)
* @return the contents of the specified text file
*/
public String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException {
Map<String, String> requestProperties = new HashMap<>();
requestProperties.put("Accept-Language", localization.getLanguage());
return download(siteUrl, requestProperties);
}
/**
* Download the text file at the supplied URL as in download(String), but set
* the HTTP header field "Accept-Language" to the supplied string.
*
* @param siteUrl the URL of the text file to return the contents of
* @param customProperties set request header properties
* @return the contents of the specified text file
* @throws IOException
*/
public String download(String siteUrl, Map<String, String> customProperties)
throws IOException, ReCaptchaException {
URL url = new URL(siteUrl);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
for (Map.Entry<String, String> pair : customProperties.entrySet()) {
con.setRequestProperty(pair.getKey(), pair.getValue());
}
return dl(con);
}
/**
* Common functionality between download(String url) and download(String url,
* String language)
*/
private static String dl(HttpsURLConnection con) throws IOException, ReCaptchaException {
StringBuilder response = new StringBuilder();
BufferedReader in = null;
try {
con.setRequestMethod("GET");
setDefaults(con);
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
} catch (UnknownHostException uhe) {// thrown when there's no internet
// connection
throw new IOException("unknown host or no network", uhe);
// Toast.makeText(getActivity(), uhe.getMessage(),
// Toast.LENGTH_LONG).show();
} catch (Exception e) {
/*
* HTTP 429 == Too Many Request Receive from Youtube.com = ReCaptcha challenge
* request See : https://github.com/rg3/youtube-dl/issues/5138
*/
if (con.getResponseCode() == 429) {
throw new ReCaptchaException("reCaptcha Challenge requested", con.getURL().toString());
}
throw new IOException(con.getResponseCode() + " " + con.getResponseMessage(), e);
} finally {
if (in != null) {
in.close();
}
}
return response.toString();
}
private static void setDefaults(HttpsURLConnection con) {
con.setConnectTimeout(30 * 1000);// 30s
con.setReadTimeout(30 * 1000);// 30s
// set default user agent
if (null == con.getRequestProperty("User-Agent")) {
con.setRequestProperty("User-Agent", USER_AGENT);
}
// add default cookies
if (getCookies().length() > 0) {
con.addRequestProperty("Cookie", getCookies());
}
}
/**
* Download (via HTTP) the text file located at the supplied URL, and return its
* contents. Primarily intended for downloading web pages.
*
* @param siteUrl the URL of the text file to download
* @return the contents of the specified text file
*/
public String download(String siteUrl) throws IOException, ReCaptchaException {
URL url = new URL(siteUrl);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
// HttpsURLConnection con = NetCipher.getHttpsURLConnection(url);
return dl(con);
}
@Override
public DownloadResponse get(String siteUrl, DownloadRequest request)
throws IOException, ReCaptchaException {
URL url = new URL(siteUrl);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
for (Map.Entry<String, List<String>> pair : request.getRequestHeaders().entrySet()) {
for(String value: pair.getValue()) {
con.addRequestProperty(pair.getKey(), value);
}
}
String responseBody = dl(con);
return new DownloadResponse(responseBody, con.getHeaderFields());
}
@Override
public DownloadResponse get(String siteUrl) throws IOException, ReCaptchaException {
return get(siteUrl, DownloadRequest.emptyRequest);
}
@Override
public DownloadResponse post(String siteUrl, DownloadRequest request)
throws IOException, ReCaptchaException {
URL url = new URL(siteUrl);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
for (Map.Entry<String, List<String>> pair : request.getRequestHeaders().entrySet()) {
for(String value: pair.getValue()) {
con.addRequestProperty(pair.getKey(), value);
}
}
// set fields to default if not set already
setDefaults(con);
if(null != request.getRequestBody()) {
byte[] postDataBytes = request.getRequestBody().getBytes("UTF-8");
con.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
con.setDoOutput(true);
con.getOutputStream().write(postDataBytes);
}
StringBuilder sb = new StringBuilder();
try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()))) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine);
}
}
return new DownloadResponse(sb.toString(), con.getHeaderFields());
}
}

View File

@ -0,0 +1,120 @@
package org.schabi.newpipe;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Request;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.localization.Localization;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class DownloaderTestImpl extends Downloader {
private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0";
private static final String DEFAULT_HTTP_ACCEPT_LANGUAGE = "en";
private static DownloaderTestImpl instance = null;
private DownloaderTestImpl() {
}
public static DownloaderTestImpl getInstance() {
if (instance == null) {
synchronized (DownloaderTestImpl.class) {
if (instance == null) {
instance = new DownloaderTestImpl();
}
}
}
return instance;
}
private void setDefaultHeaders(URLConnection connection) {
connection.setRequestProperty("User-Agent", USER_AGENT);
connection.setRequestProperty("Accept-Language", DEFAULT_HTTP_ACCEPT_LANGUAGE);
}
@Override
public Response execute(@Nonnull Request request) throws IOException, ReCaptchaException {
final String httpMethod = request.httpMethod();
final String url = request.url();
final Map<String, List<String>> headers = request.headers();
@Nullable final byte[] dataToSend = request.dataToSend();
@Nullable final Localization localization = request.localization();
final HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(30 * 1000); // 30s
connection.setReadTimeout(30 * 1000); // 30s
connection.setRequestMethod(httpMethod);
setDefaultHeaders(connection);
for (Map.Entry<String, List<String>> pair : headers.entrySet()) {
final String headerName = pair.getKey();
final List<String> headerValueList = pair.getValue();
if (headerValueList.size() > 1) {
connection.setRequestProperty(headerName, null);
for (String headerValue : headerValueList) {
connection.addRequestProperty(headerName, headerValue);
}
} else if (headerValueList.size() == 1) {
connection.setRequestProperty(headerName, headerValueList.get(0));
}
}
@Nullable OutputStream outputStream = null;
@Nullable InputStreamReader input = null;
try {
if (dataToSend != null && dataToSend.length > 0) {
connection.setDoOutput(true);
connection.setRequestProperty("Content-Length", dataToSend.length + "");
outputStream = connection.getOutputStream();
outputStream.write(dataToSend);
}
final InputStream inputStream = connection.getInputStream();
final StringBuilder response = new StringBuilder();
// Not passing any charset for decoding here... something to keep in mind.
input = new InputStreamReader(inputStream);
int readCount;
char[] buffer = new char[32 * 1024];
while ((readCount = input.read(buffer)) != -1) {
response.append(buffer, 0, readCount);
}
final int responseCode = connection.getResponseCode();
final String responseMessage = connection.getResponseMessage();
final Map<String, List<String>> responseHeaders = connection.getHeaderFields();
return new Response(responseCode, responseMessage, responseHeaders, response.toString());
} catch (Exception e) {
/*
* HTTP 429 == Too Many Request
* Receive from Youtube.com = ReCaptcha challenge request
* See : https://github.com/rg3/youtube-dl/issues/5138
*/
if (connection.getResponseCode() == 429) {
throw new ReCaptchaException("reCaptcha Challenge requested", url);
}
throw new IOException(connection.getResponseCode() + " " + connection.getResponseMessage(), e);
} finally {
if (outputStream != null) outputStream.close();
if (input != null) input.close();
}
}
}

View File

@ -2,8 +2,10 @@ package org.schabi.newpipe.extractor.services;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.util.Calendar;
import java.util.List; import java.util.List;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -27,6 +29,14 @@ public final class DefaultTests {
StreamInfoItem streamInfoItem = (StreamInfoItem) item; StreamInfoItem streamInfoItem = (StreamInfoItem) item;
assertNotEmpty("Uploader name not set: " + item, streamInfoItem.getUploaderName()); assertNotEmpty("Uploader name not set: " + item, streamInfoItem.getUploaderName());
assertNotEmpty("Uploader url not set: " + item, streamInfoItem.getUploaderUrl()); assertNotEmpty("Uploader url not set: " + item, streamInfoItem.getUploaderUrl());
final String textualUploadDate = streamInfoItem.getTextualUploadDate();
if (textualUploadDate != null && !textualUploadDate.isEmpty()) {
final DateWrapper uploadDate = streamInfoItem.getUploadDate();
assertNotNull("No parsed upload date", uploadDate);
assertTrue("Upload date not in the past", uploadDate.date().before(Calendar.getInstance()));
}
} }
} }
} }

View File

@ -2,11 +2,10 @@ package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceExtractor;
import org.schabi.newpipe.extractor.utils.Localization;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC; import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
@ -19,7 +18,7 @@ public class MediaCCCConferenceExtractorTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("en", "en_GB")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getChannelExtractor("https://api.media.ccc.de/public/conferences/froscon2017"); extractor = MediaCCC.getChannelExtractor("https://api.media.ccc.de/public/conferences/froscon2017");
extractor.fetchPage(); extractor.fetchPage();
} }

View File

@ -1,18 +1,17 @@
package org.schabi.newpipe.extractor.services.media_ccc; package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceKiosk; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceKiosk;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.List; import java.util.List;
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
/** /**
@ -24,7 +23,7 @@ public class MediaCCCConferenceListExtractorTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("en", "en_GB")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getKioskList().getDefaultKioskExtractor(); extractor = MediaCCC.getKioskList().getDefaultKioskExtractor();
extractor.fetchPage(); extractor.fetchPage();
} }

View File

@ -2,13 +2,11 @@ package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCStreamExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCStreamExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.utils.Localization;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC; import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
@ -22,7 +20,7 @@ public class MediaCCCOggTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getStreamExtractor("https://api.media.ccc.de/public/events/1317"); extractor = MediaCCC.getStreamExtractor("https://api.media.ccc.de/public/events/1317");
extractor.fetchPage(); extractor.fetchPage();

View File

@ -2,7 +2,7 @@ package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -11,7 +11,6 @@ import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.Arrays; import java.util.Arrays;
@ -28,10 +27,9 @@ public class MediaCCCSearchExtractorAllTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getSearchExtractor( new MediaCCCSearchQueryHandlerFactory() extractor = MediaCCC.getSearchExtractor( new MediaCCCSearchQueryHandlerFactory()
.fromQuery("c3", Arrays.asList(new String[0]), "") .fromQuery("c3", Arrays.asList(new String[0]), ""));
,new Localization("GB", "en"));
extractor.fetchPage(); extractor.fetchPage();
itemsPage = extractor.getInitialPage(); itemsPage = extractor.getInitialPage();
} }

View File

@ -2,7 +2,7 @@ package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -10,7 +10,6 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.search.SearchExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.Arrays; import java.util.Arrays;
@ -27,10 +26,9 @@ public class MediaCCCSearchExtractorConferencesTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getSearchExtractor( new MediaCCCSearchQueryHandlerFactory() extractor = MediaCCC.getSearchExtractor( new MediaCCCSearchQueryHandlerFactory()
.fromQuery("c3", Arrays.asList(new String[] {"conferences"}), "") .fromQuery("c3", Arrays.asList(new String[]{"conferences"}), ""));
,new Localization("GB", "en"));
extractor.fetchPage(); extractor.fetchPage();
itemsPage = extractor.getInitialPage(); itemsPage = extractor.getInitialPage();
} }

View File

@ -2,7 +2,7 @@ package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -10,7 +10,6 @@ import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.Arrays; import java.util.Arrays;
@ -28,10 +27,9 @@ public class MediaCCCSearchExtractorEventsTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getSearchExtractor( new MediaCCCSearchQueryHandlerFactory() extractor = MediaCCC.getSearchExtractor( new MediaCCCSearchQueryHandlerFactory()
.fromQuery("linux", Arrays.asList(new String[] {"events"}), "") .fromQuery("linux", Arrays.asList(new String[]{"events"}), ""));
,new Localization("GB", "en"));
extractor.fetchPage(); extractor.fetchPage();
itemsPage = extractor.getInitialPage(); itemsPage = extractor.getInitialPage();
} }

View File

@ -1,14 +1,20 @@
package org.schabi.newpipe.extractor.services.media_ccc; package org.schabi.newpipe.extractor.services.media_ccc;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.BaseExtractorTest; import org.schabi.newpipe.extractor.services.BaseExtractorTest;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCStreamExtractor; import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCStreamExtractor;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.stream.StreamExtractor;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import static java.util.Objects.requireNonNull;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC; import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
@ -20,7 +26,7 @@ public class MediaCCCStreamExtractorTest implements BaseExtractorTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getStreamExtractor("https://api.media.ccc.de/public/events/8afc16c2-d76a-53f6-85e4-90494665835d"); extractor = MediaCCC.getStreamExtractor("https://api.media.ccc.de/public/events/8afc16c2-d76a-53f6-85e4-90494665835d");
extractor.fetchPage(); extractor.fetchPage();
@ -80,4 +86,16 @@ public class MediaCCCStreamExtractorTest implements BaseExtractorTest {
public void testAudioStreams() throws Exception { public void testAudioStreams() throws Exception {
assertEquals(2, extractor.getAudioStreams().size()); assertEquals(2, extractor.getAudioStreams().size());
} }
@Test
public void testGetTextualUploadDate() throws ParsingException {
Assert.assertEquals("2018-05-11", extractor.getTextualUploadDate());
}
@Test
public void testGetUploadDate() throws ParsingException, ParseException {
final Calendar instance = Calendar.getInstance();
instance.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("2018-05-11"));
assertEquals(instance, requireNonNull(extractor.getUploadDate()).date());
}
} }

View File

@ -2,12 +2,11 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest; import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmpty; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertEmpty;
@ -24,7 +23,7 @@ public class SoundcloudChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudChannelExtractor) SoundCloud extractor = (SoundcloudChannelExtractor) SoundCloud
.getChannelExtractor("http://soundcloud.com/liluzivert/sets"); .getChannelExtractor("http://soundcloud.com/liluzivert/sets");
extractor.fetchPage(); extractor.fetchPage();
@ -108,7 +107,7 @@ public class SoundcloudChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudChannelExtractor) SoundCloud extractor = (SoundcloudChannelExtractor) SoundCloud
.getChannelExtractor("https://soundcloud.com/dubmatix"); .getChannelExtractor("https://soundcloud.com/dubmatix");
extractor.fetchPage(); extractor.fetchPage();

View File

@ -3,12 +3,11 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor; import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.List; import java.util.List;
@ -24,7 +23,7 @@ public class SoundcloudChartsExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = SoundCloud extractor = SoundCloud
.getKioskList() .getKioskList()
.getExtractorById("Top 50", null); .getExtractorById("Top 50", null);

View File

@ -2,10 +2,9 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.Localization;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -20,7 +19,7 @@ public class SoundcloudChartsLinkHandlerFactoryTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
linkHandler = new SoundcloudChartsLinkHandlerFactory(); linkHandler = new SoundcloudChartsLinkHandlerFactory();
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
} }
@Test @Test

View File

@ -1,16 +1,21 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.Assert; import org.junit.*;
import org.junit.BeforeClass; import org.schabi.newpipe.DownloaderTestImpl;
import org.junit.Test;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.*;
public class SoundcloudParsingHelperTest { public class SoundcloudParsingHelperTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
}
@Test
public void assertThatHardcodedClientIdIsValid() throws Exception {
assertTrue("Hardcoded client id is not valid anymore",
SoundcloudParsingHelper.checkIfHardcodedClientIdIsValid(DownloaderTestImpl.getInstance()));
} }
@Test @Test

View File

@ -4,14 +4,13 @@ import org.hamcrest.CoreMatchers;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest; import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
@ -27,7 +26,7 @@ public class SoundcloudPlaylistExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudPlaylistExtractor) SoundCloud extractor = (SoundcloudPlaylistExtractor) SoundCloud
.getPlaylistExtractor("https://soundcloud.com/liluzivert/sets/the-perfect-luv-tape-r?test=123"); .getPlaylistExtractor("https://soundcloud.com/liluzivert/sets/the-perfect-luv-tape-r?test=123");
extractor.fetchPage(); extractor.fetchPage();
@ -125,7 +124,7 @@ public class SoundcloudPlaylistExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudPlaylistExtractor) SoundCloud extractor = (SoundcloudPlaylistExtractor) SoundCloud
.getPlaylistExtractor("https://soundcloud.com/micky96/sets/house"); .getPlaylistExtractor("https://soundcloud.com/micky96/sets/house");
extractor.fetchPage(); extractor.fetchPage();
@ -217,7 +216,7 @@ public class SoundcloudPlaylistExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudPlaylistExtractor) SoundCloud extractor = (SoundcloudPlaylistExtractor) SoundCloud
.getPlaylistExtractor("https://soundcloud.com/user350509423/sets/edm-xxx"); .getPlaylistExtractor("https://soundcloud.com/user350509423/sets/edm-xxx");
extractor.fetchPage(); extractor.fetchPage();

View File

@ -1,18 +1,22 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import static java.util.Objects.requireNonNull;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
@ -25,7 +29,7 @@ public class SoundcloudStreamExtractorDefaultTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudStreamExtractor) SoundCloud.getStreamExtractor("https://soundcloud.com/liluzivert/do-what-i-want-produced-by-maaly-raw-don-cannon"); extractor = (SoundcloudStreamExtractor) SoundCloud.getStreamExtractor("https://soundcloud.com/liluzivert/do-what-i-want-produced-by-maaly-raw-don-cannon");
extractor.fetchPage(); extractor.fetchPage();
} }
@ -69,8 +73,15 @@ public class SoundcloudStreamExtractorDefaultTest {
} }
@Test @Test
public void testGetUploadDate() throws ParsingException { public void testGetTextualUploadDate() throws ParsingException {
assertEquals("2016-07-31", extractor.getUploadDate()); Assert.assertEquals("2016/07/31 18:18:07 +0000", extractor.getTextualUploadDate());
}
@Test
public void testGetUploadDate() throws ParsingException, ParseException {
final Calendar instance = Calendar.getInstance();
instance.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss +0000").parse("2016/07/31 18:18:07 +0000"));
assertEquals(instance, requireNonNull(extractor.getUploadDate()).date());
} }
@Test @Test

View File

@ -2,10 +2,9 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -21,7 +20,7 @@ public class SoundcloudStreamLinkHandlerFactoryTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
linkHandler = SoundcloudStreamLinkHandlerFactory.getInstance(); linkHandler = SoundcloudStreamLinkHandlerFactory.getInstance();
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)

View File

@ -2,14 +2,13 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem; import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -26,7 +25,7 @@ public class SoundcloudSubscriptionExtractorTest {
@BeforeClass @BeforeClass
public static void setupClass() { public static void setupClass() {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
subscriptionExtractor = new SoundcloudSubscriptionExtractor(ServiceList.SoundCloud); subscriptionExtractor = new SoundcloudSubscriptionExtractor(ServiceList.SoundCloud);
urlHandler = ServiceList.SoundCloud.getChannelLHFactory(); urlHandler = ServiceList.SoundCloud.getChannelLHFactory();
} }

View File

@ -2,11 +2,10 @@ package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.SuggestionExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.io.IOException; import java.io.IOException;
@ -21,7 +20,7 @@ public class SoundcloudSuggestionExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
suggestionExtractor = SoundCloud.getSuggestionExtractor(); suggestionExtractor = SoundCloud.getSuggestionExtractor();
} }

View File

@ -2,14 +2,14 @@ package org.schabi.newpipe.extractor.services.soundcloud.search;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchExtractor; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.utils.Localization;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -19,9 +19,9 @@ public class SoundcloudSearchExtractorChannelOnlyTest extends SoundcloudSearchEx
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("DE", "de")); NewPipe.init(DownloaderTestImpl.getInstance(), new Localization("de", "DE"));
extractor = (SoundcloudSearchExtractor) SoundCloud.getSearchExtractor("lill uzi vert", extractor = (SoundcloudSearchExtractor) SoundCloud.getSearchExtractor("lill uzi vert",
asList(SoundcloudSearchQueryHandlerFactory.USERS), null, new Localization("DE", "de")); asList(SoundcloudSearchQueryHandlerFactory.USERS), null);
extractor.fetchPage(); extractor.fetchPage();
itemsPage = extractor.getInitialPage(); itemsPage = extractor.getInitialPage();
} }
@ -29,7 +29,7 @@ public class SoundcloudSearchExtractorChannelOnlyTest extends SoundcloudSearchEx
@Test @Test
public void testGetSecondPage() throws Exception { public void testGetSecondPage() throws Exception {
SoundcloudSearchExtractor secondExtractor = (SoundcloudSearchExtractor) SoundCloud.getSearchExtractor("lill uzi vert", SoundcloudSearchExtractor secondExtractor = (SoundcloudSearchExtractor) SoundCloud.getSearchExtractor("lill uzi vert",
asList(SoundcloudSearchQueryHandlerFactory.USERS), null, new Localization("DE", "de")); asList(SoundcloudSearchQueryHandlerFactory.USERS), null);
ListExtractor.InfoItemsPage<InfoItem> secondPage = secondExtractor.getPage(itemsPage.getNextPageUrl()); ListExtractor.InfoItemsPage<InfoItem> secondPage = secondExtractor.getPage(itemsPage.getNextPageUrl());
assertTrue(Integer.toString(secondPage.getItems().size()), assertTrue(Integer.toString(secondPage.getItems().size()),
secondPage.getItems().size() >= 3); secondPage.getItems().size() >= 3);

View File

@ -2,22 +2,19 @@ package org.schabi.newpipe.extractor.services.soundcloud.search;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchExtractor; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchExtractor;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory; import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSearchExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSearchExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.Arrays; import java.util.Arrays;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
/* /*
* Created by Christian Schabesberger on 27.05.18 * Created by Christian Schabesberger on 27.05.18
@ -46,11 +43,10 @@ public class SoundcloudSearchExtractorDefaultTest extends SoundcloudSearchExtrac
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudSearchExtractor) SoundCloud.getSearchExtractor( extractor = (SoundcloudSearchExtractor) SoundCloud.getSearchExtractor(
new SoundcloudSearchQueryHandlerFactory().fromQuery("lill uzi vert", new SoundcloudSearchQueryHandlerFactory().fromQuery("lill uzi vert",
Arrays.asList(new String[]{"tracks"}), ""), Arrays.asList(new String[]{"tracks"}), ""));
new Localization("GB", "en"));
extractor.fetchPage(); extractor.fetchPage();
itemsPage = extractor.getInitialPage(); itemsPage = extractor.getInitialPage();
} }

View File

@ -2,22 +2,19 @@ package org.schabi.newpipe.extractor.services.soundcloud.search;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.utils.Localization;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory.PLAYLISTS; import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory.*;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory.TRACKS;
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudSearchQueryHandlerFactory.USERS;
public class SoundcloudSearchQHTest { public class SoundcloudSearchQHTest {
@BeforeClass @BeforeClass
public static void setUpClass() throws Exception { public static void setUpClass() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
} }
private static String removeClientId(String url) { private static String removeClientId(String url) {

View File

@ -2,14 +2,14 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.channel.ChannelExtractor; import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest; import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
@ -25,7 +25,7 @@ public class YoutubeChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeChannelExtractor) YouTube extractor = (YoutubeChannelExtractor) YouTube
.getChannelExtractor("http://www.youtube.com/user/Gronkh"); .getChannelExtractor("http://www.youtube.com/user/Gronkh");
extractor.fetchPage(); extractor.fetchPage();
@ -115,7 +115,7 @@ public class YoutubeChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeChannelExtractor) YouTube extractor = (YoutubeChannelExtractor) YouTube
.getChannelExtractor("https://www.youtube.com/user/Vsauce"); .getChannelExtractor("https://www.youtube.com/user/Vsauce");
extractor.fetchPage(); extractor.fetchPage();
@ -206,7 +206,7 @@ public class YoutubeChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeChannelExtractor) YouTube extractor = (YoutubeChannelExtractor) YouTube
.getChannelExtractor("https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q"); .getChannelExtractor("https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q");
extractor.fetchPage(); extractor.fetchPage();
@ -308,7 +308,7 @@ public class YoutubeChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeChannelExtractor) YouTube extractor = (YoutubeChannelExtractor) YouTube
.getChannelExtractor("https://www.youtube.com/user/CaptainDisillusion/videos"); .getChannelExtractor("https://www.youtube.com/user/CaptainDisillusion/videos");
extractor.fetchPage(); extractor.fetchPage();
@ -398,7 +398,7 @@ public class YoutubeChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeChannelExtractor) YouTube extractor = (YoutubeChannelExtractor) YouTube
.getChannelExtractor("https://www.youtube.com/user/EminemVEVO/"); .getChannelExtractor("https://www.youtube.com/user/EminemVEVO/");
extractor.fetchPage(); extractor.fetchPage();
@ -491,7 +491,7 @@ public class YoutubeChannelExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeChannelExtractor) YouTube extractor = (YoutubeChannelExtractor) YouTube
.getChannelExtractor("https://www.youtube.com/channel/UCUaQMQS9lY5lit3vurpXQ6w"); .getChannelExtractor("https://www.youtube.com/channel/UCUaQMQS9lY5lit3vurpXQ6w");
extractor.fetchPage(); extractor.fetchPage();

View File

@ -2,11 +2,10 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -21,7 +20,7 @@ public class YoutubeChannelLinkHandlerFactoryTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
linkHandler = YoutubeChannelLinkHandlerFactory.getInstance(); linkHandler = YoutubeChannelLinkHandlerFactory.getInstance();
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
} }
@Test @Test

View File

@ -0,0 +1,140 @@
package org.schabi.newpipe.extractor.services.youtube;
import org.junit.Ignore;
import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.text.SimpleDateFormat;
import java.util.*;
import static org.junit.Assert.fail;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRelatedItems;
/**
* A class that tests multiple channels and ranges of "time ago".
*/
@Ignore("Should be ran manually from time to time, as it's too time consuming.")
public class YoutubeChannelLocalizationTest {
private static final boolean DEBUG = true;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
@Test
public void testAllSupportedLocalizations() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
testLocalizationsFor("https://www.youtube.com/user/NBCNews");
testLocalizationsFor("https://www.youtube.com/channel/UCcmpeVbSSQlZRvHfdC-CRwg/videos");
testLocalizationsFor("https://www.youtube.com/channel/UC65afEgL62PGFWXY7n6CUbA");
testLocalizationsFor("https://www.youtube.com/channel/UCEOXxzW2vU0P-0THehuIIeg");
}
private void testLocalizationsFor(String channelUrl) throws Exception {
final List<Localization> supportedLocalizations = YouTube.getSupportedLocalizations();
// final List<Localization> supportedLocalizations = Arrays.asList(Localization.DEFAULT, new Localization("sr"));
final Map<Localization, List<StreamInfoItem>> results = new LinkedHashMap<>();
for (Localization currentLocalization : supportedLocalizations) {
if (DEBUG) System.out.println("Testing localization = " + currentLocalization);
ListExtractor.InfoItemsPage<StreamInfoItem> itemsPage;
try {
final ChannelExtractor extractor = YouTube.getChannelExtractor(channelUrl);
extractor.forceLocalization(currentLocalization);
extractor.fetchPage();
itemsPage = defaultTestRelatedItems(extractor, YouTube.getServiceId());
} catch (Throwable e) {
System.out.println("[!] " + currentLocalization + " → failed");
throw e;
}
final List<StreamInfoItem> items = itemsPage.getItems();
for (int i = 0; i < items.size(); i++) {
final StreamInfoItem item = items.get(i);
String debugMessage = "[" + String.format("%02d", i) + "] "
+ currentLocalization.getLocalizationCode() + "" + item.getName()
+ "\n:::: " + item.getStreamType() + ", views = " + item.getViewCount();
final DateWrapper uploadDate = item.getUploadDate();
if (uploadDate != null) {
String dateAsText = dateFormat.format(uploadDate.date().getTime());
debugMessage += "\n:::: " + item.getTextualUploadDate() +
"\n:::: " + dateAsText;
}
if (DEBUG) System.out.println(debugMessage + "\n");
}
results.put(currentLocalization, itemsPage.getItems());
if (DEBUG) System.out.println("\n===============================\n");
}
// Check results
final List<StreamInfoItem> referenceList = results.get(Localization.DEFAULT);
boolean someFail = false;
for (Map.Entry<Localization, List<StreamInfoItem>> currentResultEntry : results.entrySet()) {
if (currentResultEntry.getKey().equals(Localization.DEFAULT)) {
continue;
}
final String currentLocalizationCode = currentResultEntry.getKey().getLocalizationCode();
final String referenceLocalizationCode = Localization.DEFAULT.getLocalizationCode();
if (DEBUG) {
System.out.println("Comparing " + referenceLocalizationCode + " with " +
currentLocalizationCode);
}
final List<StreamInfoItem> currentList = currentResultEntry.getValue();
if (referenceList.size() != currentList.size()) {
if (DEBUG) System.out.println("[!] " + currentLocalizationCode + " → Lists are not equal");
someFail = true;
continue;
}
for (int i = 0; i < referenceList.size() - 1; i++) {
final StreamInfoItem referenceItem = referenceList.get(i);
final StreamInfoItem currentItem = currentList.get(i);
final DateWrapper referenceUploadDate = referenceItem.getUploadDate();
final DateWrapper currentUploadDate = currentItem.getUploadDate();
final String referenceDateString = referenceUploadDate == null ? "null" :
dateFormat.format(referenceUploadDate.date().getTime());
final String currentDateString = currentUploadDate == null ? "null" :
dateFormat.format(currentUploadDate.date().getTime());
long difference = -1;
if (referenceUploadDate != null && currentUploadDate != null) {
difference = Math.abs(referenceUploadDate.date().getTimeInMillis() - currentUploadDate.date().getTimeInMillis());
}
final boolean areTimeEquals = difference < 5 * 60 * 1000L;
if (!areTimeEquals) {
System.out.println("" +
" [!] " + currentLocalizationCode + " → [" + i + "] dates are not equal\n" +
" " + referenceLocalizationCode + ": " +
referenceDateString + "" + referenceItem.getTextualUploadDate() +
"\n " + currentLocalizationCode + ": " +
currentDateString + "" + currentItem.getTextualUploadDate());
}
}
}
if (someFail) {
fail("Some localization failed");
} else {
if (DEBUG) System.out.print("All tests passed" +
"\n\n===============================\n\n");
}
}
}

View File

@ -1,23 +1,23 @@
package org.schabi.newpipe.extractor.services.youtube; package org.schabi.newpipe.extractor.services.youtube;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
import java.io.IOException;
import java.util.List;
import org.jsoup.helper.StringUtil; import org.jsoup.helper.StringUtil;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.comments.CommentsInfo; import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.DefaultTests;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeCommentsExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeCommentsExtractor;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException;
import java.util.List;
import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
public class YoutubeCommentsExtractorTest { public class YoutubeCommentsExtractorTest {
@ -25,7 +25,7 @@ public class YoutubeCommentsExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubeCommentsExtractor) YouTube extractor = (YoutubeCommentsExtractor) YouTube
.getCommentsExtractor("https://www.youtube.com/watch?v=D00Au7k3i6o"); .getCommentsExtractor("https://www.youtube.com/watch?v=D00Au7k3i6o");
} }
@ -64,6 +64,8 @@ public class YoutubeCommentsExtractorTest {
@Test @Test
public void testGetCommentsAllData() throws IOException, ExtractionException { public void testGetCommentsAllData() throws IOException, ExtractionException {
InfoItemsPage<CommentsInfoItem> comments = extractor.getInitialPage(); InfoItemsPage<CommentsInfoItem> comments = extractor.getInitialPage();
DefaultTests.defaultTestListOfItems(YouTube.getServiceId(), comments.getItems(), comments.getErrors());
for(CommentsInfoItem c: comments.getItems()) { for(CommentsInfoItem c: comments.getItems()) {
assertFalse(StringUtil.isBlank(c.getAuthorEndpoint())); assertFalse(StringUtil.isBlank(c.getAuthorEndpoint()));
assertFalse(StringUtil.isBlank(c.getAuthorName())); assertFalse(StringUtil.isBlank(c.getAuthorName()));
@ -71,10 +73,11 @@ public class YoutubeCommentsExtractorTest {
assertFalse(StringUtil.isBlank(c.getCommentId())); assertFalse(StringUtil.isBlank(c.getCommentId()));
assertFalse(StringUtil.isBlank(c.getCommentText())); assertFalse(StringUtil.isBlank(c.getCommentText()));
assertFalse(StringUtil.isBlank(c.getName())); assertFalse(StringUtil.isBlank(c.getName()));
assertFalse(StringUtil.isBlank(c.getPublishedTime())); assertFalse(StringUtil.isBlank(c.getTextualPublishedTime()));
assertNotNull(c.getPublishedTime());
assertFalse(StringUtil.isBlank(c.getThumbnailUrl())); assertFalse(StringUtil.isBlank(c.getThumbnailUrl()));
assertFalse(StringUtil.isBlank(c.getUrl())); assertFalse(StringUtil.isBlank(c.getUrl()));
assertFalse(c.getLikeCount() == null); assertFalse(c.getLikeCount() < 0);
} }
} }

View File

@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
@ -12,7 +12,6 @@ import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest; import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -29,7 +28,7 @@ public class YoutubePlaylistExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubePlaylistExtractor) YouTube extractor = (YoutubePlaylistExtractor) YouTube
.getPlaylistExtractor("http://www.youtube.com/watch?v=lp-EO5I60KA&list=PLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj"); .getPlaylistExtractor("http://www.youtube.com/watch?v=lp-EO5I60KA&list=PLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj");
extractor.fetchPage(); extractor.fetchPage();
@ -126,7 +125,7 @@ public class YoutubePlaylistExtractorTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (YoutubePlaylistExtractor) YouTube extractor = (YoutubePlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/watch?v=8SbUC-UaAxE&list=PLWwAypAcFRgKAIIFqBr9oy-ZYZnixa_Fj"); .getPlaylistExtractor("https://www.youtube.com/watch?v=8SbUC-UaAxE&list=PLWwAypAcFRgKAIIFqBr9oy-ZYZnixa_Fj");
extractor.fetchPage(); extractor.fetchPage();

View File

@ -2,11 +2,10 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubePlaylistLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -18,8 +17,8 @@ public class YoutubePlaylistLinkHandlerFactoryTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
NewPipe.init(DownloaderTestImpl.getInstance());
linkHandler = YoutubePlaylistLinkHandlerFactory.getInstance(); linkHandler = YoutubePlaylistLinkHandlerFactory.getInstance();
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en"));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)

View File

@ -22,11 +22,10 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
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.kiosk.KioskList; import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.extractor.utils.Localization;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -41,7 +40,7 @@ public class YoutubeServiceTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
service = YouTube; service = YouTube;
kioskList = service.getKioskList(); kioskList = service.getKioskList();
} }

View File

@ -2,12 +2,11 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.FoundAdException; import org.schabi.newpipe.extractor.exceptions.FoundAdException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory; import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.utils.Localization;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -24,7 +23,7 @@ public class YoutubeStreamLinkHandlerFactoryTest {
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
linkHandler = YoutubeStreamLinkHandlerFactory.getInstance(); linkHandler = YoutubeStreamLinkHandlerFactory.getInstance();
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)

View File

@ -2,14 +2,13 @@ package org.schabi.newpipe.extractor.services.youtube;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSubscriptionExtractor; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeSubscriptionExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem; import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -28,7 +27,7 @@ public class YoutubeSubscriptionExtractorTest {
@BeforeClass @BeforeClass
public static void setupClass() { public static void setupClass() {
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); NewPipe.init(DownloaderTestImpl.getInstance());
subscriptionExtractor = new YoutubeSubscriptionExtractor(ServiceList.YouTube); subscriptionExtractor = new YoutubeSubscriptionExtractor(ServiceList.YouTube);
urlHandler = ServiceList.YouTube.getChannelLHFactory(); urlHandler = ServiceList.YouTube.getChannelLHFactory();
} }

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