Merge pull request #293 from Stypox/fix-soundcloud

[SoundCloud] Fix by migrating to `api-v2` and other improvements
This commit is contained in:
Tobias Groza 2020-03-18 17:49:54 +01:00 committed by GitHub
commit 6f03c6e877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 75 deletions

View File

@ -44,7 +44,7 @@ public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
} }
private void computNextPageAndStreams() throws IOException, ExtractionException { private void computeNextPageAndStreams() throws IOException, ExtractionException {
collector = new StreamInfoItemsCollector(getServiceId()); collector = new StreamInfoItemsCollector(getServiceId());
String apiUrl = "https://api-v2.soundcloud.com/charts" + String apiUrl = "https://api-v2.soundcloud.com/charts" +
@ -69,7 +69,7 @@ public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
@Override @Override
public String getNextPageUrl() throws IOException, ExtractionException { public String getNextPageUrl() throws IOException, ExtractionException {
if (nextPageUrl == null) { if (nextPageUrl == null) {
computNextPageAndStreams(); computeNextPageAndStreams();
} }
return nextPageUrl; return nextPageUrl;
} }
@ -78,7 +78,7 @@ public class SoundcloudChartsExtractor extends KioskExtractor<StreamInfoItem> {
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
if (collector == null) { if (collector == null) {
computNextPageAndStreams(); computeNextPageAndStreams();
} }
return new InfoItemsPage<>(collector, getNextPageUrl()); return new InfoItemsPage<>(collector, getNextPageUrl());
} }

View File

@ -108,7 +108,7 @@ public class SoundcloudParsingHelper {
* See https://developers.soundcloud.com/docs/api/reference#resolve * See https://developers.soundcloud.com/docs/api/reference#resolve
*/ */
public static JsonObject resolveFor(Downloader downloader, String url) throws IOException, ExtractionException { public static JsonObject resolveFor(Downloader downloader, String url) throws IOException, ExtractionException {
String apiUrl = "https://api.soundcloud.com/resolve" String apiUrl = "https://api-v2.soundcloud.com/resolve"
+ "?url=" + URLEncoder.encode(url, "UTF-8") + "?url=" + URLEncoder.encode(url, "UTF-8")
+ "&client_id=" + clientId(); + "&client_id=" + clientId();

View File

@ -1,8 +1,11 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@ -12,16 +15,20 @@ import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class SoundcloudPlaylistExtractor extends PlaylistExtractor { public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
private static final int streamsPerRequestedPage = 15;
private String playlistId; private String playlistId;
private JsonObject playlist; private JsonObject playlist;
private StreamInfoItemsCollector streamInfoItemsCollector = null; private StreamInfoItemsCollector streamInfoItemsCollector;
private String nextPageUrl = null; private String nextPageUrl;
public SoundcloudPlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) { public SoundcloudPlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler); super(service, linkHandler);
@ -31,7 +38,7 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
playlistId = getLinkHandler().getId(); playlistId = getLinkHandler().getId();
String apiUrl = "https://api.soundcloud.com/playlists/" + playlistId + String apiUrl = "https://api-v2.soundcloud.com/playlists/" + playlistId +
"?client_id=" + SoundcloudParsingHelper.clientId() + "?client_id=" + SoundcloudParsingHelper.clientId() +
"&representation=compact"; "&representation=compact";
@ -55,6 +62,7 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
return playlist.getString("title"); return playlist.getString("title");
} }
@Nullable
@Override @Override
public String getThumbnailUrl() { public String getThumbnailUrl() {
String artworkUrl = playlist.getString("artwork_url"); String artworkUrl = playlist.getString("artwork_url");
@ -64,21 +72,20 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
// if it also fails, return null // if it also fails, return null
try { try {
final InfoItemsPage<StreamInfoItem> infoItems = getInitialPage(); final InfoItemsPage<StreamInfoItem> infoItems = getInitialPage();
if (infoItems.getItems().isEmpty()) return null;
for (StreamInfoItem item : infoItems.getItems()) { for (StreamInfoItem item : infoItems.getItems()) {
final String thumbnailUrl = item.getThumbnailUrl(); artworkUrl = item.getThumbnailUrl();
if (thumbnailUrl == null || thumbnailUrl.isEmpty()) continue; if (artworkUrl != null && !artworkUrl.isEmpty()) break;
String thumbnailUrlBetterResolution = thumbnailUrl.replace("large.jpg", "crop.jpg");
return thumbnailUrlBetterResolution;
} }
} catch (Exception ignored) { } catch (Exception ignored) {
} }
if (artworkUrl == null) {
return null;
}
} }
String artworkUrlBetterResolution = artworkUrl.replace("large.jpg", "crop.jpg"); return artworkUrl.replace("large.jpg", "crop.jpg");
return artworkUrlBetterResolution;
} }
@Override @Override
@ -110,27 +117,42 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
@Override @Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException { public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
if (streamInfoItemsCollector == null) { if (streamInfoItemsCollector == null) {
computeStreamsAndNextPageUrl(); computeInitialTracksAndNextPageUrl();
} }
return new InfoItemsPage<>(streamInfoItemsCollector, getNextPageUrl()); return new InfoItemsPage<>(streamInfoItemsCollector, nextPageUrl);
} }
private void computeStreamsAndNextPageUrl() throws ExtractionException, IOException { private void computeInitialTracksAndNextPageUrl() throws IOException, ExtractionException {
streamInfoItemsCollector = new StreamInfoItemsCollector(getServiceId()); streamInfoItemsCollector = new StreamInfoItemsCollector(getServiceId());
StringBuilder nextPageUrlBuilder = new StringBuilder("https://api-v2.soundcloud.com/tracks?client_id=");
nextPageUrlBuilder.append(SoundcloudParsingHelper.clientId());
nextPageUrlBuilder.append("&ids=");
// Note the "api", NOT "api-v2" JsonArray tracks = playlist.getArray("tracks");
String apiUrl = "https://api.soundcloud.com/playlists/" + getId() + "/tracks" for (Object o : tracks) {
+ "?client_id=" + SoundcloudParsingHelper.clientId() if (o instanceof JsonObject) {
+ "&limit=20" JsonObject track = (JsonObject) o;
+ "&linked_partitioning=1"; if (track.has("title")) { // i.e. if full info is available
streamInfoItemsCollector.commit(new SoundcloudStreamInfoItemExtractor(track));
} else {
// %09d would be enough, but a 0 before the number does not create problems, so let's be sure
nextPageUrlBuilder.append(String.format("%010d,", track.getInt("id")));
}
}
}
nextPageUrl = SoundcloudParsingHelper.getStreamsFromApiMinItems(15, streamInfoItemsCollector, apiUrl); nextPageUrlBuilder.setLength(nextPageUrlBuilder.length() - 1); // remove trailing ,
nextPageUrl = nextPageUrlBuilder.toString();
if (nextPageUrl.endsWith("&ids")) {
// there are no other videos
nextPageUrl = "";
}
} }
@Override @Override
public String getNextPageUrl() throws IOException, ExtractionException { public String getNextPageUrl() throws IOException, ExtractionException {
if (nextPageUrl == null) { if (nextPageUrl == null) {
computeStreamsAndNextPageUrl(); computeInitialTracksAndNextPageUrl();
} }
return nextPageUrl; return nextPageUrl;
} }
@ -141,9 +163,36 @@ public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
throw new ExtractionException(new IllegalArgumentException("Page url is empty or null")); throw new ExtractionException(new IllegalArgumentException("Page url is empty or null"));
} }
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); // see computeInitialTracksAndNextPageUrl
String nextPageUrl = SoundcloudParsingHelper.getStreamsFromApiMinItems(15, collector, pageUrl); final int lengthFirstPartOfUrl = ("https://api-v2.soundcloud.com/tracks?client_id="
+ SoundcloudParsingHelper.clientId()
+ "&ids=").length();
final int lengthOfEveryStream = 11;
return new InfoItemsPage<>(collector, nextPageUrl); String currentPageUrl, nextUrl;
int lengthMaxStreams = lengthFirstPartOfUrl + lengthOfEveryStream * streamsPerRequestedPage;
if (pageUrl.length() <= lengthMaxStreams) {
currentPageUrl = pageUrl; // fetch every remaining video, there are less than the max
nextUrl = ""; // afterwards the list is complete
} else {
currentPageUrl = pageUrl.substring(0, lengthMaxStreams);
nextUrl = pageUrl.substring(0, lengthFirstPartOfUrl) + pageUrl.substring(lengthMaxStreams);
}
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
String response = NewPipe.getDownloader().get(currentPageUrl, getExtractorLocalization()).responseBody();
try {
JsonArray tracks = JsonParser.array().from(response);
for (Object track : tracks) {
if (track instanceof JsonObject) {
collector.commit(new SoundcloudStreamInfoItemExtractor((JsonObject) track));
}
}
} catch (JsonParserException e) {
throw new ParsingException("Could not parse json response", e);
}
return new InfoItemsPage<>(collector, nextUrl);
} }
} }

