Skip to content

Commit 6ac89c6

Browse files
committed
test(youtube): cover podcasts tab parsing and lockup paths
1 parent c9d1f44 commit 6ac89c6

File tree

4 files changed

+256
-0
lines changed

4 files changed

+256
-0
lines changed

extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YouTubeChannelTabExtractorTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,56 @@ public void testMoreRelatedItems() throws Exception {
187187
defaultTestMoreItems(extractor);
188188
}
189189
}
190+
191+
public static class Podcasts {
192+
private static final String CHANNEL_ID = "UCpodcastTabFixture0000000";
193+
private static YoutubeChannelTabExtractor extractor;
194+
195+
@BeforeAll
196+
public static void setUp() throws ExtractionException {
197+
extractor = (YoutubeChannelTabExtractor) YouTube.getChannelTabExtractorFromId(
198+
CHANNEL_ID, ChannelTabs.PODCASTS);
199+
}
200+
201+
@Test
202+
public void testServiceId() {
203+
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
204+
}
205+
206+
@Test
207+
public void testTab() {
208+
assertEquals(ChannelTabs.PODCASTS, extractor.getTab());
209+
}
210+
211+
@Test
212+
public void testId() throws ParsingException {
213+
assertEquals(CHANNEL_ID, extractor.getId());
214+
}
215+
216+
@Test
217+
public void testUrl() throws ParsingException {
218+
assertEquals(
219+
"https://www.youtube.com/channel/" + CHANNEL_ID + "/podcasts",
220+
extractor.getUrl());
221+
}
222+
223+
@Test
224+
public void testPodcastsParams() throws Exception {
225+
final java.lang.reflect.Method method = YoutubeChannelTabExtractor.class
226+
.getDeclaredMethod("getChannelTabsParameters");
227+
method.setAccessible(true);
228+
final String params = (String) method.invoke(extractor);
229+
assertEquals("Eghwb2RjYXN0c_IGBQoDugEA", params);
230+
}
231+
232+
@Test
233+
public void testNormalizeTabUrl() throws Exception {
234+
final java.lang.reflect.Method method = YoutubeChannelTabExtractor.class
235+
.getDeclaredMethod("normalizeTabUrl", String.class);
236+
method.setAccessible(true);
237+
final String normalized = (String) method.invoke(null,
238+
"/channel/UCpodcastTabFixture0000000/podcasts?view=57#fragment");
239+
assertEquals("/channel/UCpodcastTabFixture0000000/podcasts", normalized);
240+
}
241+
}
190242
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.schabi.newpipe.extractor.services.youtube;
2+
3+
import com.grack.nanojson.JsonObject;
4+
import com.grack.nanojson.JsonParser;
5+
import org.junit.jupiter.api.Test;
6+
import org.schabi.newpipe.extractor.linkhandler.ChannelTabs;
7+
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
8+
import org.schabi.newpipe.extractor.search.filter.FilterItem;
9+
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor;
10+
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelLinkHandlerFactory;
11+
12+
import java.lang.reflect.Field;
13+
import java.util.List;
14+
15+
import static org.junit.jupiter.api.Assertions.assertTrue;
16+
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
17+
18+
public class YoutubeChannelExtractorTabsParsingTest {
19+
private static final String CHANNEL_ID = "channel/UCpodcastTabFixture0000000";
20+
21+
@Test
22+
public void testPodcastsTabIsRecognizedFromTabUrlSuffix() throws Exception {
23+
final YoutubeChannelExtractor extractor = new YoutubeChannelExtractor(
24+
YouTube,
25+
YoutubeChannelLinkHandlerFactory.getInstance().fromId(CHANNEL_ID));
26+
27+
setPrivateField(extractor, "redirectedChannelId", CHANNEL_ID);
28+
setPrivateField(extractor, "jsonResponse", buildTabsResponse());
29+
30+
final List<ListLinkHandler> tabs = extractor.getTabs();
31+
assertTrue(tabs.stream().anyMatch(tab -> tab.getUrl().endsWith("/podcasts")));
32+
assertTrue(tabs.stream().anyMatch(tab -> {
33+
final List<FilterItem> filters = tab.getContentFilters();
34+
return !filters.isEmpty() && ChannelTabs.PODCASTS.equals(filters.get(0).getName());
35+
}));
36+
}
37+
38+
private static JsonObject buildTabsResponse() throws Exception {
39+
return JsonParser.object().from("{\"contents\":{\"twoColumnBrowseResultsRenderer\":{\"tabs\":["
40+
+ "{\"tabRenderer\":{\"endpoint\":{\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/"
41+
+ CHANNEL_ID
42+
+ "/podcasts?view=57\"}}}}},"
43+
+ "{\"tabRenderer\":{\"endpoint\":{\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/"
44+
+ CHANNEL_ID
45+
+ "/playlists\"}}}}}"
46+
+ "]}}}");
47+
}
48+
49+
private static void setPrivateField(final Object target,
50+
final String fieldName,
51+
final Object value) throws Exception {
52+
final Field field = target.getClass().getDeclaredField(fieldName);
53+
field.setAccessible(true);
54+
field.set(target, value);
55+
}
56+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package org.schabi.newpipe.extractor.services.youtube;
2+
3+
import com.grack.nanojson.JsonObject;
4+
import com.grack.nanojson.JsonParser;
5+
import org.junit.jupiter.api.Test;
6+
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
7+
import org.schabi.newpipe.extractor.linkhandler.ChannelTabs;
8+
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelTabExtractor;
9+
10+
import java.lang.reflect.Method;
11+
import java.util.Arrays;
12+
13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
15+
16+
public class YoutubeChannelTabExtractorLockupParsingTest {
17+
18+
@Test
19+
public void testCollectItemHandlesRichItemLockupViewModel() throws Exception {
20+
final YoutubeChannelTabExtractor extractor = (YoutubeChannelTabExtractor) YouTube
21+
.getChannelTabExtractorFromId("UCpodcastTabFixture0000000", ChannelTabs.PODCASTS);
22+
23+
final Method method = YoutubeChannelTabExtractor.class.getDeclaredMethod(
24+
"collectItem",
25+
MultiInfoItemsCollector.class,
26+
JsonObject.class,
27+
java.util.List.class);
28+
method.setAccessible(true);
29+
30+
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(YouTube.getServiceId());
31+
method.invoke(extractor,
32+
collector,
33+
buildRichItemLockupContent(),
34+
Arrays.asList("Memoire Vive", "https://www.youtube.com/channel/UCTFUnkRlNBCHwE0oD_6PM7g"));
35+
36+
assertEquals(1, collector.getItems().size());
37+
}
38+
39+
@Test
40+
public void testCollectItemHandlesVideoLockupViewModel() throws Exception {
41+
final YoutubeChannelTabExtractor extractor = (YoutubeChannelTabExtractor) YouTube
42+
.getChannelTabExtractorFromId("UCpodcastTabFixture0000000", ChannelTabs.PODCASTS);
43+
44+
final Method method = YoutubeChannelTabExtractor.class.getDeclaredMethod(
45+
"collectItem",
46+
MultiInfoItemsCollector.class,
47+
JsonObject.class,
48+
java.util.List.class);
49+
method.setAccessible(true);
50+
51+
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(YouTube.getServiceId());
52+
method.invoke(extractor,
53+
collector,
54+
buildVideoLockupContent(),
55+
Arrays.asList("Memoire Vive", "https://www.youtube.com/channel/UCTFUnkRlNBCHwE0oD_6PM7g"));
56+
57+
assertEquals(1, collector.getItems().size());
58+
}
59+
60+
@Test
61+
public void testCollectItemHandlesNestedRichItemShelfContent() throws Exception {
62+
final YoutubeChannelTabExtractor extractor = (YoutubeChannelTabExtractor) YouTube
63+
.getChannelTabExtractorFromId("UCpodcastTabFixture0000000", ChannelTabs.PODCASTS);
64+
65+
final Method method = YoutubeChannelTabExtractor.class.getDeclaredMethod(
66+
"collectItem",
67+
MultiInfoItemsCollector.class,
68+
JsonObject.class,
69+
java.util.List.class);
70+
method.setAccessible(true);
71+
72+
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(YouTube.getServiceId());
73+
method.invoke(extractor,
74+
collector,
75+
buildNestedRichItemShelfLockupContent(),
76+
Arrays.asList("Memoire Vive", "https://www.youtube.com/channel/UCTFUnkRlNBCHwE0oD_6PM7g"));
77+
78+
assertEquals(1, collector.getItems().size());
79+
}
80+
81+
private static JsonObject buildRichItemLockupContent() throws Exception {
82+
return JsonParser.object().from("{\"richItemRenderer\":{\"content\":{\"lockupViewModel\":{"
83+
+ "\"contentType\":\"LOCKUP_CONTENT_TYPE_PODCAST\","
84+
+ "\"contentId\":\"PL1234567890ABCDE\","
85+
+ "\"contentImage\":{\"collectionThumbnailViewModel\":{\"primaryThumbnail\":{"
86+
+ "\"thumbnailViewModel\":{\"image\":{\"sources\":[{\"url\":\"https://i.ytimg.com/vi/test/default.jpg\"}]},"
87+
+ "\"overlays\":[{\"thumbnailOverlayBadgeViewModel\":{\"thumbnailBadges\":[{\"thumbnailBadgeViewModel\":{\"text\":\"12\"}}]}}]"
88+
+ "}}}},"
89+
+ "\"metadata\":{\"lockupMetadataViewModel\":{"
90+
+ "\"title\":{\"content\":\"Test podcast\"},"
91+
+ "\"metadata\":{\"contentMetadataViewModel\":{\"metadataRows\":[{}]}}"
92+
+ "}}"
93+
+ "}}}}");
94+
}
95+
96+
private static JsonObject buildVideoLockupContent() throws Exception {
97+
return JsonParser.object().from("{\"lockupViewModel\":{"
98+
+ "\"contentType\":\"LOCKUP_CONTENT_TYPE_VIDEO\","
99+
+ "\"contentId\":\"dQw4w9WgXcQ\","
100+
+ "\"contentImage\":{\"thumbnailViewModel\":{"
101+
+ "\"image\":{\"sources\":[{\"url\":\"https://i.ytimg.com/vi/dQw4w9WgXcQ/default.jpg\"}]},"
102+
+ "\"overlays\":[]"
103+
+ "}},"
104+
+ "\"metadata\":{\"lockupMetadataViewModel\":{"
105+
+ "\"title\":{\"content\":\"Episode 1\"},"
106+
+ "\"metadata\":{\"contentMetadataViewModel\":{\"metadataRows\":[]}}"
107+
+ "}}"
108+
+ "}}");
109+
}
110+
111+
private static JsonObject buildNestedRichItemShelfLockupContent() throws Exception {
112+
return JsonParser.object().from("{\"richItemRenderer\":{\"content\":{\"shelfRenderer\":{"
113+
+ "\"content\":{\"horizontalListRenderer\":{\"items\":[{\"lockupViewModel\":{"
114+
+ "\"contentType\":\"LOCKUP_CONTENT_TYPE_PODCAST\","
115+
+ "\"contentId\":\"PL1234567890ABCDE\","
116+
+ "\"contentImage\":{\"collectionThumbnailViewModel\":{\"primaryThumbnail\":{"
117+
+ "\"thumbnailViewModel\":{\"image\":{\"sources\":[{\"url\":\"https://i.ytimg.com/vi/test/default.jpg\"}]},"
118+
+ "\"overlays\":[{\"thumbnailOverlayBadgeViewModel\":{\"thumbnailBadges\":[{\"thumbnailBadgeViewModel\":{\"text\":\"12\"}}]}}]"
119+
+ "}}}},"
120+
+ "\"metadata\":{\"lockupMetadataViewModel\":{"
121+
+ "\"title\":{\"content\":\"Test podcast\"},"
122+
+ "\"metadata\":{\"contentMetadataViewModel\":{\"metadataRows\":[{}]}}"
123+
+ "}}"
124+
+ "}}]}}}}}}}");
125+
}
126+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.schabi.newpipe.extractor.services.youtube;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
5+
import org.schabi.newpipe.extractor.linkhandler.ChannelTabs;
6+
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeChannelTabLinkHandlerFactory;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertThrows;
10+
11+
public class YoutubeChannelTabLinkHandlerFactoryTest {
12+
@Test
13+
public void testPodcastsTabSuffix() throws ParsingException {
14+
assertEquals("/podcasts", YoutubeChannelTabLinkHandlerFactory.getUrlSuffix(ChannelTabs.PODCASTS));
15+
}
16+
17+
@Test
18+
public void testUnsupportedTabSuffix() {
19+
assertThrows(ParsingException.class,
20+
() -> YoutubeChannelTabLinkHandlerFactory.getUrlSuffix("unsupported-tab"));
21+
}
22+
}

0 commit comments

Comments
 (0)