diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java index 55d760cb0..1cc29eb63 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubePlaylistExtractor.java @@ -225,12 +225,17 @@ public class YoutubePlaylistExtractor extends PlaylistExtractor { return null; } - final String continuation = contents.getObject(contents.size() - 1) - .getObject("continuationItemRenderer") - .getObject("continuationEndpoint") - .getObject("continuationCommand") - .getString("token"); - return new Page("https://www.youtube.com/browse_ajax?continuation=" + continuation); + final JsonObject lastElement = contents.getObject(contents.size() - 1); + if (lastElement.has("continuationItemRenderer")) { + final String continuation = lastElement + .getObject("continuationItemRenderer") + .getObject("continuationEndpoint") + .getObject("continuationCommand") + .getString("token"); + return new Page("https://www.youtube.com/browse_ajax?continuation=" + continuation); + } else { + return null; + } } private void collectStreamsFrom(final StreamInfoItemsCollector collector, final JsonArray videos) { diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistExtractorTest.java index a67884dd1..4a68f4a0a 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistExtractorTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistExtractorTest.java @@ -14,6 +14,7 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; 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.youtube.YoutubePlaylistExtractorTest.ContinuationsTests; import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.HugePlaylist; import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.LearningPlaylist; import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTest.NotAvailable; @@ -21,7 +22,9 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubePlaylistExtractorTes import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubePlaylistExtractor; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; import static org.schabi.newpipe.extractor.ServiceList.YouTube; @@ -31,7 +34,8 @@ import static org.schabi.newpipe.extractor.services.DefaultTests.*; * Test for {@link YoutubePlaylistExtractor} */ @RunWith(Suite.class) -@SuiteClasses({NotAvailable.class, TimelessPopHits.class, HugePlaylist.class, LearningPlaylist.class}) +@SuiteClasses({NotAvailable.class, TimelessPopHits.class, HugePlaylist.class, + LearningPlaylist.class, ContinuationsTests.class}) public class YoutubePlaylistExtractorTest { public static class NotAvailable { @@ -123,7 +127,7 @@ public class YoutubePlaylistExtractorTest { @Ignore @Test - public void testBannerUrl() throws Exception { + public void testBannerUrl() { final String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); assertTrue(bannerUrl, bannerUrl.contains("yt")); @@ -236,7 +240,7 @@ public class YoutubePlaylistExtractorTest { @Ignore @Test - public void testBannerUrl() throws Exception { + public void testBannerUrl() { final String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); assertTrue(bannerUrl, bannerUrl.contains("yt")); @@ -333,7 +337,7 @@ public class YoutubePlaylistExtractorTest { @Ignore @Test - public void testBannerUrl() throws Exception { + public void testBannerUrl() { final String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); assertTrue(bannerUrl, bannerUrl.contains("yt")); @@ -361,4 +365,34 @@ public class YoutubePlaylistExtractorTest { assertTrue("Error in the streams count", extractor.getStreamCount() > 40); } } + + public static class ContinuationsTests { + + @BeforeClass + public static void setUp() { + NewPipe.init(DownloaderTestImpl.getInstance()); + } + + @Test + public void testNoContinuations() throws Exception { + final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube + .getPlaylistExtractor( + "https://www.youtube.com/playlist?list=PLXJg25X-OulsVsnvZ7RVtSDW-id9_RzAO"); + extractor.fetchPage(); + + assertNoMoreItems(extractor); + } + + @Test + public void testOnlySingleContinuation() throws Exception { + final YoutubePlaylistExtractor extractor = (YoutubePlaylistExtractor) YouTube + .getPlaylistExtractor( + "https://www.youtube.com/playlist?list=PLjgwFL8urN2DFRuRkFTkmtHjyoNWHHdZX"); + extractor.fetchPage(); + + final ListExtractor.InfoItemsPage page = defaultTestMoreItems( + extractor); + assertFalse("More items available when it shouldn't", page.hasNextPage()); + } + } }