View File

@ -4,6 +4,7 @@ import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException; import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
@ -13,9 +14,15 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -24,6 +31,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.annotation.Nonnull;
public class SoundcloudStreamExtractor extends StreamExtractor { public class SoundcloudStreamExtractor extends StreamExtractor {
private JsonObject track; private JsonObject track;
@ -55,14 +64,14 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@Nonnull @Nonnull
@Override @Override
public String getTextualUploadDate() { public String getTextualUploadDate() throws ParsingException {
return track.getString("created_at"); return track.getString("created_at").replace("T"," ").replace("Z", "");
} }
@Nonnull @Nonnull
@Override @Override
public DateWrapper getUploadDate() throws ParsingException { public DateWrapper getUploadDate() throws ParsingException {
return new DateWrapper(SoundcloudParsingHelper.parseDate(getTextualUploadDate())); return new DateWrapper(SoundcloudParsingHelper.parseDate(track.getString("created_at")));
} }
@Nonnull @Nonnull
@ -146,24 +155,13 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
List<AudioStream> audioStreams = new ArrayList<>(); List<AudioStream> audioStreams = new ArrayList<>();
Downloader dl = NewPipe.getDownloader(); Downloader dl = NewPipe.getDownloader();
String apiUrl = "https://api-v2.soundcloud.com/tracks/" + urlEncode(getId())
+ "?client_id=" + urlEncode(SoundcloudParsingHelper.clientId());
String response = dl.get(apiUrl, getExtractorLocalization()).responseBody();
JsonObject responseObject;
try {
responseObject = JsonParser.object().from(response);
} catch (JsonParserException e) {
throw new ParsingException("Could not parse json response", e);
}
// Streams can be streamable and downloadable - or explicitly not. // Streams can be streamable and downloadable - or explicitly not.
// For playing the track, it is only necessary to have a streamable track. // For playing the track, it is only necessary to have a streamable track.
// If this is not the case, this track might not be published yet. // If this is not the case, this track might not be published yet.
if (!responseObject.getBoolean("streamable")) return audioStreams; if (!track.getBoolean("streamable")) return audioStreams;
try { try {
JsonArray transcodings = responseObject.getObject("media").getArray("transcodings"); JsonArray transcodings = track.getObject("media").getArray("transcodings");
// get information about what stream formats are available // get information about what stream formats are available
for (Object transcoding : transcodings) { for (Object transcoding : transcodings) {

View File

@ -36,7 +36,7 @@ public class SoundcloudSubscriptionExtractor extends SubscriptionExtractor {
throw new InvalidSourceException(e); throw new InvalidSourceException(e);
} }
String apiUrl = "https://api.soundcloud.com/users/" + id + "/followings" String apiUrl = "https://api-v2.soundcloud.com/users/" + id + "/followings"
+ "?client_id=" + SoundcloudParsingHelper.clientId() + "?client_id=" + SoundcloudParsingHelper.clientId()
+ "&limit=200"; + "&limit=200";
ChannelInfoItemsCollector collector = new ChannelInfoItemsCollector(service.getServiceId()); ChannelInfoItemsCollector collector = new ChannelInfoItemsCollector(service.getServiceId());

View File

@ -24,6 +24,8 @@ public class SoundcloudParsingHelperTest {
public void resolveUrlWithEmbedPlayerTest() throws Exception { public void resolveUrlWithEmbedPlayerTest() throws Exception {
Assert.assertEquals("https://soundcloud.com/trapcity", SoundcloudParsingHelper.resolveUrlWithEmbedPlayer("https://api.soundcloud.com/users/26057743")); Assert.assertEquals("https://soundcloud.com/trapcity", SoundcloudParsingHelper.resolveUrlWithEmbedPlayer("https://api.soundcloud.com/users/26057743"));
Assert.assertEquals("https://soundcloud.com/nocopyrightsounds", SoundcloudParsingHelper.resolveUrlWithEmbedPlayer("https://api.soundcloud.com/users/16069159")); Assert.assertEquals("https://soundcloud.com/nocopyrightsounds", SoundcloudParsingHelper.resolveUrlWithEmbedPlayer("https://api.soundcloud.com/users/16069159"));
Assert.assertEquals("https://soundcloud.com/trapcity", SoundcloudParsingHelper.resolveUrlWithEmbedPlayer("https://api-v2.soundcloud.com/users/26057743"));
Assert.assertEquals("https://soundcloud.com/nocopyrightsounds", SoundcloudParsingHelper.resolveUrlWithEmbedPlayer("https://api-v2.soundcloud.com/users/16069159"));
} }
@Test @Test

View File

@ -1,8 +1,6 @@
package org.schabi.newpipe.extractor.services.soundcloud; package org.schabi.newpipe.extractor.services.soundcloud;
import org.hamcrest.CoreMatchers;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl; import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListExtractor;
@ -11,6 +9,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest; import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
@ -70,14 +69,8 @@ public class SoundcloudPlaylistExtractorTest {
} }
@Test @Test
public void testMoreRelatedItems() { public void testMoreRelatedItems() throws Exception {
try {
defaultTestMoreItems(extractor); defaultTestMoreItems(extractor);
} catch (Throwable ignored) {
return;
}
fail("This playlist doesn't have more items, it should throw an error");
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -89,17 +82,17 @@ public class SoundcloudPlaylistExtractorTest {
assertIsSecureUrl(extractor.getThumbnailUrl()); assertIsSecureUrl(extractor.getThumbnailUrl());
} }
@Ignore
@Test @Test
public void testBannerUrl() { public void testBannerUrl() {
assertIsSecureUrl(extractor.getBannerUrl()); // SoundCloud playlists do not have a banner
assertNull(extractor.getBannerUrl());
} }
@Test @Test
public void testUploaderUrl() { public void testUploaderUrl() {
final String uploaderUrl = extractor.getUploaderUrl(); final String uploaderUrl = extractor.getUploaderUrl();
assertIsSecureUrl(uploaderUrl); assertIsSecureUrl(uploaderUrl);
assertTrue(uploaderUrl, uploaderUrl.contains("liluzivert")); assertThat(uploaderUrl, containsString("liluzivert"));
} }
@Test @Test
@ -114,7 +107,7 @@ public class SoundcloudPlaylistExtractorTest {
@Test @Test
public void testStreamCount() { public void testStreamCount() {
assertTrue("Error in the streams count", extractor.getStreamCount() >= 10); assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 10);
} }
} }
@ -181,17 +174,17 @@ public class SoundcloudPlaylistExtractorTest {
assertIsSecureUrl(extractor.getThumbnailUrl()); assertIsSecureUrl(extractor.getThumbnailUrl());
} }
@Ignore("not implemented")
@Test @Test
public void testBannerUrl() { public void testBannerUrl() {
assertIsSecureUrl(extractor.getBannerUrl()); // SoundCloud playlists do not have a banner
assertNull(extractor.getBannerUrl());
} }
@Test @Test
public void testUploaderUrl() { public void testUploaderUrl() {
final String uploaderUrl = extractor.getUploaderUrl(); final String uploaderUrl = extractor.getUploaderUrl();
assertIsSecureUrl(uploaderUrl); assertIsSecureUrl(uploaderUrl);
assertThat(uploaderUrl, CoreMatchers.containsString("micky96")); assertThat(uploaderUrl, containsString("micky96"));
} }
@Test @Test
@ -206,7 +199,7 @@ public class SoundcloudPlaylistExtractorTest {
@Test @Test
public void testStreamCount() { public void testStreamCount() {
assertTrue("Error in the streams count", extractor.getStreamCount() >= 10); assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 10);
} }
} }
@ -227,7 +220,7 @@ public class SoundcloudPlaylistExtractorTest {
@Test @Test
public void testGetPageInNewExtractor() throws Exception { public void testGetPageInNewExtractor() throws Exception {
final PlaylistExtractor newExtractor = SoundCloud.getPlaylistExtractor(extractor.getUrl()); PlaylistExtractor newExtractor = SoundCloud.getPlaylistExtractor(extractor.getUrl());
defaultTestGetPageInNewExtractor(extractor, newExtractor); defaultTestGetPageInNewExtractor(extractor, newExtractor);
} }
@ -264,14 +257,11 @@ public class SoundcloudPlaylistExtractorTest {
// ListExtractor // ListExtractor
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Ignore
@Test @Test
public void testRelatedItems() throws Exception { public void testRelatedItems() throws Exception {
defaultTestRelatedItems(extractor); defaultTestRelatedItems(extractor);
} }
//TODO: FUCK THIS: This triggers a 500 at sever
@Ignore
@Test @Test
public void testMoreRelatedItems() throws Exception { public void testMoreRelatedItems() throws Exception {
ListExtractor.InfoItemsPage<StreamInfoItem> currentPage = defaultTestMoreItems(extractor); ListExtractor.InfoItemsPage<StreamInfoItem> currentPage = defaultTestMoreItems(extractor);
@ -286,16 +276,15 @@ public class SoundcloudPlaylistExtractorTest {
// PlaylistExtractor // PlaylistExtractor
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Ignore
@Test @Test
public void testThumbnailUrl() { public void testThumbnailUrl() {
assertIsSecureUrl(extractor.getThumbnailUrl()); assertIsSecureUrl(extractor.getThumbnailUrl());
} }
@Ignore
@Test @Test
public void testBannerUrl() { public void testBannerUrl() {
assertIsSecureUrl(extractor.getBannerUrl()); // SoundCloud playlists do not have a banner
assertNull(extractor.getBannerUrl());
} }
@Test @Test
@ -317,7 +306,105 @@ public class SoundcloudPlaylistExtractorTest {
@Test @Test
public void testStreamCount() { public void testStreamCount() {
assertTrue("Error in the streams count", extractor.getStreamCount() >= 3900); assertTrue("Stream count does not fit: " + extractor.getStreamCount(), extractor.getStreamCount() >= 370);
}
}
public static class SmallPlaylist implements BasePlaylistExtractorTest {
private static SoundcloudPlaylistExtractor extractor;
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (SoundcloudPlaylistExtractor) SoundCloud
.getPlaylistExtractor("https://soundcloud.com/breezy-123/sets/empty-playlist?test=123");
extractor.fetchPage();
}
/*//////////////////////////////////////////////////////////////////////////
// Extractor
//////////////////////////////////////////////////////////////////////////*/
@Test
public void testServiceId() {
assertEquals(SoundCloud.getServiceId(), extractor.getServiceId());
}
@Test
public void testName() {
assertEquals("EMPTY PLAYLIST", extractor.getName());
}
@Test
public void testId() {
assertEquals("23483459", extractor.getId());
}
@Test
public void testUrl() throws Exception {
assertEquals("https://soundcloud.com/breezy-123/sets/empty-playlist", extractor.getUrl());
}
@Test
public void testOriginalUrl() throws Exception {
assertEquals("https://soundcloud.com/breezy-123/sets/empty-playlist?test=123", extractor.getOriginalUrl());
}
/*//////////////////////////////////////////////////////////////////////////
// ListExtractor
//////////////////////////////////////////////////////////////////////////*/
@Test
public void testRelatedItems() throws Exception {
defaultTestRelatedItems(extractor);
}
@Test
public void testMoreRelatedItems() throws Exception {
try {
defaultTestMoreItems(extractor);
} catch (Throwable ignored) {
return;
}
fail("This playlist doesn't have more items, it should throw an error");
}
/*//////////////////////////////////////////////////////////////////////////
// PlaylistExtractor
//////////////////////////////////////////////////////////////////////////*/
@Test
public void testThumbnailUrl() {
assertIsSecureUrl(extractor.getThumbnailUrl());
}
@Test
public void testBannerUrl() {
// SoundCloud playlists do not have a banner
assertNull(extractor.getBannerUrl());
}
@Test
public void testUploaderUrl() {
final String uploaderUrl = extractor.getUploaderUrl();
assertIsSecureUrl(uploaderUrl);
assertThat(uploaderUrl, containsString("breezy-123"));
}
@Test
public void testUploaderName() {
assertEquals("breezy-123", extractor.getUploaderName());
}
@Test
public void testUploaderAvatarUrl() {
assertIsSecureUrl(extractor.getUploaderAvatarUrl());
}
@Test
public void testStreamCount() {
assertEquals(2, extractor.getStreamCount());
} }
} }
} }

View File

@ -74,7 +74,7 @@ public class SoundcloudStreamExtractorDefaultTest {
@Test @Test
public void testGetTextualUploadDate() throws ParsingException { public void testGetTextualUploadDate() throws ParsingException {
Assert.assertEquals("2016/07/31 18:18:07 +0000", extractor.getTextualUploadDate()); Assert.assertEquals("2016-07-31 18:18:07", extractor.getTextualUploadDate());
} }
@Test @Test