diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java index fb851de639..27d50ca879 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java @@ -5,13 +5,12 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertContains; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; +import static org.schabi.newpipe.extractor.ExtractorAsserts.assertTabsContained; import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestGetPageInNewExtractor; -import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestMoreItems; -import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRelatedItems; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -20,14 +19,18 @@ import org.schabi.newpipe.extractor.ExtractorAsserts; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.channel.ChannelExtractor; +import org.schabi.newpipe.extractor.channel.tabs.ChannelTabExtractor; +import org.schabi.newpipe.extractor.channel.tabs.ChannelTabs; import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; -import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.linkhandler.ReadyChannelTabListLinkHandler; import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest; import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor; +import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelTabPlaylistExtractor; import java.io.IOException; +import java.util.List; /** * Test for {@link ChannelExtractor} @@ -132,19 +135,19 @@ public void accountTerminatedCopyrightFetch() throws Exception { } - public static class NotSupported { + static class SystemTopic { @BeforeAll - public static void setUp() throws IOException { + static void setUp() throws IOException { YoutubeTestsUtils.ensureStateless(); - NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "notSupported")); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "systemTopic")); } @Test - void noVideoTab() throws Exception { + void noSupportedTab() throws Exception { final ChannelExtractor extractor = YouTube.getChannelExtractor("https://invidio.us/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"); extractor.fetchPage(); - assertThrows(ContentNotSupportedException.class, extractor::getInitialPage); + assertTrue(extractor.getTabs().isEmpty()); } } @@ -189,20 +192,6 @@ public void testOriginalUrl() throws ParsingException { assertEquals("http://www.youtube.com/@Gronkh", extractor.getOriginalUrl()); } - /*////////////////////////////////////////////////////////////////////////// - // ListExtractor - //////////////////////////////////////////////////////////////////////////*/ - - @Test - public void testRelatedItems() throws Exception { - defaultTestRelatedItems(extractor); - } - - @Test - public void testMoreRelatedItems() throws Exception { - defaultTestMoreItems(extractor); - } - /*////////////////////////////////////////////////////////////////////////// // ChannelExtractor //////////////////////////////////////////////////////////////////////////*/ @@ -216,14 +205,14 @@ public void testDescription() throws Exception { public void testAvatarUrl() throws Exception { String avatarUrl = extractor.getAvatarUrl(); assertIsSecureUrl(avatarUrl); - ExtractorAsserts.assertContains("yt3", avatarUrl); + assertContains("yt3", avatarUrl); } @Test public void testBannerUrl() throws Exception { String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); - ExtractorAsserts.assertContains("yt3", bannerUrl); + assertContains("yt3", bannerUrl); } @Test @@ -241,6 +230,18 @@ public void testVerified() throws Exception { assertTrue(extractor.isVerified()); } + @Test + @Override + public void testTabs() throws Exception { + assertTabsContained(extractor.getTabs(), ChannelTabs.VIDEOS, + ChannelTabs.LIVESTREAMS, ChannelTabs.PLAYLISTS, ChannelTabs.CHANNELS); + } + + @Test + @Override + public void testTags() throws Exception { + assertTrue(extractor.getTags().contains("gronkh")); + } } // Youtube RED/Premium ad blocking test @@ -286,23 +287,9 @@ public void testOriginalUrl() throws ParsingException { } /*////////////////////////////////////////////////////////////////////////// - // ListExtractor + // ChannelExtractor //////////////////////////////////////////////////////////////////////////*/ - @Test - public void testRelatedItems() throws Exception { - defaultTestRelatedItems(extractor); - } - - @Test - public void testMoreRelatedItems() throws Exception { - defaultTestMoreItems(extractor); - } - - /*////////////////////////////////////////////////////////////////////////// - // ChannelExtractor - //////////////////////////////////////////////////////////////////////////*/ - @Test public void testDescription() throws Exception { assertContains("Our World is Amazing. \n\nQuestions? Ideas? Tweet me:", extractor.getDescription()); @@ -312,14 +299,14 @@ public void testDescription() throws Exception { public void testAvatarUrl() throws Exception { String avatarUrl = extractor.getAvatarUrl(); assertIsSecureUrl(avatarUrl); - ExtractorAsserts.assertContains("yt3", avatarUrl); + assertContains("yt3", avatarUrl); } @Test public void testBannerUrl() throws Exception { String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); - ExtractorAsserts.assertContains("yt3", bannerUrl); + assertContains("yt3", bannerUrl); } @Test @@ -337,6 +324,19 @@ public void testVerified() throws Exception { assertTrue(extractor.isVerified()); } + @Test + @Override + public void testTabs() throws Exception { + assertTabsContained(extractor.getTabs(), ChannelTabs.VIDEOS, ChannelTabs.LIVESTREAMS, + ChannelTabs.SHORTS, ChannelTabs.PLAYLISTS, ChannelTabs.CHANNELS); + } + + @Test + @Override + public void testTags() throws Exception { + assertTrue(extractor.getTags().containsAll(List.of("questions", "education", + "learning", "schools", "Science"))); + } } public static class Kurzgesagt implements BaseChannelExtractorTest { @@ -381,23 +381,9 @@ public void testOriginalUrl() throws ParsingException { } /*////////////////////////////////////////////////////////////////////////// - // ListExtractor + // ChannelExtractor //////////////////////////////////////////////////////////////////////////*/ - @Test - public void testRelatedItems() throws Exception { - defaultTestRelatedItems(extractor); - } - - @Test - public void testMoreRelatedItems() throws Exception { - defaultTestMoreItems(extractor); - } - - /*////////////////////////////////////////////////////////////////////////// - // ChannelExtractor - //////////////////////////////////////////////////////////////////////////*/ - @Test public void testDescription() throws Exception { ExtractorAsserts.assertContains("science", extractor.getDescription()); @@ -410,14 +396,14 @@ public void testDescription() throws Exception { public void testAvatarUrl() throws Exception { String avatarUrl = extractor.getAvatarUrl(); assertIsSecureUrl(avatarUrl); - ExtractorAsserts.assertContains("yt3", avatarUrl); + assertContains("yt3", avatarUrl); } @Test public void testBannerUrl() throws Exception { String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); - ExtractorAsserts.assertContains("yt3", bannerUrl); + assertContains("yt3", bannerUrl); } @Test @@ -434,25 +420,46 @@ public void testSubscriberCount() throws Exception { public void testVerified() throws Exception { assertTrue(extractor.isVerified()); } + + @Test + @Override + public void testTabs() throws Exception { + assertTabsContained(extractor.getTabs(), ChannelTabs.VIDEOS, ChannelTabs.SHORTS, + ChannelTabs.PLAYLISTS, ChannelTabs.CHANNELS); + } + + @Test + @Override + public void testTags() throws Exception { + assertTrue(extractor.getTags().containsAll(List.of("universe", "Science", + "black hole", "humanism", "evolution"))); + } } public static class KurzgesagtAdditional { private static YoutubeChannelExtractor extractor; + private static ChannelTabExtractor tabExtractor; @BeforeAll public static void setUp() throws Exception { // Test is not deterministic, mocks can't be used NewPipe.init(DownloaderTestImpl.getInstance()); - extractor = (YoutubeChannelExtractor) YouTube - .getChannelExtractor("https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q"); + extractor = (YoutubeChannelExtractor) YouTube.getChannelExtractor( + "https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q"); extractor.fetchPage(); + + tabExtractor = YouTube.getChannelTabExtractor(extractor.getTabs().get(0)); + tabExtractor.fetchPage(); } @Test public void testGetPageInNewExtractor() throws Exception { final ChannelExtractor newExtractor = YouTube.getChannelExtractor(extractor.getUrl()); - defaultTestGetPageInNewExtractor(extractor, newExtractor); + newExtractor.fetchPage(); + final ChannelTabExtractor newTabExtractor = YouTube.getChannelTabExtractor( + newExtractor.getTabs().get(0)); + defaultTestGetPageInNewExtractor(tabExtractor, newTabExtractor); } } @@ -498,40 +505,26 @@ public void testOriginalUrl() throws ParsingException { } /*////////////////////////////////////////////////////////////////////////// - // ListExtractor + // ChannelExtractor //////////////////////////////////////////////////////////////////////////*/ - @Test - public void testRelatedItems() throws Exception { - defaultTestRelatedItems(extractor); - } - - @Test - public void testMoreRelatedItems() throws Exception { - defaultTestMoreItems(extractor); - } - - /*////////////////////////////////////////////////////////////////////////// - // ChannelExtractor - //////////////////////////////////////////////////////////////////////////*/ - @Test public void testDescription() throws Exception { - ExtractorAsserts.assertContains("In a world where", extractor.getDescription()); + assertContains("In a world where", extractor.getDescription()); } @Test public void testAvatarUrl() throws Exception { String avatarUrl = extractor.getAvatarUrl(); assertIsSecureUrl(avatarUrl); - ExtractorAsserts.assertContains("yt3", avatarUrl); + assertContains("yt3", avatarUrl); } @Test public void testBannerUrl() throws Exception { String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); - ExtractorAsserts.assertContains("yt3", bannerUrl); + assertContains("yt3", bannerUrl); } @Test @@ -548,6 +541,20 @@ public void testSubscriberCount() throws Exception { public void testVerified() throws Exception { assertTrue(extractor.isVerified()); } + + @Test + @Override + public void testTabs() throws Exception { + assertTabsContained(extractor.getTabs(), ChannelTabs.VIDEOS, ChannelTabs.PLAYLISTS, + ChannelTabs.CHANNELS); + } + + @Test + @Override + public void testTags() throws Exception { + assertTrue(extractor.getTags().containsAll(List.of("critical thinking", + "visual effects", "VFX", "sci-fi", "humor"))); + } } public static class RandomChannel implements BaseChannelExtractorTest { @@ -592,46 +599,26 @@ public void testOriginalUrl() throws ParsingException { } /*////////////////////////////////////////////////////////////////////////// - // ListExtractor + // ChannelExtractor //////////////////////////////////////////////////////////////////////////*/ - @Test - public void testRelatedItems() throws Exception { - defaultTestRelatedItems(extractor); - } - - @Test - public void testMoreRelatedItems() { - try { - defaultTestMoreItems(extractor); - } catch (final Throwable ignored) { - return; - } - - fail("This channel doesn't have more items, it should throw an error"); - } - - /*////////////////////////////////////////////////////////////////////////// - // ChannelExtractor - //////////////////////////////////////////////////////////////////////////*/ - @Test public void testDescription() throws Exception { - ExtractorAsserts.assertContains("Hey there iu will upoload a load of pranks onto this channel", extractor.getDescription()); + assertContains("Hey there iu will upoload a load of pranks onto this channel", extractor.getDescription()); } @Test public void testAvatarUrl() throws Exception { String avatarUrl = extractor.getAvatarUrl(); assertIsSecureUrl(avatarUrl); - ExtractorAsserts.assertContains("yt3", avatarUrl); + assertContains("yt3", avatarUrl); } @Test public void testBannerUrl() throws Exception { String bannerUrl = extractor.getBannerUrl(); assertIsSecureUrl(bannerUrl); - ExtractorAsserts.assertContains("yt3", bannerUrl); + assertContains("yt3", bannerUrl); } @Test @@ -648,6 +635,19 @@ public void testSubscriberCount() throws Exception { public void testVerified() throws Exception { assertFalse(extractor.isVerified()); } + + @Test + @Override + public void testTabs() throws Exception { + assertTabsContained(extractor.getTabs(), ChannelTabs.VIDEOS, ChannelTabs.PLAYLISTS, + ChannelTabs.CHANNELS); + } + + @Test + @Override + public void testTags() throws Exception { + assertTrue(extractor.getTags().isEmpty()); + } } public static class CarouselHeader implements BaseChannelExtractorTest { @@ -692,31 +692,18 @@ public void testOriginalUrl() throws ParsingException { } /*////////////////////////////////////////////////////////////////////////// - // ListExtractor + // ChannelExtractor //////////////////////////////////////////////////////////////////////////*/ - @Test - public void testRelatedItems() throws Exception { - defaultTestRelatedItems(extractor); - } - - @Test - public void testMoreRelatedItems() throws Exception { - defaultTestMoreItems(extractor); + @Override + public void testDescription() throws ParsingException { } - /*////////////////////////////////////////////////////////////////////////// - // ChannelExtractor - //////////////////////////////////////////////////////////////////////////*/ - @Override - public void testDescription() { - } - @Test public void testAvatarUrl() throws Exception { String avatarUrl = extractor.getAvatarUrl(); assertIsSecureUrl(avatarUrl); - ExtractorAsserts.assertContains("yt3", avatarUrl); + assertContains("yt3", avatarUrl); } @Test @@ -739,5 +726,145 @@ public void testSubscriberCount() throws Exception { public void testVerified() throws Exception { assertTrue(extractor.isVerified()); } + + @Test + @Override + public void testTabs() throws Exception { + assertTabsContained(extractor.getTabs(), ChannelTabs.VIDEOS, ChannelTabs.SHORTS, + ChannelTabs.LIVESTREAMS, ChannelTabs.PLAYLISTS, ChannelTabs.CHANNELS); + } + + @Test + @Override + public void testTags() throws Exception { + assertTrue(extractor.getTags().containsAll(List.of("coachella", "music", "california", + "festival", "arts"))); + } + } + + /** + * A YouTube channel which is age-restricted and requires login to view its contents on a + * channel page. + * + *

