mirror of
https://github.com/TeamNewPipe/NewPipeExtractor
synced 2024-12-13 20:40:03 +01:00
commit
021da75f24
@ -44,6 +44,7 @@ The following sites are currently supported:
|
||||
- SoundCloud
|
||||
- media.ccc.de
|
||||
- PeerTube (no P2P)
|
||||
- Bandcamp
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.schabi.newpipe.extractor;
|
||||
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.BandcampService;
|
||||
import org.schabi.newpipe.extractor.services.media_ccc.MediaCCCService;
|
||||
import org.schabi.newpipe.extractor.services.peertube.PeertubeService;
|
||||
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudService;
|
||||
@ -39,6 +40,7 @@ public final class ServiceList {
|
||||
public static final SoundcloudService SoundCloud;
|
||||
public static final MediaCCCService MediaCCC;
|
||||
public static final PeertubeService PeerTube;
|
||||
public static final BandcampService Bandcamp;
|
||||
|
||||
/**
|
||||
* When creating a new service, put this service in the end of this list,
|
||||
@ -49,7 +51,8 @@ public final class ServiceList {
|
||||
YouTube = new YoutubeService(0),
|
||||
SoundCloud = new SoundcloudService(1),
|
||||
MediaCCC = new MediaCCCService(2),
|
||||
PeerTube = new PeertubeService(3)
|
||||
PeerTube = new PeertubeService(3),
|
||||
Bandcamp = new BandcampService(4)
|
||||
));
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ListLinkHandlerFactory extends LinkHandlerFactory {
|
||||
@ -13,7 +14,7 @@ public abstract class ListLinkHandlerFactory extends LinkHandlerFactory {
|
||||
///////////////////////////////////
|
||||
|
||||
public List<String> getContentFilter(String url) throws ParsingException {
|
||||
return new ArrayList<>(0);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public String getSortFilter(String url) throws ParsingException {
|
||||
|
@ -0,0 +1,127 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskList;
|
||||
import org.schabi.newpipe.extractor.linkhandler.*;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.*;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.*;
|
||||
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampFeaturedExtractor.FEATURED_API_URL;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampFeaturedExtractor.KIOSK_FEATURED;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.KIOSK_RADIO;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.RADIO_API_URL;
|
||||
|
||||
public class BandcampService extends StreamingService {
|
||||
|
||||
public BandcampService(final int id) {
|
||||
super(id, "Bandcamp", Collections.singletonList(AUDIO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return BASE_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkHandlerFactory getStreamLHFactory() {
|
||||
return new BandcampStreamLinkHandlerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListLinkHandlerFactory getChannelLHFactory() {
|
||||
return new BandcampChannelLinkHandlerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListLinkHandlerFactory getPlaylistLHFactory() {
|
||||
return new BandcampPlaylistLinkHandlerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchQueryHandlerFactory getSearchQHFactory() {
|
||||
return new BandcampSearchQueryHandlerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListLinkHandlerFactory getCommentsLHFactory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchExtractor getSearchExtractor(final SearchQueryHandler queryHandler) {
|
||||
return new BandcampSearchExtractor(this, queryHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionExtractor getSuggestionExtractor() {
|
||||
return new BandcampSuggestionExtractor(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionExtractor getSubscriptionExtractor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KioskList getKioskList() throws ExtractionException {
|
||||
|
||||
KioskList kioskList = new KioskList(this);
|
||||
|
||||
try {
|
||||
kioskList.addKioskEntry((streamingService, url, kioskId) ->
|
||||
new BandcampFeaturedExtractor(
|
||||
BandcampService.this,
|
||||
new BandcampFeaturedLinkHandlerFactory().fromUrl(FEATURED_API_URL), kioskId),
|
||||
new BandcampFeaturedLinkHandlerFactory(), KIOSK_FEATURED);
|
||||
|
||||
kioskList.addKioskEntry((streamingService, url, kioskId) ->
|
||||
new BandcampRadioExtractor(BandcampService.this,
|
||||
new BandcampFeaturedLinkHandlerFactory().fromUrl(RADIO_API_URL), kioskId),
|
||||
new BandcampFeaturedLinkHandlerFactory(), KIOSK_RADIO);
|
||||
|
||||
kioskList.setDefaultKiosk(KIOSK_FEATURED);
|
||||
|
||||
} catch (final Exception e) {
|
||||
throw new ExtractionException(e);
|
||||
}
|
||||
|
||||
return kioskList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelExtractor getChannelExtractor(final ListLinkHandler linkHandler) {
|
||||
return new BandcampChannelExtractor(this, linkHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaylistExtractor getPlaylistExtractor(final ListLinkHandler linkHandler) {
|
||||
return new BandcampPlaylistExtractor(this, linkHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamExtractor getStreamExtractor(final LinkHandler linkHandler) {
|
||||
if (BandcampExtractorHelper.isRadioUrl(linkHandler.getUrl()))
|
||||
return new BandcampRadioStreamExtractor(this, linkHandler);
|
||||
else
|
||||
return new BandcampStreamExtractor(this, linkHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
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.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem.BandcampDiscographStreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BandcampChannelExtractor extends ChannelExtractor {
|
||||
|
||||
private JsonObject channelInfo;
|
||||
|
||||
public BandcampChannelExtractor(final StreamingService service, final ListLinkHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAvatarUrl() {
|
||||
if (channelInfo.getLong("bio_image_id") == 0) return "";
|
||||
|
||||
return BandcampExtractorHelper.getImageUrl(channelInfo.getLong("bio_image_id"), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBannerUrl() throws ParsingException {
|
||||
/*
|
||||
* Why does the mobile endpoint not contain the header?? Or at least not the same one?
|
||||
* Anyway we're back to querying websites
|
||||
*/
|
||||
try {
|
||||
final String html = getDownloader()
|
||||
.get(channelInfo.getString("bandcamp_url").replace("http://", "https://"))
|
||||
.responseBody();
|
||||
|
||||
return Jsoup.parse(html)
|
||||
.getElementById("customHeader")
|
||||
.getElementsByTag("img")
|
||||
.first()
|
||||
.attr("src");
|
||||
|
||||
} catch (final IOException | ReCaptchaException e) {
|
||||
throw new ParsingException("Could not download artist web site", e);
|
||||
} catch (final NullPointerException e) {
|
||||
// No banner available
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bandcamp stopped providing RSS feeds when appending /feed to any URL
|
||||
* because too few people used it.
|
||||
*/
|
||||
@Override
|
||||
public String getFeedUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSubscriberCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return channelInfo.getString("bio");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParentChannelName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParentChannelUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParentChannelAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ParsingException {
|
||||
|
||||
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
|
||||
|
||||
final JsonArray discography = channelInfo.getArray("discography");
|
||||
|
||||
for (int i = 0; i < discography.size(); i++) {
|
||||
// I define discograph as an item that can appear in a discography
|
||||
final JsonObject discograph = discography.getObject(i);
|
||||
|
||||
if (!discograph.getString("item_type").equals("track")) continue;
|
||||
|
||||
collector.commit(new BandcampDiscographStreamInfoItemExtractor(discograph, getUrl()));
|
||||
}
|
||||
|
||||
return new InfoItemsPage<>(collector, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getPage(Page page) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
|
||||
channelInfo = BandcampExtractorHelper.getArtistDetails(getId());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return channelInfo.getString("name");
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
public class BandcampChannelInfoItemExtractor implements ChannelInfoItemExtractor {
|
||||
|
||||
private final Element resultInfo, searchResult;
|
||||
|
||||
public BandcampChannelInfoItemExtractor(final Element searchResult) {
|
||||
this.searchResult = searchResult;
|
||||
resultInfo = searchResult.getElementsByClass("result-info").first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return resultInfo.getElementsByClass("heading").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
return resultInfo.getElementsByClass("itemurl").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
final Element img = searchResult.getElementsByClass("art").first()
|
||||
.getElementsByTag("img").first();
|
||||
if (img != null) {
|
||||
return img.attr("src");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return resultInfo.getElementsByClass("subhead").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSubscriberCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import com.grack.nanojson.JsonWriter;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class BandcampExtractorHelper {
|
||||
|
||||
public static final String BASE_URL = "https://bandcamp.com";
|
||||
public static final String BASE_API_URL = BASE_URL + "/api";
|
||||
|
||||
/**
|
||||
* Translate all these parameters together to the URL of the corresponding album or track
|
||||
* using the mobile API
|
||||
*/
|
||||
public static String getStreamUrlFromIds(final long bandId, final long itemId, final String itemType)
|
||||
throws ParsingException {
|
||||
|
||||
try {
|
||||
final String jsonString = NewPipe.getDownloader().get(
|
||||
BASE_API_URL + "/mobile/22/tralbum_details?band_id=" + bandId
|
||||
+ "&tralbum_id=" + itemId + "&tralbum_type=" + itemType.charAt(0))
|
||||
.responseBody();
|
||||
|
||||
return JsonParser.object().from(jsonString)
|
||||
.getString("bandcamp_url").replace("http://", "https://");
|
||||
|
||||
} catch (final JsonParserException | ReCaptchaException | IOException e) {
|
||||
throw new ParsingException("Ids could not be translated to URL", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch artist details from mobile endpoint.
|
||||
* <a href=https://notabug.org/fynngodau/bandcampDirect/wiki/rewindBandcamp+%E2%80%93+Fetching+artist+details>
|
||||
* More technical info.</a>
|
||||
*/
|
||||
public static JsonObject getArtistDetails(String id) throws ParsingException {
|
||||
try {
|
||||
return
|
||||
JsonParser.object().from(
|
||||
NewPipe.getDownloader().post(
|
||||
BASE_API_URL + "/mobile/22/band_details",
|
||||
null,
|
||||
JsonWriter.string()
|
||||
.object()
|
||||
.value("band_id", id)
|
||||
.end()
|
||||
.done()
|
||||
.getBytes()
|
||||
).responseBody()
|
||||
);
|
||||
} catch (final IOException | ReCaptchaException | JsonParserException e) {
|
||||
throw new ParsingException("Could not download band details", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id The image ID
|
||||
* @param album Whether this is the cover of an album
|
||||
* @return URL of image with this ID in size 10 which is 1200x1200 (we could also choose size 0
|
||||
* but we don't want something as large as 3460x3460 here)
|
||||
*/
|
||||
public static String getImageUrl(final long id, final boolean album) {
|
||||
return "https://f4.bcbits.com/img/" + (album ? 'a' : "") + id + "_10.jpg";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the given URL looks like it comes from a bandcamp custom domain
|
||||
* or if it comes from <code>bandcamp.com</code> itself
|
||||
*/
|
||||
public static boolean isSupportedDomain(final String url) throws ParsingException {
|
||||
|
||||
// Accept all bandcamp.com URLs
|
||||
if (url.toLowerCase().matches("https?://.+\\.bandcamp\\.com(/.*)?")) return true;
|
||||
|
||||
try {
|
||||
// Accept all other URLs if they contain a <meta> tag that says they are generated by bandcamp
|
||||
return Jsoup.parse(
|
||||
NewPipe.getDownloader().get(url).responseBody()
|
||||
)
|
||||
.getElementsByAttributeValue("name", "generator")
|
||||
.attr("content").equals("Bandcamp");
|
||||
} catch (IOException | ReCaptchaException e) {
|
||||
throw new ParsingException("Could not determine whether URL is custom domain " +
|
||||
"(not available? network error?)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the URL points to a radio kiosk.
|
||||
* @param url the URL to check
|
||||
* @return true if the URL matches <code>https://bandcamp.com/?show=SHOW_ID</code>
|
||||
*/
|
||||
public static boolean isRadioUrl(final String url) {
|
||||
return url.toLowerCase().matches("https?://bandcamp\\.com/\\?show=\\d+");
|
||||
}
|
||||
|
||||
static DateWrapper parseDate(final String textDate) throws ParsingException {
|
||||
try {
|
||||
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(
|
||||
textDate, DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH));
|
||||
return new DateWrapper(zonedDateTime.toOffsetDateTime(), false);
|
||||
} catch (final DateTimeException e) {
|
||||
throw new ParsingException("Could not parse date '" + textDate + "'", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
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.ParsingException;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemsCollector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_API_URL;
|
||||
|
||||
public class BandcampFeaturedExtractor extends KioskExtractor<PlaylistInfoItem> {
|
||||
|
||||
public static final String KIOSK_FEATURED = "Featured";
|
||||
public static final String FEATURED_API_URL = BASE_API_URL + "/mobile/24/bootstrap_data";
|
||||
|
||||
private JsonObject json;
|
||||
|
||||
public BandcampFeaturedExtractor(final StreamingService streamingService, final ListLinkHandler listLinkHandler,
|
||||
final String kioskId) {
|
||||
super(streamingService, listLinkHandler, kioskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
|
||||
try {
|
||||
json = JsonParser.object().from(
|
||||
getDownloader().post(
|
||||
FEATURED_API_URL, null, "{\"platform\":\"\",\"version\":0}".getBytes()
|
||||
).responseBody()
|
||||
);
|
||||
} catch (final JsonParserException e) {
|
||||
throw new ParsingException("Could not parse Bandcamp featured API response", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return KIOSK_FEATURED;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<PlaylistInfoItem> getInitialPage() throws IOException, ExtractionException {
|
||||
|
||||
final PlaylistInfoItemsCollector c = new PlaylistInfoItemsCollector(getServiceId());
|
||||
|
||||
final JsonArray featuredStories = json.getObject("feed_content")
|
||||
.getObject("stories")
|
||||
.getArray("featured");
|
||||
|
||||
for (int i = 0; i < featuredStories.size(); i++) {
|
||||
final JsonObject featuredStory = featuredStories.getObject(i);
|
||||
|
||||
if (featuredStory.isNull("album_title")) {
|
||||
// Is not an album, ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
c.commit(new BandcampPlaylistInfoItemFeaturedExtractor(featuredStory));
|
||||
}
|
||||
|
||||
return new InfoItemsPage<>(c, null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItemsPage<PlaylistInfoItem> getPage(Page page) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem.BandcampPlaylistStreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl;
|
||||
import static org.schabi.newpipe.extractor.utils.JsonUtils.getJsonData;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampStreamExtractor.getAlbumInfoJson;
|
||||
|
||||
public class BandcampPlaylistExtractor extends PlaylistExtractor {
|
||||
|
||||
/**
|
||||
* An arbitrarily chosen number above which cover arts won't be fetched individually for each track;
|
||||
* instead, it will be assumed that every track has the same cover art as the album, which is not
|
||||
* always the case.
|
||||
*/
|
||||
private static final int MAXIMUM_INDIVIDUAL_COVER_ARTS = 10;
|
||||
|
||||
private Document document;
|
||||
private JsonObject albumJson;
|
||||
private JsonArray trackInfo;
|
||||
private String name;
|
||||
|
||||
public BandcampPlaylistExtractor(final StreamingService service, final ListLinkHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException {
|
||||
final String html = downloader.get(getLinkHandler().getUrl()).responseBody();
|
||||
document = Jsoup.parse(html);
|
||||
albumJson = getAlbumInfoJson(html);
|
||||
trackInfo = albumJson.getArray("trackinfo");
|
||||
|
||||
try {
|
||||
name = getJsonData(html, "data-embed").getString("album_title");
|
||||
} catch (final JsonParserException e) {
|
||||
throw new ParsingException("Faulty JSON; page likely does not contain album data", e);
|
||||
} catch (final ArrayIndexOutOfBoundsException e) {
|
||||
throw new ParsingException("JSON does not exist", e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (trackInfo.size() <= 0) {
|
||||
// Albums without trackInfo need to be purchased before they can be played
|
||||
throw new ContentNotAvailableException("Album needs to be purchased");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
if (albumJson.isNull("art_id")) {
|
||||
return "";
|
||||
} else {
|
||||
return getImageUrl(albumJson.getLong("art_id"), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBannerUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderUrl() throws ParsingException {
|
||||
final String[] parts = getUrl().split("/");
|
||||
// https: (/) (/) * .bandcamp.com (/) and leave out the rest
|
||||
return "https://" + parts[2] + "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
return albumJson.getString("artist");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
try {
|
||||
return document.getElementsByClass("band-photo").first().attr("src");
|
||||
} catch (NullPointerException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return trackInfo.size();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSubChannelName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSubChannelUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSubChannelAvatarUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() throws ExtractionException {
|
||||
|
||||
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
|
||||
|
||||
for (int i = 0; i < trackInfo.size(); i++) {
|
||||
JsonObject track = trackInfo.getObject(i);
|
||||
|
||||
if (trackInfo.size() < MAXIMUM_INDIVIDUAL_COVER_ARTS) {
|
||||
// Load cover art of every track individually
|
||||
collector.commit(new BandcampPlaylistStreamInfoItemExtractor(
|
||||
track, getUploaderUrl(), getService()));
|
||||
} else {
|
||||
// Pretend every track has the same cover art as the album
|
||||
collector.commit(new BandcampPlaylistStreamInfoItemExtractor(
|
||||
track, getUploaderUrl(), getThumbnailUrl()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new InfoItemsPage<>(collector, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getPage(Page page) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BandcampPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
|
||||
private final Element searchResult, resultInfo;
|
||||
|
||||
public BandcampPlaylistInfoItemExtractor(@Nonnull Element searchResult) {
|
||||
this.searchResult = searchResult;
|
||||
resultInfo = searchResult.getElementsByClass("result-info").first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
return resultInfo.getElementsByClass("subhead").text()
|
||||
.split(" by")[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
final String length = resultInfo.getElementsByClass("length").text();
|
||||
return Integer.parseInt(length.split(" track")[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return resultInfo.getElementsByClass("heading").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return resultInfo.getElementsByClass("itemurl").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() {
|
||||
final Element img = searchResult.getElementsByClass("art").first()
|
||||
.getElementsByTag("img").first();
|
||||
if (img != null) {
|
||||
return img.attr("src");
|
||||
} else return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl;
|
||||
|
||||
public class BandcampPlaylistInfoItemFeaturedExtractor implements PlaylistInfoItemExtractor {
|
||||
|
||||
private final JsonObject featuredStory;
|
||||
|
||||
public BandcampPlaylistInfoItemFeaturedExtractor(final JsonObject featuredStory) {
|
||||
this.featuredStory = featuredStory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
return featuredStory.getString("band_name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStreamCount() {
|
||||
return featuredStory.getInt("num_streamable_tracks");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return featuredStory.getString("album_title");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return featuredStory.getString("item_url").replaceAll("http://", "https://");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() {
|
||||
return featuredStory.has("art_id") ? getImageUrl(featuredStory.getLong("art_id"), true) : "";
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
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.ParsingException;
|
||||
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.StreamInfoItemsCollector;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_API_URL;
|
||||
|
||||
public class BandcampRadioExtractor extends KioskExtractor<StreamInfoItem> {
|
||||
|
||||
public static final String KIOSK_RADIO = "Radio";
|
||||
public static final String RADIO_API_URL = BASE_API_URL + "/bcweekly/1/list";
|
||||
|
||||
private JsonObject json = null;
|
||||
|
||||
public BandcampRadioExtractor(final StreamingService streamingService, final ListLinkHandler linkHandler,
|
||||
final String kioskId) {
|
||||
super(streamingService, linkHandler, kioskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException {
|
||||
try {
|
||||
json = JsonParser.object().from(
|
||||
getDownloader().get(RADIO_API_URL).responseBody());
|
||||
} catch (final JsonParserException e) {
|
||||
throw new ExtractionException("Could not parse Bandcamp Radio API response", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return KIOSK_RADIO;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getInitialPage() {
|
||||
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
|
||||
|
||||
final JsonArray radioShows = json.getArray("results");
|
||||
|
||||
for (int i = 0; i < radioShows.size(); i++) {
|
||||
final JsonObject radioShow = radioShows.getObject(i);
|
||||
collector.commit(new BandcampRadioInfoItemExtractor(radioShow));
|
||||
}
|
||||
|
||||
return new InfoItemsPage<>(collector, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItemsPage<StreamInfoItem> getPage(final Page page) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
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.StreamType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl;
|
||||
|
||||
public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
|
||||
private final JsonObject show;
|
||||
|
||||
public BandcampRadioInfoItemExtractor(final JsonObject radioShow) {
|
||||
show = radioShow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
/* Duration is only present in the more detailed information that has to be queried separately.
|
||||
* Because the servers would probably not like over 300 queries every time someone opens the kiosk,
|
||||
* we're just providing 0 here.
|
||||
*/
|
||||
//return query(show.getInt("id")).getLong("audio_duration");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return show.getString("date");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
return BandcampExtractorHelper.parseDate(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return show.getString("subtitle");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return BASE_URL + "/?show=" + show.getInt("id");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() {
|
||||
return getImageUrl(show.getLong("image_id"), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamType getStreamType() {
|
||||
return StreamType.AUDIO_STREAM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
// JSON does not contain uploader name
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAd() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.jsoup.Jsoup;
|
||||
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.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.Description;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.*;
|
||||
|
||||
public class BandcampRadioStreamExtractor extends BandcampStreamExtractor {
|
||||
|
||||
private JsonObject showInfo;
|
||||
|
||||
public BandcampRadioStreamExtractor(final StreamingService service, final LinkHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
static JsonObject query(final int id) throws ParsingException {
|
||||
try {
|
||||
return JsonParser.object().from(
|
||||
NewPipe.getDownloader().get(BASE_API_URL + "/bcweekly/1/get?id=" + id).responseBody()
|
||||
);
|
||||
} catch (final IOException | ReCaptchaException | JsonParserException e) {
|
||||
throw new ParsingException("could not get show data", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException {
|
||||
showInfo = query(Integer.parseInt(getId()));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return showInfo.getString("subtitle"); // "audio_title" is a boring title
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderUrl() throws ContentNotSupportedException {
|
||||
throw new ContentNotSupportedException("Fan pages are not supported");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
return getLinkHandler().getUrl();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
return Jsoup.parse(showInfo.getString("image_caption"))
|
||||
.getElementsByTag("a").first().text();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return showInfo.getString("published_date");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
return getImageUrl(showInfo.getLong("show_image_id"), false);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
return BASE_URL + "/img/buttons/bandcamp-button-circle-whitecolor-512.png";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
return new Description(showInfo.getString("desc"), Description.PLAIN_TEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return showInfo.getLong("audio_duration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AudioStream> getAudioStreams() {
|
||||
final ArrayList<AudioStream> list = new ArrayList<>();
|
||||
final JsonObject streams = showInfo.getObject("audio_stream");
|
||||
|
||||
if (streams.has("opus-lo")) {
|
||||
list.add(new AudioStream(
|
||||
streams.getString("opus-lo"),
|
||||
MediaFormat.OPUS, 100
|
||||
));
|
||||
}
|
||||
if (streams.has("mp3-128")) {
|
||||
list.add(new AudioStream(
|
||||
streams.getString("mp3-128"),
|
||||
MediaFormat.MP3, 128
|
||||
));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getLicence() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getCategory() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getTags() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
import org.schabi.newpipe.extractor.Page;
|
||||
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.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
|
||||
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem.BandcampSearchStreamInfoItemExtractor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BandcampSearchExtractor extends SearchExtractor {
|
||||
|
||||
public BandcampSearchExtractor(StreamingService service, SearchQueryHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getSearchSuggestion() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCorrectedSearch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<MetaInfo> getMetaInfo() throws ParsingException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public InfoItemsPage<InfoItem> getPage(final Page page) throws IOException, ExtractionException {
|
||||
// okay apparently this is where we DOWNLOAD the page and then COMMIT its ENTRIES to an INFOITEMPAGE
|
||||
final String html = getDownloader().get(page.getUrl()).responseBody();
|
||||
|
||||
final InfoItemsSearchCollector collector = new InfoItemsSearchCollector(getServiceId());
|
||||
|
||||
|
||||
final Document d = Jsoup.parse(html);
|
||||
|
||||
final Elements searchResultsElements = d.getElementsByClass("searchresult");
|
||||
|
||||
for (final Element searchResult : searchResultsElements) {
|
||||
|
||||
final String type = searchResult.getElementsByClass("result-info").first()
|
||||
.getElementsByClass("itemtype").first().text();
|
||||
|
||||
switch (type) {
|
||||
default:
|
||||
continue;
|
||||
case "FAN":
|
||||
// don't display fan results
|
||||
break;
|
||||
|
||||
case "ARTIST":
|
||||
collector.commit(new BandcampChannelInfoItemExtractor(searchResult));
|
||||
break;
|
||||
|
||||
case "ALBUM":
|
||||
collector.commit(new BandcampPlaylistInfoItemExtractor(searchResult));
|
||||
break;
|
||||
|
||||
case "TRACK":
|
||||
collector.commit(new BandcampSearchStreamInfoItemExtractor(searchResult, null));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Count pages
|
||||
final Elements pageLists = d.getElementsByClass("pagelist");
|
||||
if (pageLists.size() == 0)
|
||||
return new InfoItemsPage<>(collector, null);
|
||||
|
||||
final Elements pages = pageLists.first().getElementsByTag("li");
|
||||
|
||||
// Find current page
|
||||
int currentPage = -1;
|
||||
for (int i = 0; i < pages.size(); i++) {
|
||||
final Element pageElement = pages.get(i);
|
||||
if (pageElement.getElementsByTag("span").size() > 0) {
|
||||
currentPage = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Search results appear to be capped at six pages
|
||||
assert pages.size() < 10;
|
||||
|
||||
String nextUrl = null;
|
||||
if (currentPage < pages.size()) {
|
||||
nextUrl = page.getUrl().substring(0, page.getUrl().length() - 1) + (currentPage + 1);
|
||||
}
|
||||
|
||||
return new InfoItemsPage<>(collector, new Page(nextUrl));
|
||||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<InfoItem> getInitialPage() throws IOException, ExtractionException {
|
||||
return getPage(new Page(getUrl()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,343 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
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.ParsingException;
|
||||
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.utils.JsonUtils;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImageUrl;
|
||||
|
||||
public class BandcampStreamExtractor extends StreamExtractor {
|
||||
|
||||
private JsonObject albumJson;
|
||||
private JsonObject current;
|
||||
private Document document;
|
||||
|
||||
public BandcampStreamExtractor(final StreamingService service, final LinkHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException, ExtractionException {
|
||||
final String html = downloader.get(getLinkHandler().getUrl()).responseBody();
|
||||
document = Jsoup.parse(html);
|
||||
albumJson = getAlbumInfoJson(html);
|
||||
current = albumJson.getObject("current");
|
||||
|
||||
if (albumJson.getArray("trackinfo").size() > 1) {
|
||||
// In this case, we are actually viewing an album page!
|
||||
throw new ExtractionException("Page is actually an album, not a track");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON that contains album's metadata from page
|
||||
*
|
||||
* @param html Website
|
||||
* @return Album metadata JSON
|
||||
* @throws ParsingException In case of a faulty website
|
||||
*/
|
||||
public static JsonObject getAlbumInfoJson(final String html) throws ParsingException {
|
||||
try {
|
||||
return JsonUtils.getJsonData(html, "data-tralbum");
|
||||
} catch (final JsonParserException e) {
|
||||
throw new ParsingException("Faulty JSON; page likely does not contain album data", e);
|
||||
} catch (final ArrayIndexOutOfBoundsException e) {
|
||||
throw new ParsingException("JSON does not exist", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return current.getString("title");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderUrl() throws ParsingException {
|
||||
final String[] parts = getUrl().split("/");
|
||||
// https: (/) (/) * .bandcamp.com (/) and leave out the rest
|
||||
return "https://" + parts[2] + "/";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
return albumJson.getString("url").replace("http://", "https://");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
return albumJson.getString("artist");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return current.getString("publish_date");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
return BandcampExtractorHelper.parseDate(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
if (albumJson.isNull("art_id")) return "";
|
||||
else return getImageUrl(albumJson.getLong("art_id"), true);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getUploaderAvatarUrl() {
|
||||
try {
|
||||
return document.getElementsByClass("band-photo").first().attr("src");
|
||||
} catch (final NullPointerException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSubChannelUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSubChannelName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSubChannelAvatarUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
final String s = Utils.nonEmptyAndNullJoin(
|
||||
"\n\n",
|
||||
new String[]{
|
||||
current.getString("about"),
|
||||
current.getString("lyrics"),
|
||||
current.getString("credits")
|
||||
}
|
||||
);
|
||||
return new Description(s, Description.PLAIN_TEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAgeLimit() {
|
||||
return NO_AGE_LIMIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeStamp() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLikeCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDislikeCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getDashMpdUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getHlsUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AudioStream> getAudioStreams() {
|
||||
final List<AudioStream> audioStreams = new ArrayList<>();
|
||||
|
||||
audioStreams.add(new AudioStream(
|
||||
albumJson.getArray("trackinfo").getObject(0)
|
||||
.getObject("file").getString("mp3-128"),
|
||||
MediaFormat.MP3, 128
|
||||
));
|
||||
return audioStreams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VideoStream> getVideoStreams() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VideoStream> getVideoOnlyStreams() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<SubtitlesStream> getSubtitlesDefault() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<SubtitlesStream> getSubtitles(MediaFormat format) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamType getStreamType() {
|
||||
return StreamType.AUDIO_STREAM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamInfoItemsCollector getRelatedStreams() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getHost() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getPrivacy() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getCategory() {
|
||||
// Get first tag from html, which is the artist's Genre
|
||||
return document
|
||||
.getElementsByClass("tralbum-tags").first()
|
||||
.getElementsByClass("tag").first().text();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getLicence() {
|
||||
|
||||
int license = current.getInt("license_type");
|
||||
|
||||
// Tests resulted in this mapping of ints to licence: https://cloud.disroot.org/s/ZTWBxbQ9fKRmRWJ/preview
|
||||
|
||||
switch (license) {
|
||||
case 1:
|
||||
return "All rights reserved ©";
|
||||
case 2:
|
||||
return "CC BY-NC-ND 3.0";
|
||||
case 3:
|
||||
return "CC BY-NC-SA 3.0";
|
||||
case 4:
|
||||
return "CC BY-NC 3.0";
|
||||
case 5:
|
||||
return "CC BY-ND 3.0";
|
||||
case 6:
|
||||
return "CC BY 3.0";
|
||||
case 8:
|
||||
return "CC BY-SA 3.0";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Locale getLanguageInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> getTags() {
|
||||
final Elements tagElements = document.getElementsByAttributeValue("itemprop", "keywords");
|
||||
|
||||
final List<String> tags = new ArrayList<>();
|
||||
|
||||
for (final Element e : tagElements) {
|
||||
tags.add(e.text());
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getSupportInfo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<StreamSegment> getStreamSegments() throws ParsingException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<MetaInfo> getMetaInfo() throws ParsingException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_API_URL;
|
||||
|
||||
public class BandcampSuggestionExtractor extends SuggestionExtractor {
|
||||
|
||||
private static final String AUTOCOMPLETE_URL = BASE_API_URL + "/fuzzysearch/1/autocomplete?q=";
|
||||
public BandcampSuggestionExtractor(final StreamingService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestionList(final String query) throws IOException, ExtractionException {
|
||||
final Downloader downloader = NewPipe.getDownloader();
|
||||
|
||||
try {
|
||||
final JsonObject fuzzyResults = JsonParser.object().from(
|
||||
downloader.get(AUTOCOMPLETE_URL + URLEncoder.encode(query, "UTF-8")).responseBody()
|
||||
);
|
||||
|
||||
final JsonArray jsonArray = fuzzyResults.getObject("auto")
|
||||
.getArray("results");
|
||||
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
|
||||
for (final Object fuzzyResult : jsonArray) {
|
||||
final String res = ((JsonObject) fuzzyResult).getString("name");
|
||||
|
||||
if (!suggestions.contains(res)) suggestions.add(res);
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
} catch (final JsonParserException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
|
||||
public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
|
||||
|
||||
private final JsonObject discograph;
|
||||
public BandcampDiscographStreamInfoItemExtractor(final JsonObject discograph, final String uploaderUrl) {
|
||||
super(uploaderUrl);
|
||||
|
||||
this.discograph = discograph;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
return discograph.getString("band_name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return discograph.getString("title");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
return BandcampExtractorHelper.getStreamUrlFromIds(
|
||||
discograph.getLong("band_id"),
|
||||
discograph.getLong("item_id"),
|
||||
discograph.getString("item_type")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
return BandcampExtractorHelper.getImageUrl(
|
||||
discograph.getLong("art_id"), true
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return -1;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
|
||||
|
||||
private final JsonObject track;
|
||||
private String substituteCoverUrl;
|
||||
private final StreamingService service;
|
||||
|
||||
public BandcampPlaylistStreamInfoItemExtractor(final JsonObject track, final String uploaderUrl,
|
||||
final StreamingService service) {
|
||||
super(uploaderUrl);
|
||||
this.track = track;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public BandcampPlaylistStreamInfoItemExtractor(final JsonObject track, final String uploaderUrl,
|
||||
final String substituteCoverUrl) {
|
||||
this(track, uploaderUrl, (StreamingService) null);
|
||||
this.substituteCoverUrl = substituteCoverUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return track.getString("title");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return getUploaderUrl() + track.getString("title_link");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return track.getLong("duration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
/* Tracks can have an individual artist name, but it is not included in the
|
||||
* given JSON.
|
||||
*/
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Each track can have its own cover art. Therefore, unless a substitute is provided,
|
||||
* the thumbnail is extracted using a stream extractor.
|
||||
*/
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
if (substituteCoverUrl != null) {
|
||||
return substituteCoverUrl;
|
||||
} else {
|
||||
try {
|
||||
final StreamExtractor extractor = service.getStreamExtractor(getUrl());
|
||||
extractor.fetchPage();
|
||||
return extractor.getThumbnailUrl();
|
||||
} catch (final ExtractionException | IOException e) {
|
||||
throw new ParsingException("could not download cover art location", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
|
||||
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
|
||||
|
||||
private final Element resultInfo, searchResult;
|
||||
|
||||
public BandcampSearchStreamInfoItemExtractor(final Element searchResult, final String uploaderUrl) {
|
||||
super(uploaderUrl);
|
||||
this.searchResult = searchResult;
|
||||
resultInfo = searchResult.getElementsByClass("result-info").first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() {
|
||||
final String subhead = resultInfo.getElementsByClass("subhead").text();
|
||||
final String[] splitBy = subhead.split("by ");
|
||||
if (splitBy.length > 1) {
|
||||
return splitBy[1];
|
||||
} else {
|
||||
return splitBy[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() throws ParsingException {
|
||||
return resultInfo.getElementsByClass("heading").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() throws ParsingException {
|
||||
return resultInfo.getElementsByClass("itemurl").text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
final Element img = searchResult.getElementsByClass("art").first()
|
||||
.getElementsByTag("img").first();
|
||||
if (img != null) {
|
||||
return img.attr("src");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return -1;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
|
||||
|
||||
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.StreamType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Implements methods that return a constant value for better readability in
|
||||
* subclasses.
|
||||
*/
|
||||
public abstract class BandcampStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
private final String uploaderUrl;
|
||||
|
||||
public BandcampStreamInfoItemExtractor(final String uploaderUrl) {
|
||||
this.uploaderUrl = uploaderUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamType getStreamType() {
|
||||
return StreamType.AUDIO_STREAM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderUrl() {
|
||||
return uploaderUrl;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUploaderVerified() throws ParsingException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAd() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.linkHandler;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
import org.schabi.newpipe.extractor.utils.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Artist do have IDs that are useful
|
||||
*/
|
||||
public class BandcampChannelLinkHandlerFactory extends ListLinkHandlerFactory {
|
||||
|
||||
|
||||
@Override
|
||||
public String getId(final String url) throws ParsingException {
|
||||
try {
|
||||
final String response = NewPipe.getDownloader().get(url).responseBody();
|
||||
|
||||
// Use band data embedded in website to extract ID
|
||||
final JsonObject bandData = JsonUtils.getJsonData(response, "data-band");
|
||||
|
||||
return String.valueOf(bandData.getLong("id"));
|
||||
|
||||
} catch (final IOException | ReCaptchaException | ArrayIndexOutOfBoundsException | JsonParserException e) {
|
||||
throw new ParsingException("Download failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the mobile endpoint as a "translator" from id to url
|
||||
*/
|
||||
@Override
|
||||
public String getUrl(final String id, final List<String> contentFilter, final String sortFilter)
|
||||
throws ParsingException {
|
||||
try {
|
||||
return BandcampExtractorHelper.getArtistDetails(id)
|
||||
.getString("bandcamp_url")
|
||||
.replace("http://", "https://");
|
||||
} catch (final NullPointerException e) {
|
||||
throw new ParsingException("JSON does not contain URL (invalid id?) or is otherwise invalid", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts only pages that lead to the root of an artist profile. Supports external pages.
|
||||
*/
|
||||
@Override
|
||||
public boolean onAcceptUrl(final String url) throws ParsingException {
|
||||
|
||||
// https: | | artist.bandcamp.com | releases
|
||||
// 0 1 2 3
|
||||
String[] splitUrl = url.split("/");
|
||||
|
||||
// URL is too short
|
||||
if (splitUrl.length < 3) return false;
|
||||
|
||||
// Must have "releases" as segment after url or none at all
|
||||
if (splitUrl.length > 3 && !splitUrl[3].equals("releases")) {
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
if (splitUrl[2].equals("daily.bandcamp.com")) {
|
||||
// Refuse links to daily.bandcamp.com as that is not an artist
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test whether domain is supported
|
||||
return BandcampExtractorHelper.isSupportedDomain(url);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.linkHandler;
|
||||
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampFeaturedExtractor.FEATURED_API_URL;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampFeaturedExtractor.KIOSK_FEATURED;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.KIOSK_RADIO;
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor.RADIO_API_URL;
|
||||
|
||||
public class BandcampFeaturedLinkHandlerFactory extends ListLinkHandlerFactory {
|
||||
|
||||
@Override
|
||||
public String getUrl(final String id, final List<String> contentFilter, final String sortFilter) {
|
||||
if (id.equals(KIOSK_FEATURED)) {
|
||||
return FEATURED_API_URL; // doesn't have a website
|
||||
} else if (id.equals(KIOSK_RADIO)) {
|
||||
return RADIO_API_URL; // doesn't have its own website
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId(String url) {
|
||||
url = Utils.replaceHttpWithHttps(url);
|
||||
if (BandcampExtractorHelper.isRadioUrl(url) || url.equals(RADIO_API_URL)) {
|
||||
return KIOSK_RADIO;
|
||||
} else if (url.equals(FEATURED_API_URL)) {
|
||||
return KIOSK_FEATURED;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAcceptUrl(String url) {
|
||||
url = Utils.replaceHttpWithHttps(url);
|
||||
return url.equals(FEATURED_API_URL) || (url.equals(RADIO_API_URL) || BandcampExtractorHelper.isRadioUrl(url));
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.linkHandler;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Just as with streams, the album ids are essentially useless for us.
|
||||
*/
|
||||
public class BandcampPlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
|
||||
@Override
|
||||
public String getId(final String url) throws ParsingException {
|
||||
return getUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl(final String url, final List<String> contentFilter, final String sortFilter)
|
||||
throws ParsingException {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts all bandcamp URLs that contain /album/ behind their domain name.
|
||||
*/
|
||||
@Override
|
||||
public boolean onAcceptUrl(final String url) throws ParsingException {
|
||||
|
||||
// Exclude URLs which do not lead to an album
|
||||
if (!url.toLowerCase().matches("https?://.+\\..+/album/.+")) return false;
|
||||
|
||||
// Test whether domain is supported
|
||||
return BandcampExtractorHelper.isSupportedDomain(url);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.linkHandler;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
|
||||
|
||||
public class BandcampSearchQueryHandlerFactory extends SearchQueryHandlerFactory {
|
||||
|
||||
|
||||
@Override
|
||||
public String getUrl(final String query, final List<String> contentFilter, final String sortFilter)
|
||||
throws ParsingException {
|
||||
try {
|
||||
|
||||
return BASE_URL + "/search?q=" +
|
||||
URLEncoder.encode(query, "UTF-8")
|
||||
+ "&page=1";
|
||||
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new ParsingException("query \"" + query + "\" could not be encoded", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp.linkHandler;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
|
||||
|
||||
/**
|
||||
* <p>Tracks don't have standalone ids, they are always in combination with the band id.
|
||||
* That's why id = url.</p>
|
||||
*
|
||||
* <p>Radio (bandcamp weekly) shows do have ids.</p>
|
||||
*/
|
||||
public class BandcampStreamLinkHandlerFactory extends LinkHandlerFactory {
|
||||
|
||||
|
||||
/**
|
||||
* @see BandcampStreamLinkHandlerFactory
|
||||
*/
|
||||
@Override
|
||||
public String getId(final String url) throws ParsingException {
|
||||
if (BandcampExtractorHelper.isRadioUrl(url)) {
|
||||
return url.split("bandcamp.com/\\?show=")[1];
|
||||
} else {
|
||||
return getUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up url
|
||||
* @see BandcampStreamLinkHandlerFactory
|
||||
*/
|
||||
@Override
|
||||
public String getUrl(final String input) {
|
||||
if (input.matches("\\d+")) {
|
||||
return BASE_URL + "/?show=" + input;
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts URLs that point to a bandcamp radio show or that are a bandcamp
|
||||
* domain and point to a track.
|
||||
*/
|
||||
@Override
|
||||
public boolean onAcceptUrl(final String url) throws ParsingException {
|
||||
|
||||
// Accept Bandcamp radio
|
||||
if (BandcampExtractorHelper.isRadioUrl(url)) return true;
|
||||
|
||||
// Don't accept URLs that don't point to a track
|
||||
if (!url.toLowerCase().matches("https?://.+\\..+/track/.+")) return false;
|
||||
|
||||
// Test whether domain is supported
|
||||
return BandcampExtractorHelper.isSupportedDomain(url);
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -118,4 +120,35 @@ public class JsonUtils {
|
||||
throw new ParsingException("Could not parse JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get an attribute of a web page as JSON
|
||||
*
|
||||
* <p>Originally a part of bandcampDirect.</p>
|
||||
* <p>Example HTML:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* <p data-town="{"name":"Mycenae","country":"Greece"}">This is Sparta!</p>
|
||||
* }
|
||||
* </pre>
|
||||
* <p>Calling this function to get the attribute <code>data-town</code> returns the JsonObject for</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* {
|
||||
* "name": "Mycenae",
|
||||
* "country": "Greece"
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* @param html The HTML where the JSON we're looking for is stored inside a
|
||||
* variable inside some JavaScript block
|
||||
* @param variable Name of the variable
|
||||
* @return The JsonObject stored in the variable with this name
|
||||
*/
|
||||
public static JsonObject getJsonData(final String html, final String variable)
|
||||
throws JsonParserException, ArrayIndexOutOfBoundsException {
|
||||
final Document document = Jsoup.parse(html);
|
||||
final String json = document.getElementsByAttribute(variable).attr(variable);
|
||||
return JsonParser.object().from(json);
|
||||
}
|
||||
}
|
||||
|
@ -271,4 +271,12 @@ public class Utils {
|
||||
return join(delimiter, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate all non-null, non-empty and strings which are not equal to <code>"null"</code>.
|
||||
*/
|
||||
public static String nonEmptyAndNullJoin(final CharSequence delimiter, final String[] elements) {
|
||||
final List<String> list = new java.util.ArrayList<>(Arrays.asList(elements));
|
||||
list.removeIf(s -> isNullOrEmpty(s) || s.equals("null"));
|
||||
return join(delimiter, list);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
public class BandcampChannelExtractorTest implements BaseChannelExtractorTest {
|
||||
|
||||
private static ChannelExtractor extractor;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = Bandcamp.getChannelExtractor("https://toupie.bandcamp.com/releases");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLength() throws ExtractionException, IOException {
|
||||
assertTrue(extractor.getInitialPage().getItems().size() >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
assertEquals("making music:)", extractor.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAvatarUrl() throws Exception {
|
||||
assertTrue("unexpected avatar URL", extractor.getAvatarUrl().contains("://f4.bcbits.com/"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testBannerUrl() throws Exception {
|
||||
assertTrue("unexpected banner URL", extractor.getBannerUrl().contains("://f4.bcbits.com/"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertNull(extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testSubscriberCount() throws Exception {
|
||||
assertEquals(-1, extractor.getSubscriberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testRelatedItems() throws Exception {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testServiceId() {
|
||||
assertEquals(Bandcamp.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testName() throws Exception {
|
||||
assertEquals("toupie", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testId() throws Exception {
|
||||
assertEquals("https://toupie.bandcamp.com/", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUrl() throws Exception {
|
||||
assertEquals("https://toupie.bandcamp.com", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testOriginalUrl() throws Exception {
|
||||
assertEquals("https://toupie.bandcamp.com", extractor.getUrl());
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampChannelLinkHandlerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test for {@link BandcampChannelLinkHandlerFactory}
|
||||
*/
|
||||
public class BandcampChannelLinkHandlerFactoryTest {
|
||||
private static BandcampChannelLinkHandlerFactory linkHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
linkHandler = new BandcampChannelLinkHandlerFactory();
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptUrl() throws ParsingException {
|
||||
// Bandcamp URLs
|
||||
assertTrue(linkHandler.acceptUrl("http://zachbenson.bandcamp.com"));
|
||||
assertTrue(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/"));
|
||||
assertTrue(linkHandler.acceptUrl("https://billwurtz.bandcamp.com/releases"));
|
||||
|
||||
assertTrue(linkHandler.acceptUrl("http://zachbenson.bandcamp.com/"));
|
||||
|
||||
assertFalse(linkHandler.acceptUrl("https://bandcamp.com"));
|
||||
assertFalse(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/track/kitchen"));
|
||||
assertFalse(linkHandler.acceptUrl("https://daily.bandcamp.com/"));
|
||||
assertFalse(linkHandler.acceptUrl("https://daily.bandcamp.com/best-of-2020/bandcamp-daily-staffers-on-their-favorite-albums-of-2020"));
|
||||
|
||||
// External URLs
|
||||
assertTrue(linkHandler.acceptUrl("http://interovgm.com/releases/"));
|
||||
assertTrue(linkHandler.acceptUrl("https://interovgm.com/releases"));
|
||||
|
||||
assertFalse(linkHandler.acceptUrl("https://example.com/releases"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetId() throws ParsingException {
|
||||
assertEquals("1196681540", linkHandler.getId("https://macbenson.bandcamp.com/"));
|
||||
assertEquals("1196681540", linkHandler.getId("http://macbenson.bandcamp.com/"));
|
||||
assertEquals("1581461772", linkHandler.getId("https://interovgm.com/releases"));
|
||||
assertEquals("3321800855", linkHandler.getId("https://infiniteammo.bandcamp.com/"));
|
||||
assertEquals("3775652329", linkHandler.getId("https://npet.bandcamp.com/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUrl() throws ParsingException {
|
||||
assertEquals("https://macbenson.bandcamp.com", linkHandler.getUrl("1196681540"));
|
||||
assertEquals("https://interovgm.com", linkHandler.getUrl("1581461772"));
|
||||
assertEquals("https://infiniteammo.bandcamp.com", linkHandler.getUrl("3321800855"));
|
||||
}
|
||||
|
||||
@Test(expected = ParsingException.class)
|
||||
public void testGetUrlWithInvalidId() throws ParsingException {
|
||||
linkHandler.getUrl("0");
|
||||
}
|
||||
|
||||
@Test(expected = ParsingException.class)
|
||||
public void testGetIdWithInvalidUrl() throws ParsingException {
|
||||
linkHandler.getId("https://bandcamp.com");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.services.BaseListExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.DefaultTests;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampFeaturedExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
/**
|
||||
* Tests for {@link BandcampFeaturedExtractor}
|
||||
*/
|
||||
public class BandcampFeaturedExtractorTest implements BaseListExtractorTest {
|
||||
|
||||
private static BandcampFeaturedExtractor extractor;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws ExtractionException, IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = (BandcampFeaturedExtractor) Bandcamp
|
||||
.getKioskList().getDefaultKioskExtractor();
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeaturedCount() throws ExtractionException, IOException {
|
||||
final List<PlaylistInfoItem> list = extractor.getInitialPage().getItems();
|
||||
assertTrue(list.size() > 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttps() throws ExtractionException, IOException {
|
||||
final List<PlaylistInfoItem> list = extractor.getInitialPage().getItems();
|
||||
assertTrue(list.get(0).getUrl().contains("https://"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testRelatedItems() throws Exception {
|
||||
DefaultTests.defaultTestRelatedItems(extractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
// more items not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testServiceId() {
|
||||
assertEquals(Bandcamp.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testName() throws Exception {
|
||||
assertEquals("Featured", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testId() {
|
||||
assertEquals("", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUrl() throws Exception {
|
||||
assertEquals("", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testOriginalUrl() throws Exception {
|
||||
assertEquals("", extractor.getOriginalUrl());
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampFeaturedLinkHandlerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link BandcampFeaturedLinkHandlerFactory}
|
||||
*/
|
||||
public class BandcampFeaturedLinkHandlerFactoryTest {
|
||||
|
||||
private static BandcampFeaturedLinkHandlerFactory linkHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
linkHandler = new BandcampFeaturedLinkHandlerFactory();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAcceptUrl() throws ParsingException {
|
||||
assertTrue(linkHandler.acceptUrl("http://bandcamp.com/?show=1"));
|
||||
assertTrue(linkHandler.acceptUrl("https://bandcamp.com/?show=1"));
|
||||
assertTrue(linkHandler.acceptUrl("http://bandcamp.com/?show=2"));
|
||||
assertTrue(linkHandler.acceptUrl("https://bandcamp.com/api/mobile/24/bootstrap_data"));
|
||||
assertTrue(linkHandler.acceptUrl("https://bandcamp.com/api/bcweekly/1/list"));
|
||||
|
||||
assertFalse(linkHandler.acceptUrl("https://bandcamp.com/?show="));
|
||||
assertFalse(linkHandler.acceptUrl("https://bandcamp.com/?show=a"));
|
||||
assertFalse(linkHandler.acceptUrl("https://bandcamp.com/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUrl() throws ParsingException {
|
||||
assertEquals("https://bandcamp.com/api/mobile/24/bootstrap_data", linkHandler.getUrl("Featured"));
|
||||
assertEquals("https://bandcamp.com/api/bcweekly/1/list", linkHandler.getUrl("Radio"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetId() {
|
||||
assertEquals("Featured", linkHandler.getId("http://bandcamp.com/api/mobile/24/bootstrap_data"));
|
||||
assertEquals("Featured", linkHandler.getId("https://bandcamp.com/api/mobile/24/bootstrap_data"));
|
||||
assertEquals("Radio", linkHandler.getId("http://bandcamp.com/?show=1"));
|
||||
assertEquals("Radio", linkHandler.getId("https://bandcamp.com/api/bcweekly/1/list"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
|
||||
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampPlaylistExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
/**
|
||||
* Tests for {@link BandcampPlaylistExtractor}
|
||||
*/
|
||||
public class BandcampPlaylistExtractorTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether playlists contain the correct amount of items
|
||||
*/
|
||||
@Test
|
||||
public void testCount() throws ExtractionException, IOException {
|
||||
final PlaylistExtractor extractor = Bandcamp.getPlaylistExtractor("https://macbenson.bandcamp.com/album/coming-of-age");
|
||||
extractor.fetchPage();
|
||||
|
||||
assertEquals(5, extractor.getStreamCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether different stream thumbnails (track covers) get loaded correctly
|
||||
*/
|
||||
@Test
|
||||
public void testDifferentTrackCovers() throws ExtractionException, IOException {
|
||||
final PlaylistExtractor extractor = Bandcamp.getPlaylistExtractor("https://zachbensonarchive.bandcamp.com/album/results-of-boredom");
|
||||
extractor.fetchPage();
|
||||
|
||||
final List<StreamInfoItem> l = extractor.getInitialPage().getItems();
|
||||
assertEquals(extractor.getThumbnailUrl(), l.get(0).getThumbnailUrl());
|
||||
assertNotEquals(extractor.getThumbnailUrl(), l.get(5).getThumbnailUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that no attempt to load every track's cover individually is made
|
||||
*/
|
||||
@Test(timeout = 10000L)
|
||||
public void testDifferentTrackCoversDuration() throws ExtractionException, IOException {
|
||||
final PlaylistExtractor extractor = Bandcamp.getPlaylistExtractor("https://infiniteammo.bandcamp.com/album/night-in-the-woods-vol-1-at-the-end-of-everything");
|
||||
extractor.fetchPage();
|
||||
|
||||
/* All tracks in this album have the same cover art, but I don't know any albums with more than 10 tracks
|
||||
* that has at least one track with a cover art different from the rest.
|
||||
*/
|
||||
final List<StreamInfoItem> l = extractor.getInitialPage().getItems();
|
||||
assertEquals(extractor.getThumbnailUrl(), l.get(0).getThumbnailUrl());
|
||||
assertEquals(extractor.getThumbnailUrl(), l.get(5).getThumbnailUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test playlists with locked content
|
||||
*/
|
||||
@Test(expected = ContentNotAvailableException.class)
|
||||
public void testLockedContent() throws ExtractionException, IOException {
|
||||
final PlaylistExtractor extractor = Bandcamp.getPlaylistExtractor("https://billwurtz.bandcamp.com/album/high-enough");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test playlist with just one track
|
||||
*/
|
||||
@Test
|
||||
public void testSingleStreamPlaylist() throws ExtractionException, IOException {
|
||||
final PlaylistExtractor extractor = Bandcamp.getPlaylistExtractor("https://zachjohnson1.bandcamp.com/album/endless");
|
||||
extractor.fetchPage();
|
||||
|
||||
assertEquals(1, extractor.getStreamCount());
|
||||
|
||||
}
|
||||
|
||||
public static class ComingOfAge implements BasePlaylistExtractorTest {
|
||||
|
||||
private static PlaylistExtractor extractor;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws ExtractionException, IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = Bandcamp.getPlaylistExtractor("https://macbenson.bandcamp.com/album/coming-of-age");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailUrl() throws ParsingException {
|
||||
assertTrue(extractor.getThumbnailUrl().contains("f4.bcbits.com/img"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBannerUrl() throws ParsingException {
|
||||
assertEquals("", extractor.getBannerUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploaderUrl() throws ParsingException {
|
||||
assertTrue(extractor.getUploaderUrl().contains("macbenson.bandcamp.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploaderName() throws ParsingException {
|
||||
assertEquals("mac benson", extractor.getUploaderName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploaderAvatarUrl() throws ParsingException {
|
||||
assertTrue(extractor.getUploaderAvatarUrl().contains("f4.bcbits.com/img"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamCount() throws ParsingException {
|
||||
assertEquals(5, extractor.getStreamCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testUploaderVerified() throws Exception {
|
||||
assertFalse(extractor.isUploaderVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialPage() throws IOException, ExtractionException {
|
||||
assertNotNull(extractor.getInitialPage().getItems().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(Bandcamp.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testName() throws ParsingException {
|
||||
assertEquals("Coming of Age", extractor.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("https://macbenson.bandcamp.com/album/coming-of-age", extractor.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUrl() throws Exception {
|
||||
assertEquals("https://macbenson.bandcamp.com/album/coming-of-age", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOriginalUrl() throws Exception {
|
||||
assertEquals("https://macbenson.bandcamp.com/album/coming-of-age", extractor.getOriginalUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNextPageUrl() throws IOException, ExtractionException {
|
||||
assertNull(extractor.getPage(extractor.getInitialPage().getNextPage()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelatedItems() throws Exception {
|
||||
// DefaultTests.defaultTestRelatedItems(extractor);
|
||||
// Would fail because BandcampPlaylistStreamInfoItemExtractor.getUploaderName() returns an empty String
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampPlaylistLinkHandlerFactory;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for {@link BandcampPlaylistLinkHandlerFactory}
|
||||
*/
|
||||
public class BandcampPlaylistLinkHandlerFactoryTest {
|
||||
|
||||
private static BandcampPlaylistLinkHandlerFactory linkHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
linkHandler = new BandcampPlaylistLinkHandlerFactory();
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptUrl() throws ParsingException {
|
||||
assertFalse(linkHandler.acceptUrl("http://interovgm.com/releases/"));
|
||||
assertFalse(linkHandler.acceptUrl("https://interovgm.com/releases"));
|
||||
assertFalse(linkHandler.acceptUrl("http://zachbenson.bandcamp.com"));
|
||||
assertFalse(linkHandler.acceptUrl("https://bandcamp.com"));
|
||||
assertFalse(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/"));
|
||||
assertFalse(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/track/kitchen"));
|
||||
assertFalse(linkHandler.acceptUrl("https://interovgm.com/track/title"));
|
||||
assertFalse(linkHandler.acceptUrl("https://example.com/album/samplealbum"));
|
||||
|
||||
assertTrue(linkHandler.acceptUrl("https://powertothequeerkids.bandcamp.com/album/power-to-the-queer-kids"));
|
||||
assertTrue(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/album/prom"));
|
||||
assertTrue(linkHandler.acceptUrl("https://MACBENSON.BANDCAMP.COM/ALBUM/COMING-OF-AGE"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.services.BaseListExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
/**
|
||||
* Tests for {@link BandcampRadioExtractor}
|
||||
*/
|
||||
public class BandcampRadioExtractorTest implements BaseListExtractorTest {
|
||||
|
||||
private static BandcampRadioExtractor extractor;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws ExtractionException, IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = (BandcampRadioExtractor) Bandcamp
|
||||
.getKioskList()
|
||||
.getExtractorById("Radio", null);
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRadioCount() throws ExtractionException, IOException {
|
||||
final List<StreamInfoItem> list = extractor.getInitialPage().getItems();
|
||||
assertTrue(list.size() > 300);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelatedItems() throws Exception {
|
||||
// DefaultTests.defaultTestRelatedItems(extractor);
|
||||
// Would fail because BandcampRadioInfoItemExtractor.getUploaderName() returns an empty String
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
// All items are on one page
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(Bandcamp.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertEquals("Radio", extractor.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testId() {
|
||||
assertEquals("Radio", extractor.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUrl() throws Exception {
|
||||
assertEquals("https://bandcamp.com/api/bcweekly/1/list", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOriginalUrl() throws Exception {
|
||||
assertEquals("https://bandcamp.com/api/bcweekly/1/list", extractor.getOriginalUrl());
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.DefaultStreamExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampRadioStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
public class BandcampRadioStreamExtractorTest extends DefaultStreamExtractorTest {
|
||||
|
||||
private static StreamExtractor extractor;
|
||||
|
||||
private static final String URL = "https://bandcamp.com/?show=230";
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws IOException, ExtractionException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = Bandcamp.getStreamExtractor(URL);
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettingCorrectStreamExtractor() throws ExtractionException {
|
||||
assertTrue(Bandcamp.getStreamExtractor("https://bandcamp.com/?show=3") instanceof BandcampRadioStreamExtractor);
|
||||
assertFalse(Bandcamp.getStreamExtractor("https://zachbenson.bandcamp.com/track/deflated")
|
||||
instanceof BandcampRadioStreamExtractor);
|
||||
}
|
||||
|
||||
@Override public StreamExtractor extractor() { return extractor; }
|
||||
@Override public String expectedName() throws Exception { return "Sound Movements"; }
|
||||
@Override public String expectedId() throws Exception { return "230"; }
|
||||
@Override public String expectedUrlContains() throws Exception { return URL; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return URL; }
|
||||
@Override public boolean expectedHasVideoStreams() { return false; }
|
||||
@Override public boolean expectedHasSubtitles() { return false; }
|
||||
@Override public boolean expectedHasFrames() { return false; }
|
||||
@Override public boolean expectedHasRelatedStreams() { return false; }
|
||||
@Override public StreamType expectedStreamType() { return StreamType.AUDIO_STREAM; }
|
||||
@Override public StreamingService expectedService() { return Bandcamp; }
|
||||
@Override public String expectedUploaderName() { return "Andrew Jervis"; }
|
||||
|
||||
@Test(expected = ContentNotSupportedException.class)
|
||||
public void testGetUploaderUrl() throws ParsingException {
|
||||
extractor.getUploaderUrl();
|
||||
}
|
||||
|
||||
@Test(expected = ContentNotSupportedException.class)
|
||||
@Override
|
||||
public void testUploaderUrl() throws Exception {
|
||||
super.testUploaderUrl();
|
||||
}
|
||||
@Override public String expectedUploaderUrl() { return null; }
|
||||
|
||||
@Override
|
||||
public List<String> expectedDescriptionContains() {
|
||||
return Collections.singletonList("Featuring special guests Nick Hakim and Elbows, plus fresh cuts from Eddie Palmieri, KRS One, Ladi6, and Moonchild.");
|
||||
}
|
||||
|
||||
@Override public long expectedLength() { return 5619; }
|
||||
@Override public long expectedViewCountAtLeast() { return -1; }
|
||||
@Override public long expectedLikeCountAtLeast() { return -1; }
|
||||
@Override public long expectedDislikeCountAtLeast() { return -1; }
|
||||
|
||||
@Override public String expectedUploadDate() { return "16 May 2017 00:00:00 GMT"; }
|
||||
@Override public String expectedTextualUploadDate() { return "16 May 2017 00:00:00 GMT"; }
|
||||
@Test
|
||||
public void testUploadDate() throws ParsingException {
|
||||
final Calendar expectedCalendar = Calendar.getInstance();
|
||||
|
||||
// 16 May 2017 00:00:00 GMT
|
||||
expectedCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
expectedCalendar.setTimeInMillis(0);
|
||||
expectedCalendar.set(2017, Calendar.MAY, 16);
|
||||
|
||||
assertEquals(expectedCalendar.getTimeInMillis(), extractor.getUploadDate().offsetDateTime().toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetThumbnailUrl() throws ParsingException {
|
||||
assertTrue(extractor.getThumbnailUrl().contains("bcbits.com/img"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUploaderAvatarUrl() throws ParsingException {
|
||||
assertTrue(extractor.getUploaderAvatarUrl().contains("bandcamp-button"));
|
||||
}
|
||||
|
||||
@Test public void testGetAudioStreams() throws ExtractionException, IOException {
|
||||
assertEquals(2, extractor.getAudioStreams().size());
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.*;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampSearchExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
/**
|
||||
* Test for {@link BandcampSearchExtractor}
|
||||
*/
|
||||
public class BandcampSearchExtractorTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether searching bandcamp for "best friend's basement" returns
|
||||
* the accordingly named song by Zach Benson
|
||||
*/
|
||||
@Test
|
||||
public void testStreamSearch() throws ExtractionException, IOException {
|
||||
final SearchExtractor extractor = Bandcamp.getSearchExtractor("best friend's basement");
|
||||
|
||||
final ListExtractor.InfoItemsPage<InfoItem> page = extractor.getInitialPage();
|
||||
final StreamInfoItem bestFriendsBasement = (StreamInfoItem) page.getItems().get(0);
|
||||
|
||||
// The track by Zach Benson should be the first result, no?
|
||||
assertEquals("Best Friend's Basement", bestFriendsBasement.getName());
|
||||
assertEquals("Zach Benson", bestFriendsBasement.getUploaderName());
|
||||
assertTrue(bestFriendsBasement.getThumbnailUrl().endsWith(".jpg"));
|
||||
assertTrue(bestFriendsBasement.getThumbnailUrl().contains("f4.bcbits.com/img/"));
|
||||
assertEquals(InfoItem.InfoType.STREAM, bestFriendsBasement.getInfoType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether searching bandcamp for "C418" returns the artist's profile
|
||||
*/
|
||||
@Test
|
||||
public void testChannelSearch() throws ExtractionException, IOException {
|
||||
final SearchExtractor extractor = Bandcamp.getSearchExtractor("C418");
|
||||
final InfoItem c418 = extractor.getInitialPage()
|
||||
.getItems().get(0);
|
||||
|
||||
// C418's artist profile should be the first result, no?
|
||||
assertEquals("C418", c418.getName());
|
||||
assertTrue(c418.getThumbnailUrl().endsWith(".jpg"));
|
||||
assertTrue(c418.getThumbnailUrl().contains("f4.bcbits.com/img/"));
|
||||
assertEquals("https://c418.bandcamp.com", c418.getUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether searching bandcamp for "minecraft volume alpha" returns the corresponding album
|
||||
*/
|
||||
@Test
|
||||
public void testAlbumSearch() throws ExtractionException, IOException {
|
||||
final SearchExtractor extractor = Bandcamp.getSearchExtractor("minecraft volume alpha");
|
||||
InfoItem minecraft = extractor.getInitialPage()
|
||||
.getItems().get(0);
|
||||
|
||||
// Minecraft volume alpha should be the first result, no?
|
||||
assertEquals("Minecraft - Volume Alpha", minecraft.getName());
|
||||
assertTrue(minecraft.getThumbnailUrl().endsWith(".jpg"));
|
||||
assertTrue(minecraft.getThumbnailUrl().contains("f4.bcbits.com/img/"));
|
||||
assertEquals("https://c418.bandcamp.com/album/minecraft-volume-alpha", minecraft.getUrl());
|
||||
|
||||
// Verify that playlist tracks counts get extracted correctly
|
||||
assertEquals(24, ((PlaylistInfoItem) minecraft).getStreamCount());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests searches with multiple pages
|
||||
*/
|
||||
@Test
|
||||
public void testMultiplePages() throws ExtractionException, IOException {
|
||||
// A query practically guaranteed to have the maximum amount of pages
|
||||
final SearchExtractor extractor = Bandcamp.getSearchExtractor("e");
|
||||
|
||||
final Page page2 = extractor.getInitialPage().getNextPage();
|
||||
assertEquals("https://bandcamp.com/search?q=e&page=2", page2.getUrl());
|
||||
|
||||
final Page page3 = extractor.getPage(page2).getNextPage();
|
||||
assertEquals("https://bandcamp.com/search?q=e&page=3", page3.getUrl());
|
||||
}
|
||||
|
||||
public static class DefaultTest extends DefaultSearchExtractorTest {
|
||||
private static SearchExtractor extractor;
|
||||
private static final String QUERY = "noise";
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = Bandcamp.getSearchExtractor(QUERY);
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Override public SearchExtractor extractor() { return extractor; }
|
||||
@Override public StreamingService expectedService() { return Bandcamp; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "bandcamp.com/search?q=" + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return "bandcamp.com/search?q=" + QUERY; }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return null; }
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampSearchQueryHandlerFactory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
public class BandcampSearchQueryHandlerFactoryTest {
|
||||
|
||||
static BandcampSearchQueryHandlerFactory searchQuery;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
|
||||
searchQuery = (BandcampSearchQueryHandlerFactory) Bandcamp
|
||||
.getSearchQHFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncoding() throws ParsingException {
|
||||
// Note: this isn't exactly as bandcamp does it (it wouldn't encode '!'), but both works
|
||||
assertEquals("https://bandcamp.com/search?q=hello%21%22%C2%A7%24%25%26%2F%28%29%3D&page=1", searchQuery.getUrl("hello!\"§$%&/()="));
|
||||
// Note: bandcamp uses %20 instead of '+', but both works
|
||||
assertEquals("https://bandcamp.com/search?q=search+query+with+spaces&page=1", searchQuery.getUrl("search query with spaces"));
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
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.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.DefaultStreamExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
/**
|
||||
* Tests for {@link BandcampStreamExtractor}
|
||||
*/
|
||||
public class BandcampStreamExtractorTest extends DefaultStreamExtractorTest {
|
||||
|
||||
private static BandcampStreamExtractor extractor;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws ExtractionException, IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
|
||||
extractor = (BandcampStreamExtractor) Bandcamp
|
||||
.getStreamExtractor("https://teaganbear.bandcamp.com/track/just-for-the-halibut-creative-commons-attribution");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamExtractor extractor() {
|
||||
return extractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamingService expectedService() {
|
||||
return Bandcamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedName() {
|
||||
return "Just for the Halibut [Creative Commons: Attribution]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedId() {
|
||||
return "https://teaganbear.bandcamp.com/track/just-for-the-halibut-creative-commons-attribution";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedUrlContains() {
|
||||
return "https://teaganbear.bandcamp.com/track/just-for-the-halibut-creative-commons-attribution";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedOriginalUrlContains() {
|
||||
return "https://teaganbear.bandcamp.com/track/just-for-the-halibut-creative-commons-attribution";
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamType expectedStreamType() {
|
||||
return StreamType.AUDIO_STREAM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedUploaderName() {
|
||||
return "Teaganbear";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedUploaderUrl() {
|
||||
return "https://teaganbear.bandcamp.com/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> expectedDescriptionContains() {
|
||||
return Collections.singletonList("it's Creative Commons so feel free to use it in whatever");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long expectedLength() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long expectedViewCountAtLeast() {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedUploadDate() {
|
||||
return "2019-03-10 23:00:42.000";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedTextualUploadDate() {
|
||||
return "10 Mar 2019 23:00:42 GMT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public long expectedLikeCountAtLeast() {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long expectedDislikeCountAtLeast() {
|
||||
return Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectedHasVideoStreams() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectedHasRelatedStreams() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectedHasSubtitles() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectedHasFrames() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedLicence() {
|
||||
return "CC BY 3.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedCategory() {
|
||||
return "dance";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArtistProfilePicture() throws Exception {
|
||||
final String url = extractor().getUploaderAvatarUrl();
|
||||
assertTrue(url.contains("://f4.bcbits.com/img/") && url.endsWith(".jpg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTranslateIdsToUrl() throws ParsingException {
|
||||
// To add tests: look at website's source, search for `band_id` and `item_id`
|
||||
assertEquals(
|
||||
"https://teaganbear.bandcamp.com/track/just-for-the-halibut-creative-commons-attribution",
|
||||
BandcampExtractorHelper.getStreamUrlFromIds(3877364987L, 3486455278L, "track")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampStreamLinkHandlerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test for {@link BandcampStreamLinkHandlerFactory}
|
||||
*/
|
||||
public class BandcampStreamLinkHandlerFactoryTest {
|
||||
|
||||
private static BandcampStreamLinkHandlerFactory linkHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
linkHandler = new BandcampStreamLinkHandlerFactory();
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRadioUrl() {
|
||||
assertEquals("https://bandcamp.com/?show=1", linkHandler.getUrl("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRadioId() throws ParsingException {
|
||||
assertEquals("2", linkHandler.getId("https://bandcamp.com/?show=2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptUrl() throws ParsingException {
|
||||
assertFalse(linkHandler.acceptUrl("http://interovgm.com/releases/"));
|
||||
assertFalse(linkHandler.acceptUrl("https://interovgm.com/releases"));
|
||||
assertFalse(linkHandler.acceptUrl("http://zachbenson.bandcamp.com"));
|
||||
assertFalse(linkHandler.acceptUrl("https://bandcamp.com"));
|
||||
assertFalse(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/"));
|
||||
assertFalse(linkHandler.acceptUrl("https://powertothequeerkids.bandcamp.com/album/power-to-the-queer-kids"));
|
||||
assertFalse(linkHandler.acceptUrl("https://example.com/track/sampletrack"));
|
||||
|
||||
assertTrue(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/track/kitchen"));
|
||||
assertTrue(linkHandler.acceptUrl("http://ZachBenson.Bandcamp.COM/Track/U-I-Tonite/"));
|
||||
assertTrue(linkHandler.acceptUrl("https://interovgm.com/track/title"));
|
||||
assertTrue(linkHandler.acceptUrl("http://bandcamP.com/?show=38"));
|
||||
assertTrue(linkHandler.acceptUrl("https://goodgoodblood-tl.bandcamp.com/track/when-it-all-wakes-up"));
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
|
||||
|
||||
package org.schabi.newpipe.extractor.services.bandcamp;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampSuggestionExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.Bandcamp;
|
||||
|
||||
/**
|
||||
* Tests for {@link BandcampSuggestionExtractor}
|
||||
*/
|
||||
public class BandcampSuggestionExtractorTest {
|
||||
|
||||
private static BandcampSuggestionExtractor extractor;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = (BandcampSuggestionExtractor) Bandcamp.getSuggestionExtractor();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchExample() throws IOException, ExtractionException {
|
||||
final List<String> c418 = extractor.suggestionList("c418");
|
||||
|
||||
assertTrue(c418.contains("C418"));
|
||||
|
||||
// There should be five results, but we can't be sure of that forever
|
||||
assertTrue(c418.size() > 2);
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ public class UtilsTest {
|
||||
@Test
|
||||
public void testJoin() {
|
||||
assertEquals("some,random,stuff", Utils.join(",", Arrays.asList("some", "random", "stuff")));
|
||||
assertEquals("some,random,not-null,stuff", Utils.nonEmptyAndNullJoin(",", new String[]{"some", "null", "random", "", "not-null", null, "stuff"}));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user