Bandcamp service with support for streams and searches

This commit is contained in:
Fynn Godau 2019-12-21 19:00:07 +01:00
parent d83787a5ca
commit a579337c9a
15 changed files with 1000 additions and 1 deletions

View File

@ -6,6 +6,7 @@ dependencies {
implementation 'org.mozilla:rhino:1.7.7.1'
implementation 'com.github.spotbugs:spotbugs-annotations:3.1.0'
implementation 'org.nibor.autolink:autolink:0.8.0'
implementation 'org.json:json:20190722'
testImplementation 'junit:junit:4.12'
}

View File

@ -4,6 +4,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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)
));
/**

View File

@ -0,0 +1,102 @@
// 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.BandcampSearchExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampStreamExtractor;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampChannelLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampSearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.linkHandler.BandcampStreamLinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
import java.util.Collections;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
public class BandcampService extends StreamingService {
public BandcampService(int id) {
super(id, "bandcamp", Collections.singletonList(AUDIO));
}
@Override
public String getBaseUrl() {
return "https://bandcamp.com";
}
@Override
public LinkHandlerFactory getStreamLHFactory() {
return new BandcampStreamLinkHandlerFactory();
}
@Override
public ListLinkHandlerFactory getChannelLHFactory() {
//return new BandcampChannelLinkHandlerFactory(); TODO
return null;
}
@Override
public ListLinkHandlerFactory getPlaylistLHFactory() {
return null;
}
@Override
public SearchQueryHandlerFactory getSearchQHFactory() {
return new BandcampSearchQueryHandlerFactory();
}
@Override
public ListLinkHandlerFactory getCommentsLHFactory() {
return null;
}
@Override
public SearchExtractor getSearchExtractor(SearchQueryHandler queryHandler) {
return new BandcampSearchExtractor(this, queryHandler);
}
@Override
public SuggestionExtractor getSuggestionExtractor() {
return null;
}
@Override
public SubscriptionExtractor getSubscriptionExtractor() {
return null;
}
@Override
public KioskList getKioskList() throws ExtractionException {
return null;
}
@Override
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) throws ExtractionException {
return null;
}
@Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) throws ExtractionException {
return null;
}
@Override
public StreamExtractor getStreamExtractor(LinkHandler linkHandler) throws ExtractionException {
return new BandcampStreamExtractor(this, linkHandler);
}
@Override
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) throws ExtractionException {
return null;
}
}

View File

@ -0,0 +1,83 @@
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
import org.json.JSONException;
import org.json.JSONObject;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import java.util.ArrayList;
import java.util.Arrays;
public class BandcampExtractorHelper {
/**
* Get JSON behind <code>var $variable = </code> out of web page
* <br/<br/>
* Originally a part of bandcampDirect.
*
* @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 getJSONFromJavaScriptVariables(String html, String variable) throws JSONException, ParsingException {
String[] part = html.split("var " + variable + " = ");
String firstHalfGone = part[1];
firstHalfGone = firstHalfGone.replaceAll("\" \\+ \"", "");
int position = -1;
int level = 0;
for (char character : firstHalfGone.toCharArray()) {
position++;
switch (character) {
case '{':
level++;
continue;
case '}':
level--;
if (level == 0) {
return new JSONObject(firstHalfGone.substring(0, position + 1)
.replaceAll(" {4}//.+", "") // Remove comments in JSON
);
}
}
}
throw new ParsingException("Unexpected HTML: JSON never ends");
}
/**
* Concatenate all non-null and non-empty strings together while separating them using
* the comma parameter
*/
public static String smartConcatenate(String[] strings, String comma) {
StringBuilder result = new StringBuilder();
// Remove empty strings
ArrayList<String> list = new ArrayList<>(Arrays.asList(strings));
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) == null || list.get(i).isEmpty()) {
list.remove(i);
}
}
// Append remaining strings to result
for (int i = 0; i < list.size(); i++) {
String string = list.get(i);
result.append(string);
if (i != list.size() - 1) {
// This is not the last iteration yet
result.append(comma);
}
}
return String.valueOf(result);
}
}

View File