+ * Note that age-restrictions on channels may not apply for countries, so check that the + * channel is age-restricted in the network you use to update the test's mocks before updating + * them. + *

+ */ + static class AgeRestrictedChannel implements BaseChannelExtractorTest { + + private static ChannelExtractor extractor; + + @BeforeAll + static void setUp() throws Exception { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "ageRestricted")); + extractor = YouTube.getChannelExtractor( + "https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig"); + extractor.fetchPage(); + } + + @Test + @Override + public void testDescription() throws Exception { + // Description cannot be extracted from age-restricted channels + assertTrue(isNullOrEmpty(extractor.getDescription())); + } + + @Test + @Override + public void testAvatarUrl() throws Exception { + final String avatarUrl = extractor.getAvatarUrl(); + assertIsSecureUrl(avatarUrl); + assertContains("yt3", avatarUrl); + } + + @Test + @Override + public void testBannerUrl() throws Exception { + // Banners cannot be extracted from age-restricted channels + assertTrue(isNullOrEmpty(extractor.getBannerUrl())); + } + + @Test + @Override + public void testFeedUrl() throws Exception { + assertEquals( + "https://www.youtube.com/feeds/videos.xml?channel_id=UCbfnHqxXs_K3kvaH-WlNlig", + extractor.getFeedUrl()); + } + + @Test + @Override + public void testSubscriberCount() throws Exception { + // Subscriber count cannot be extracted from age-restricted channels + assertEquals(ChannelExtractor.UNKNOWN_SUBSCRIBER_COUNT, extractor.getSubscriberCount()); + } + + @Test + @Override + public void testServiceId() throws Exception { + assertEquals(YouTube.getServiceId(), extractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals("Laphroaig Whisky", extractor.getName()); + } + + @Test + @Override + public void testId() throws Exception { + assertEquals("UCbfnHqxXs_K3kvaH-WlNlig", extractor.getId()); + } + + @Test + @Override + public void testUrl() throws Exception { + assertEquals("https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig", + extractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig", + extractor.getOriginalUrl()); + } + + @Test + @Override + public void testVerified() throws Exception { + // Verification status cannot be extracted from age-restricted channels + assertFalse(extractor.isVerified()); + } + + @Test + @Override + public void testTabs() throws Exception { + // Channel tabs which may be available and which will be extracted from channel system + // uploads playlists + assertTabsContained(extractor.getTabs(), + ChannelTabs.VIDEOS, ChannelTabs.SHORTS, ChannelTabs.LIVESTREAMS); + + // Check if all tabs are not classic tabs, so that link handlers are of the appropriate + // type and build YoutubeChannelTabPlaylistExtractor instances + assertTrue(extractor.getTabs() + .stream() + .allMatch(linkHandler -> + linkHandler.getClass() == ReadyChannelTabListLinkHandler.class + && ((ReadyChannelTabListLinkHandler) linkHandler) + .getChannelTabExtractor(extractor.getService()) + .getClass() == YoutubeChannelTabPlaylistExtractor.class)); + } + + @Test + @Override + public void testTags() throws Exception { + // Tags cannot be extracted from age-restricted channels + assertTrue(extractor.getTags().isEmpty()); + } } } diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java index 533ad7ee94..cc5fbc7edf 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelLocalizationTest.java @@ -6,9 +6,11 @@ import org.junit.jupiter.api.Test; import org.schabi.newpipe.downloader.DownloaderFactory; +import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.channel.ChannelExtractor; +import org.schabi.newpipe.extractor.channel.tabs.ChannelTabExtractor; import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.stream.StreamInfoItem; @@ -18,6 +20,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * A class that tests multiple channels and ranges of "time ago". @@ -47,18 +50,27 @@ private void testLocalizationsFor(final String channelUrl) throws Exception { for (Localization currentLocalization : supportedLocalizations) { if (DEBUG) System.out.println("Testing localization = " + currentLocalization); - ListExtractor.InfoItemsPage itemsPage; + ListExtractor.InfoItemsPage itemsPage; try { final ChannelExtractor extractor = YouTube.getChannelExtractor(channelUrl); extractor.forceLocalization(currentLocalization); extractor.fetchPage(); - itemsPage = defaultTestRelatedItems(extractor); + + // Use Videos tab only + final ChannelTabExtractor tabExtractor = YouTube.getChannelTabExtractor( + extractor.getTabs().get(0)); + tabExtractor.fetchPage(); + itemsPage = defaultTestRelatedItems(tabExtractor); } catch (final Throwable e) { System.out.println("[!] " + currentLocalization + " → failed"); throw e; } - final List items = itemsPage.getItems(); + final List items = itemsPage.getItems() + .stream() + .filter(StreamInfoItem.class::isInstance) + .map(StreamInfoItem.class::cast) + .collect(Collectors.toUnmodifiableList()); for (int i = 0; i < items.size(); i++) { final StreamInfoItem item = items.get(i); @@ -73,7 +85,7 @@ private void testLocalizationsFor(final String channelUrl) throws Exception { } if (DEBUG) System.out.println(debugMessage + "\n"); } - results.put(currentLocalization, itemsPage.getItems()); + results.put(currentLocalization, items); if (DEBUG) System.out.println("\n===============================\n"); } diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelTabExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelTabExtractorTest.java new file mode 100644 index 0000000000..05ef8eff36 --- /dev/null +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelTabExtractorTest.java @@ -0,0 +1,397 @@ +package org.schabi.newpipe.extractor.services.youtube; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.schabi.newpipe.downloader.DownloaderFactory; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.channel.ChannelExtractor; +import org.schabi.newpipe.extractor.channel.tabs.ChannelTabExtractor; +import org.schabi.newpipe.extractor.channel.tabs.ChannelTabs; +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.services.BaseListExtractorTest; +import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelTabExtractor; + +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.schabi.newpipe.extractor.ServiceList.YouTube; +import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestMoreItems; +import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestRelatedItems; + +class YoutubeChannelTabExtractorTest { + + private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + + "services/youtube/extractor/channelTabs/"; + + static class Videos implements BaseListExtractorTest { + private static YoutubeChannelTabExtractor extractor; + + @BeforeAll + static void setUp() throws IOException, ExtractionException { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "videos")); + extractor = (YoutubeChannelTabExtractor) YouTube.getChannelTabExtractorFromId( + "user/creativecommons", ChannelTabs.VIDEOS); + extractor.fetchPage(); + } + + @Test + @Override + public void testServiceId() { + assertEquals(YouTube.getServiceId(), extractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals(ChannelTabs.VIDEOS, extractor.getName()); + } + + @Test + @Override + public void testId() throws ParsingException { + assertEquals("UCTwECeGqMZee77BjdoYtI2Q", extractor.getId()); + } + + @Test + @Override + public void testUrl() throws ParsingException { + assertEquals("https://www.youtube.com/channel/UCTwECeGqMZee77BjdoYtI2Q/videos", + extractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/user/creativecommons/videos", + extractor.getOriginalUrl()); + } + + @Test + @Override + public void testRelatedItems() throws Exception { + defaultTestRelatedItems(extractor); + } + + @Test + @Override + public void testMoreRelatedItems() throws Exception { + defaultTestMoreItems(extractor); + } + } + + static class Playlists implements BaseListExtractorTest { + private static YoutubeChannelTabExtractor extractor; + + @BeforeAll + static void setUp() throws IOException, ExtractionException { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "playlists")); + extractor = (YoutubeChannelTabExtractor) YouTube.getChannelTabExtractorFromId( + "@EEVblog", ChannelTabs.PLAYLISTS); + extractor.fetchPage(); + } + + @Test + @Override + public void testServiceId() { + assertEquals(YouTube.getServiceId(), extractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals(ChannelTabs.PLAYLISTS, extractor.getName()); + } + + @Test + @Override + public void testId() throws ParsingException { + assertEquals("UC2DjFE7Xf11URZqWBigcVOQ", extractor.getId()); + } + + @Test + @Override + public void testUrl() throws ParsingException { + assertEquals("https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ/playlists", + extractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/@EEVblog/playlists", + extractor.getOriginalUrl()); + } + + @Test + @Override + public void testRelatedItems() throws Exception { + defaultTestRelatedItems(extractor); + } + + @Test + @Override + public void testMoreRelatedItems() throws Exception { + defaultTestMoreItems(extractor); + } + } + + static class Channels implements BaseListExtractorTest { + private static YoutubeChannelTabExtractor extractor; + + @BeforeAll + static void setUp() throws IOException, ExtractionException { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "channels")); + extractor = (YoutubeChannelTabExtractor) YouTube.getChannelTabExtractorFromId( + "channel/UC2DjFE7Xf11URZqWBigcVOQ", ChannelTabs.CHANNELS); + extractor.fetchPage(); + } + + @Test + @Override + public void testServiceId() { + assertEquals(YouTube.getServiceId(), extractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals(ChannelTabs.CHANNELS, extractor.getName()); + } + + @Test + @Override + public void testId() throws ParsingException { + assertEquals("UC2DjFE7Xf11URZqWBigcVOQ", extractor.getId()); + } + + @Test + @Override + public void testUrl() throws ParsingException { + assertEquals("https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ/channels", + extractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ/channels", + extractor.getUrl()); + } + + @Test + @Override + public void testRelatedItems() throws Exception { + defaultTestRelatedItems(extractor); + } + + @Test + @Override + public void testMoreRelatedItems() throws Exception { + defaultTestMoreItems(extractor); + } + } + + static class Livestreams implements BaseListExtractorTest { + private static YoutubeChannelTabExtractor extractor; + + @BeforeAll + static void setUp() throws IOException, ExtractionException { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "livestreams")); + extractor = (YoutubeChannelTabExtractor) YouTube.getChannelTabExtractorFromId( + "c/JeffGeerling", ChannelTabs.LIVESTREAMS); + extractor.fetchPage(); + } + + @Test + @Override + public void testServiceId() { + assertEquals(YouTube.getServiceId(), extractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals(ChannelTabs.LIVESTREAMS, extractor.getName()); + } + + @Test + @Override + public void testId() throws ParsingException { + assertEquals("UCR-DXc1voovS8nhAvccRZhg", extractor.getId()); + } + + @Test + @Override + public void testUrl() throws ParsingException { + assertEquals("https://www.youtube.com/channel/UCR-DXc1voovS8nhAvccRZhg/streams", + extractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/c/JeffGeerling/streams", + extractor.getOriginalUrl()); + } + + @Test + @Override + public void testRelatedItems() throws Exception { + defaultTestRelatedItems(extractor); + } + + @Test + @Override + public void testMoreRelatedItems() throws Exception { + defaultTestMoreItems(extractor); + } + } + + static class Shorts implements BaseListExtractorTest { + private static YoutubeChannelTabExtractor extractor; + + @BeforeAll + static void setUp() throws IOException, ExtractionException { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "shorts")); + extractor = (YoutubeChannelTabExtractor) YouTube.getChannelTabExtractorFromId( + "channel/UCh8gHdtzO2tXd593_bjErWg", ChannelTabs.SHORTS); + extractor.fetchPage(); + } + + @Test + @Override + public void testServiceId() { + assertEquals(YouTube.getServiceId(), extractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals(ChannelTabs.SHORTS, extractor.getName()); + } + + @Test + @Override + public void testId() throws ParsingException { + assertEquals("UCh8gHdtzO2tXd593_bjErWg", extractor.getId()); + } + + @Test + @Override + public void testUrl() throws ParsingException { + assertEquals("https://www.youtube.com/channel/UCh8gHdtzO2tXd593_bjErWg/shorts", + extractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/channel/UCh8gHdtzO2tXd593_bjErWg/shorts", + extractor.getOriginalUrl()); + } + + @Test + @Override + public void testRelatedItems() throws Exception { + defaultTestRelatedItems(extractor); + } + + @Test + @Override + public void testMoreRelatedItems() throws Exception { + defaultTestMoreItems(extractor); + } + } + + static class AgeRestrictedTabs implements BaseListExtractorTest { + private static ChannelTabExtractor videosTabExtractor; + private static ChannelTabExtractor shortsTabExtractor; + + @BeforeAll + static void setUp() throws IOException, ExtractionException { + YoutubeTestsUtils.ensureStateless(); + NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "ageRestricted")); + final ChannelExtractor extractor = YouTube.getChannelExtractor( + "https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig"); + extractor.fetchPage(); + + // Fetching the tabs individually would use the standard tabs without fallback to + // system playlists for stream tabs, we need to fetch the channel extractor to get the + // channel playlist tabs + // TODO: implement system playlists fallback in YoutubeChannelTabExtractor for stream + // tabs + final List tabs = extractor.getTabs(); + videosTabExtractor = YouTube.getChannelTabExtractor(tabs.get(0)); + videosTabExtractor.fetchPage(); + shortsTabExtractor = YouTube.getChannelTabExtractor(tabs.get(1)); + shortsTabExtractor.fetchPage(); + } + + @Test + @Override + public void testServiceId() { + assertEquals(YouTube.getServiceId(), videosTabExtractor.getServiceId()); + assertEquals(YouTube.getServiceId(), shortsTabExtractor.getServiceId()); + } + + @Test + @Override + public void testName() throws Exception { + assertEquals(ChannelTabs.VIDEOS, videosTabExtractor.getName()); + assertEquals(ChannelTabs.SHORTS, shortsTabExtractor.getName()); + } + + @Test + @Override + public void testId() throws ParsingException { + assertEquals("UCbfnHqxXs_K3kvaH-WlNlig", videosTabExtractor.getId()); + assertEquals("UCbfnHqxXs_K3kvaH-WlNlig", shortsTabExtractor.getId()); + } + + @Test + @Override + public void testUrl() throws ParsingException { + assertEquals("https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig/videos", + videosTabExtractor.getUrl()); + assertEquals("https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig/shorts", + shortsTabExtractor.getUrl()); + } + + @Test + @Override + public void testOriginalUrl() throws Exception { + assertEquals("https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig/videos", + videosTabExtractor.getOriginalUrl()); + assertEquals("https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig/shorts", + shortsTabExtractor.getOriginalUrl()); + } + + @Test + @Override + public void testRelatedItems() throws Exception { + defaultTestRelatedItems(videosTabExtractor); + // No shorts on this channel, the channel tab playlist extractor should return no + // streams + assertTrue(shortsTabExtractor.getInitialPage().getItems().isEmpty()); + } + + @Test + @Override + public void testMoreRelatedItems() throws Exception { + defaultTestMoreItems(videosTabExtractor); + // No shorts on this channel, the channel tab playlist extractor should return no + // streams + assertFalse(shortsTabExtractor.getInitialPage().hasNextPage()); + } + } +}