@ -0,0 +1,123 @@
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
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.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import javax.annotation.Nonnull;
import java.io.IOException;
public class BandcampSearchExtractor extends SearchExtractor {
public BandcampSearchExtractor(StreamingService service, SearchQueryHandler linkHandler) {
super(service, linkHandler);
}
@Override
public String getSearchSuggestion() {
return null;
}
@Override
public InfoItemsPage<InfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
// okay apparently this is where we DOWNLOAD the page and then COMMIT its ENTRIES to an INFOITEMPAGE
String html = getDownloader().get(pageUrl).responseBody();
InfoItemsSearchCollector collector = getInfoItemSearchCollector();
Document d = Jsoup.parse(html);
Elements searchResultsElements = d.getElementsByClass("searchresult");
for (Element searchResult :
searchResultsElements) {
Element resultInfo = searchResult.getElementsByClass("result-info").first();
String type = resultInfo
.getElementsByClass("itemtype").first().text();
String image = null;
Element img = searchResult.getElementsByClass("art").first()
.getElementsByTag("img").first();
if (img != null) {
image = img.attr("src");
}
String heading = resultInfo.getElementsByClass("heading").text();
String subhead = resultInfo.getElementsByClass("subhead").text();
String url = resultInfo.getElementsByClass("itemurl").text();
switch (type) {
default:
continue;
case "FAN":
//collector.commit Channel (?) with heading, url, image
break;
case "ARTIST":
String id = resultInfo.getElementsByClass("itemurl").first()
.getElementsByTag("a").first()
.attr("href") // the link contains the id
.split("search_item_id=")
[1] // the number is behind its name
.split("&") // there is another attribute behind the name
[0]; // get the number
//searchResults.add(new Artist(heading, Long.parseLong(id), image, subhead));
//collector.commit Channel with heading, id, image, subhead
break;
case "ALBUM":
String artist = subhead.split(" by")[0];
//searchResults.add(new Album(heading, artist, url, image));
//collector.commit Playlist with heading, artist, url, image
break;
case "TRACK":
String album = subhead.split("from ")[0].split(" by")[0];
String[] splitBy = subhead.split(" by");
String artist1 = null;
if (splitBy.length > 1) {
artist1 = subhead.split(" by")[1];
}
collector.commit(new BandcampStreamInfoItemExtractor(heading, url, image, artist1, album));
break;
}
}
return new InfoItemsPage<>(getInfoItemSearchCollector(), null);
}
@Nonnull
@Override
public InfoItemsPage<InfoItem> getInitialPage() throws IOException, ExtractionException {
return getPage(getUrl());//new InfoItemsPage<>(getInfoItemSearchCollector(), null);
}
@Override
public String getNextPageUrl() throws IOException, ExtractionException {
return null;
}
@Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
}
}

View File

@ -0,0 +1,224 @@
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.schabi.newpipe.extractor.MediaFormat;
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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
public class BandcampStreamExtractor extends StreamExtractor {
private JSONObject albumJson;
private JSONObject current;
private Document document;
public BandcampStreamExtractor(StreamingService service, LinkHandler linkHandler) {
super(service, linkHandler);
}
@Override
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
String html = downloader.get(getLinkHandler().getUrl()).responseBody();
document = Jsoup.parse(html);
albumJson = getAlbumInfoJson(html);
current = albumJson.getJSONObject("current");
if (albumJson.getJSONArray("trackinfo").length() > 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(String html) throws ParsingException {
try {
return BandcampExtractorHelper.getJSONFromJavaScriptVariables(html, "TralbumData");
} catch (JSONException e) {
throw new ParsingException("Faulty JSON", e);
}
}
@Nonnull
@Override
public String getName() throws ParsingException {
return current.getString("title");
}
@Nonnull
@Override
public String getUploaderUrl() throws ParsingException {
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() throws ParsingException {
return albumJson.getString("artist");
}
@Nullable
@Override
public String getTextualUploadDate() throws ParsingException {
return current.getString("release_date");
}
@Nullable
@Override
public DateWrapper getUploadDate() {
return null;
}
@Nonnull
@Override
public String getThumbnailUrl() throws ParsingException {
return document.getElementsByAttributeValue("property", "og:image").get(0).attr("content");
}
@Nonnull
@Override
public String getUploaderAvatarUrl() {
return document.getElementsByClass("band-photo").first().attr("src");
}
@Nonnull
@Override
public String getDescription() {
return BandcampExtractorHelper.smartConcatenate(
new String[]{
getStringOrNull(current, "about"),
getStringOrNull(current, "lyrics"),
getStringOrNull(current, "credits")
}, "\n\n"
);
}
/**
* Avoid exceptions like "<code>JSONObject["about"] not a string.</code>" and instead just return null.
* This is for the case that the actual JSON has something like <code>"about": null</code>.
*/
private String getStringOrNull(JSONObject jsonObject, String value) {
try {
return jsonObject.getString(value);
} catch (JSONException e) {
return null;
}
}
@Override
public int getAgeLimit() throws ParsingException {
return 0;
}
@Override
public long getLength() throws ParsingException {
return 0;
}
@Override
public long getTimeStamp() throws ParsingException {
return 0;
}
@Override
public long getViewCount() throws ParsingException {
return -1;
}
@Override
public long getLikeCount() throws ParsingException {
return -1;
}
@Override
public long getDislikeCount() throws ParsingException {
return -1;
}
@Nonnull
@Override
public String getDashMpdUrl() throws ParsingException {
return null;
}
@Nonnull
@Override
public String getHlsUrl() throws ParsingException {
return null;
}
@Override
public List<AudioStream> getAudioStreams() throws IOException, ExtractionException {
return null;
}
@Override
public List<VideoStream> getVideoStreams() throws IOException, ExtractionException {
return null;
}
@Override
public List<VideoStream> getVideoOnlyStreams() throws IOException, ExtractionException {
return null;
}
@Nonnull
@Override
public List<SubtitlesStream> getSubtitlesDefault() throws IOException, ExtractionException {
return null;
}
@Nonnull
@Override
public List<SubtitlesStream> getSubtitles(MediaFormat format) throws IOException, ExtractionException {
return null;
}
@Override
public StreamType getStreamType() throws ParsingException {
return null;
}
@Override
public StreamInfoItem getNextStream() throws IOException, ExtractionException {
return null;
}
@Override
public StreamInfoItemsCollector getRelatedStreams() throws IOException, ExtractionException {
return null;
}
@Override
public String getErrorMessage() {
return null;
}
}

View File

@ -0,0 +1,87 @@
// Created by Fynn Godau 2019, licensed GNU GPL version 3 or later
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
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;
public class BandcampStreamInfoItemExtractor implements StreamInfoItemExtractor {
private String title;
private String url;
private String cover;
private String artist;
private String albumName;
public BandcampStreamInfoItemExtractor(String title, String url, String cover, String artist, String albumName) {
this.title = title;
this.url = url;
this.cover = cover;
this.artist = artist;
this.albumName = albumName;
}
@Override
public StreamType getStreamType() throws ParsingException {
return StreamType.AUDIO_STREAM;
}
@Override
public long getDuration() throws ParsingException {
return -1;
}
@Override
public long getViewCount() throws ParsingException {
return -1;
}
@Override
public String getUploaderName() throws ParsingException {
return artist;
}
@Override
public String getUploaderUrl() throws ParsingException {
return null;
}
@Nullable
@Override
public String getTextualUploadDate() throws ParsingException {
return null; // TODO
}
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
return null;
}
@Override
public String getName() throws ParsingException {
return title;
}
@Override
public String getUrl() throws ParsingException {
return url;
}
@Override
public String getThumbnailUrl() throws ParsingException {
return cover;
}
/**
* There are no ads just like that, duh
*/
@Override
public boolean isAd() throws ParsingException {
return false;
}
}

View File

@ -0,0 +1,59 @@
// 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.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampStreamExtractor;
import java.io.IOException;
import java.util.List;
/**
* Artist do have IDs that are useful
*/
public class BandcampChannelLinkHandlerFactory extends ListLinkHandlerFactory {
@Override
public String getId(String url) throws ParsingException {
try {
String response = NewPipe.getDownloader().get(url).responseBody();
return BandcampStreamExtractor.getAlbumInfoJson(response)
.getString("band_id");
} catch (IOException | ReCaptchaException e) {
throw new ParsingException("Download failed", e);
}
}
@Override
public String getUrl(String id, List<String> contentFilter, String sortFilter) throws ParsingException {
return null; // TODO
}
/**
* Matches <code>* .bandcamp.com</code> as well as custom domains
* where the profile is at <code>* . * /releases</code>
*/
@Override
public boolean onAcceptUrl(String url) throws ParsingException {
// Ends with "bandcamp.com" or "bandcamp.com/"?
boolean endsWithBandcampCom = url.endsWith("bandcamp.com")
|| url.endsWith("bandcamp.com/");
// Is a subdomain of bandcamp.com?
boolean isBandcampComSubdomain = url.matches("https?://.+\\.bandcamp\\.com");
// Is root of bandcamp.com subdomain?
boolean isBandcampComArtistPage = endsWithBandcampCom && isBandcampComSubdomain;
boolean isCustomDomainReleases = url.matches("https?://.+\\..+/releases/?(?!.)");
return isBandcampComArtistPage || isCustomDomainReleases;
}
}

View File

@ -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;
public class BandcampSearchQueryHandlerFactory extends SearchQueryHandlerFactory {
private static final String SEARCH_URL = "https://bandcamp.com/search?q=";
public static final String CHARSET_UTF_8 = "UTF-8";
@Override
public String getUrl(String query, List<String> contentFilter, String sortFilter) throws ParsingException {
try {
return SEARCH_URL +
URLEncoder.encode(query, CHARSET_UTF_8);
} catch (UnsupportedEncodingException e) {
throw new ParsingException("query \"" + query + "\" could not be encoded", e);
}
}
}

View File

@ -0,0 +1,47 @@
// 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;
/**
* Tracks do have IDs, but they are not really useful. That's why id = url.
* Instead, URLs are cleaned up so that they always look the same.
*/
public class BandcampStreamLinkHandlerFactory extends LinkHandlerFactory {
/**
* @see BandcampStreamLinkHandlerFactory
*/
@Override
public String getId(String url) throws ParsingException {
return getUrl(url);
}
/**
* Clean up url
* @see BandcampStreamLinkHandlerFactory
*/
@Override
public String getUrl(String url) {
if (url.endsWith("/"))
url = url.substring(0, url.length() - 1);
url = url.replace("http://", "https://").toLowerCase();
return url;
}
/**
* Sometimes, the root page of an artist is also an album or track
* page. In that case, it is assumed that one actually wants to open
* the profile and not the track it has set as the default one.
* <br/><br/>Urls are expected to be in this format to account for
* custom domains:
* <br/><code>https:// * . * /track/ *</code>
*/
@Override
public boolean onAcceptUrl(String url) {
return getUrl(url).matches("https?://.+\\..+/track/.+");
}
}

View File

@ -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.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.*;
import static org.junit.Assert.assertTrue;
/**
* 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 {
// Tests expecting true
assertTrue(linkHandler.acceptUrl("http://interovgm.com/releases/"));
assertTrue(linkHandler.acceptUrl("https://interovgm.com/releases"));
assertTrue(linkHandler.acceptUrl("http://zachbenson.bandcamp.com"));
// Tests expecting false
assertFalse(linkHandler.acceptUrl("https://bandcamp.com"));
assertFalse(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/track/kitchen"));
assertFalse(linkHandler.acceptUrl("https://zachbenson.bandcamp.com/"));
}
}

View File

@ -0,0 +1,55 @@
// 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.DownloaderTestImpl;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampSearchExtractor;
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 {
private static BandcampSearchExtractor extractor;
@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 testBestFriendsBasement() throws ExtractionException, IOException {
extractor = (BandcampSearchExtractor) bandcamp
.getSearchExtractor("best friend's basement");
ListExtractor.InfoItemsPage<InfoItem> page = extractor.getInitialPage();
InfoItem bestFriendsBasement = page.getItems().get(0);
// The track by Zach Benson should be the first result, no?
assertEquals("Best Friend's Basement", bestFriendsBasement.getName());
assertTrue(bestFriendsBasement.getThumbnailUrl().endsWith(".jpg"));
assertTrue(bestFriendsBasement.getThumbnailUrl().contains("f4.bcbits.com/img/"));
assertEquals(InfoItem.InfoType.STREAM, bestFriendsBasement.getInfoType());
}
}

View File

@ -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.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", searchQuery.getUrl("hello!\"§$%&/()="));
// Note: bandcamp uses %20 instead of '+', but both works
assertEquals("https://bandcamp.com/search?q=search+query+with+spaces", searchQuery.getUrl("search query with spaces"));
}
}

View File

@ -0,0 +1,64 @@
// 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.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampStreamExtractor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.bandcamp;
public class BandcampStreamExtractorTest {
private static BandcampStreamExtractor extractor;
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (BandcampStreamExtractor) bandcamp
.getStreamExtractor("https://zachbenson.bandcamp.com/track/kitchen");
extractor.fetchPage();
}
@Test(expected = ExtractionException.class)
public void testAlbum() throws ExtractionException {
bandcamp.getStreamExtractor("https://zachbenson.bandcamp.com/album/prom");
}
@Test
public void testServiceId() {
}
@Test
public void testName() throws ParsingException {
assertEquals("kitchen", extractor.getName());
}
@Test
public void testUrl() throws ParsingException {
assertEquals("https://zachbenson.bandcamp.com/track/kitchen", extractor.getUrl());
}
@Test
public void testArtistUrl() throws ParsingException {
assertEquals("https://zachbenson.bandcamp.com/", extractor.getUploaderUrl());
}
@Test
public void testDescription() {
assertEquals(831, extractor.getDescription().length());
}
@Test
public void testArtistProfilePicture() {
String url = extractor.getUploaderAvatarUrl();
assertTrue(url.contains("://f4.bcbits.com/img/") && url.endsWith(".jpg"));
}
}

View File

@ -0,0 +1,47 @@
// 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.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.*;
import static org.junit.Assert.assertFalse;
/**
* 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 testUrlCleanup() {
assertEquals("https://zachbenson.bandcamp.com/track/u-i-tonite", linkHandler.getUrl("http://ZachBenson.Bandcamp.COM/Track/U-I-Tonite/"));
}
@Test
public void testAcceptUrl() throws ParsingException {
// Tests expecting false
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/"));
// Tests expecting true
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"));
}
}