Skip to content

Commit cc9ade9

Browse files
committed
[MediaCCC] Allow obtaining channel tab extractor from scratch
i.e. without needing to pass through the conference/channel extractor This was needed because clients (like NewPipe) might rely on link handlers to hold as little data as possible, since they might be kept around for long or passed around in system transactions, so this commit allows obtaining a standalone link handler that does not hold a JsonObject within itself.
1 parent 3402cdb commit cc9ade9

4 files changed

Lines changed: 114 additions & 78 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
2020
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
2121
import org.schabi.newpipe.extractor.search.SearchExtractor;
22+
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCChannelTabExtractor;
2223
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceExtractor;
2324
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceKiosk;
2425
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCLiveStreamExtractor;
@@ -57,7 +58,9 @@ public ListLinkHandlerFactory getChannelLHFactory() {
5758

5859
@Override
5960
public ListLinkHandlerFactory getChannelTabLHFactory() {
60-
return null;
61+
// there is just one channel tab in MediaCCC, the one containing conferences, so there is
62+
// no need for a specific channel tab link handler, but we can just use the channel one
63+
return MediaCCCConferenceLinkHandlerFactory.getInstance();
6164
}
6265

6366
@Override
@@ -86,17 +89,13 @@ public ChannelExtractor getChannelExtractor(final ListLinkHandler linkHandler) {
8689
@Override
8790
public ChannelTabExtractor getChannelTabExtractor(final ListLinkHandler linkHandler) {
8891
if (linkHandler instanceof ReadyChannelTabListLinkHandler) {
92+
// conference data has already been fetched, let the ReadyChannelTabListLinkHandler
93+
// create a MediaCCCChannelTabExtractor with that data
8994
return ((ReadyChannelTabListLinkHandler) linkHandler).getChannelTabExtractor(this);
95+
} else {
96+
// conference data has not been fetched yet, so pass null instead
97+
return new MediaCCCChannelTabExtractor(this, linkHandler, null);
9098
}
91-
92-
/*
93-
Channel tab extractors are only supported in conferences and should only come from a
94-
ReadyChannelTabListLinkHandler instance with a ChannelTabExtractorBuilder instance of the
95-
conferences extractor
96-
97-
If that's not the case, return null in this case, so no channel tabs support
98-
*/
99-
return null;
10099
}
101100

102101
@Override
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.schabi.newpipe.extractor.services.media_ccc.extractors;
2+
3+
import com.grack.nanojson.JsonObject;
4+
5+
import org.schabi.newpipe.extractor.InfoItem;
6+
import org.schabi.newpipe.extractor.ListExtractor;
7+
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
8+
import org.schabi.newpipe.extractor.Page;
9+
import org.schabi.newpipe.extractor.StreamingService;
10+
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabExtractor;
11+
import org.schabi.newpipe.extractor.downloader.Downloader;
12+
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
13+
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
14+
import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCStreamInfoItemExtractor;
15+
16+
import java.io.IOException;
17+
import java.util.Objects;
18+
19+
import javax.annotation.Nonnull;
20+
import javax.annotation.Nullable;
21+
22+
/**
23+
* MediaCCC does not really have channel tabs, but rather a list of videos for each conference,
24+
* so this class just acts as a videos channel tab extractor.
25+
*/
26+
public class MediaCCCChannelTabExtractor extends ChannelTabExtractor {
27+
@Nullable
28+
private JsonObject conferenceData;
29+
30+
/**
31+
* @param conferenceData will be not-null if conference data has already been fetched by
32+
* {@link MediaCCCConferenceExtractor}. Otherwise, if this parameter is
33+
* {@code null}, conference data will be fetched anew.
34+
*/
35+
public MediaCCCChannelTabExtractor(final StreamingService service,
36+
final ListLinkHandler linkHandler,
37+
@Nullable final JsonObject conferenceData) {
38+
super(service, linkHandler);
39+
this.conferenceData = conferenceData;
40+
}
41+
42+
@Override
43+
public void onFetchPage(@Nonnull final Downloader downloader)
44+
throws ExtractionException, IOException {
45+
if (conferenceData == null) {
46+
// only fetch conference data if we don't have it already
47+
conferenceData = MediaCCCConferenceExtractor.fetchConferenceData(downloader, getId());
48+
}
49+
}
50+
51+
@Nonnull
52+
@Override
53+
public ListExtractor.InfoItemsPage<InfoItem> getInitialPage() {
54+
final MultiInfoItemsCollector collector =
55+
new MultiInfoItemsCollector(getServiceId());
56+
Objects.requireNonNull(conferenceData) // will surely be != null after onFetchPage
57+
.getArray("events")
58+
.stream()
59+
.filter(JsonObject.class::isInstance)
60+
.map(JsonObject.class::cast)
61+
.forEach(event -> collector.commit(new MediaCCCStreamInfoItemExtractor(event)));
62+
return new ListExtractor.InfoItemsPage<>(collector, null);
63+
}
64+
65+
@Override
66+
public ListExtractor.InfoItemsPage<InfoItem> getPage(final Page page) {
67+
return ListExtractor.InfoItemsPage.emptyPage();
68+
}
69+
}
Lines changed: 19 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
package org.schabi.newpipe.extractor.services.media_ccc.extractors;
22

3+
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getImageListFromLogoImageUrl;
4+
35
import com.grack.nanojson.JsonObject;
46
import com.grack.nanojson.JsonParser;
57
import com.grack.nanojson.JsonParserException;
68

7-
import org.schabi.newpipe.extractor.InfoItem;
8-
import org.schabi.newpipe.extractor.ListExtractor;
9-
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
109
import org.schabi.newpipe.extractor.Image;
11-
import org.schabi.newpipe.extractor.Page;
1210
import org.schabi.newpipe.extractor.StreamingService;
1311
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
14-
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabExtractor;
1512
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabs;
1613
import org.schabi.newpipe.extractor.downloader.Downloader;
1714
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
1815
import org.schabi.newpipe.extractor.exceptions.ParsingException;
1916
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
2017
import org.schabi.newpipe.extractor.linkhandler.ReadyChannelTabListLinkHandler;
21-
import org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems.MediaCCCStreamInfoItemExtractor;
2218
import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory;
2319

2420
import java.io.IOException;
@@ -27,8 +23,6 @@
2723

2824
import javax.annotation.Nonnull;
2925

30-
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getImageListFromLogoImageUrl;
31-
3226
public class MediaCCCConferenceExtractor extends ChannelExtractor {
3327
private JsonObject conferenceData;
3428

@@ -37,6 +31,19 @@ public MediaCCCConferenceExtractor(final StreamingService service,
3731
super(service, linkHandler);
3832
}
3933

34+
static JsonObject fetchConferenceData(@Nonnull final Downloader downloader,
35+
@Nonnull final String conferenceId)
36+
throws IOException, ExtractionException {
37+
final String conferenceUrl
38+
= MediaCCCConferenceLinkHandlerFactory.CONFERENCE_API_ENDPOINT + conferenceId;
39+
try {
40+
return JsonParser.object().from(downloader.get(conferenceUrl).responseBody());
41+
} catch (final JsonParserException jpe) {
42+
throw new ExtractionException("Could not parse json returned by URL: " + conferenceUrl);
43+
}
44+
}
45+
46+
4047
@Nonnull
4148
@Override
4249
public List<Image> getAvatars() {
@@ -88,76 +95,20 @@ public boolean isVerified() {
8895
@Nonnull
8996
@Override
9097
public List<ListLinkHandler> getTabs() throws ParsingException {
91-
return List.of(new ReadyChannelTabListLinkHandler(getUrl(), getId(),
92-
ChannelTabs.VIDEOS, new VideosTabExtractorBuilder(conferenceData)));
98+
return List.of(new ReadyChannelTabListLinkHandler(getUrl(), getId(), ChannelTabs.VIDEOS,
99+
(service, linkHandler) ->
100+
new MediaCCCChannelTabExtractor(service, linkHandler, conferenceData)));
93101
}
94102

95103
@Override
96104
public void onFetchPage(@Nonnull final Downloader downloader)
97105
throws IOException, ExtractionException {
98-
final String conferenceUrl
99-
= MediaCCCConferenceLinkHandlerFactory.CONFERENCE_API_ENDPOINT + getId();
100-
try {
101-
conferenceData = JsonParser.object().from(downloader.get(conferenceUrl).responseBody());
102-
} catch (final JsonParserException jpe) {
103-
throw new ExtractionException("Could not parse json returned by URL: " + conferenceUrl);
104-
}
106+
conferenceData = fetchConferenceData(downloader, getId());
105107
}
106108

107109
@Nonnull
108110
@Override
109111
public String getName() throws ParsingException {
110112
return conferenceData.getString("title");
111113
}
112-
113-
private static final class VideosTabExtractorBuilder
114-
implements ReadyChannelTabListLinkHandler.ChannelTabExtractorBuilder {
115-
116-
private final JsonObject conferenceData;
117-
118-
VideosTabExtractorBuilder(final JsonObject conferenceData) {
119-
this.conferenceData = conferenceData;
120-
}
121-
122-
@Nonnull
123-
@Override
124-
public ChannelTabExtractor build(@Nonnull final StreamingService service,
125-
@Nonnull final ListLinkHandler linkHandler) {
126-
return new VideosChannelTabExtractor(service, linkHandler, conferenceData);
127-
}
128-
}
129-
130-
private static final class VideosChannelTabExtractor extends ChannelTabExtractor {
131-
private final JsonObject conferenceData;
132-
133-
VideosChannelTabExtractor(final StreamingService service,
134-
final ListLinkHandler linkHandler,
135-
final JsonObject conferenceData) {
136-
super(service, linkHandler);
137-
this.conferenceData = conferenceData;
138-
}
139-
140-
@Override
141-
public void onFetchPage(@Nonnull final Downloader downloader) {
142-
// Nothing to do here, as data was already fetched
143-
}
144-
145-
@Nonnull
146-
@Override
147-
public ListExtractor.InfoItemsPage<InfoItem> getInitialPage() {
148-
final MultiInfoItemsCollector collector =
149-
new MultiInfoItemsCollector(getServiceId());
150-
conferenceData.getArray("events")
151-
.stream()
152-
.filter(JsonObject.class::isInstance)
153-
.map(JsonObject.class::cast)
154-
.forEach(event -> collector.commit(new MediaCCCStreamInfoItemExtractor(event)));
155-
return new InfoItemsPage<>(collector, null);
156-
}
157-
158-
@Override
159-
public InfoItemsPage<InfoItem> getPage(final Page page) {
160-
return InfoItemsPage.emptyPage();
161-
}
162-
}
163114
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCConferenceLinkHandlerFactory.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package org.schabi.newpipe.extractor.services.media_ccc.linkHandler;
22

3+
import org.schabi.newpipe.extractor.channel.tabs.ChannelTabs;
34
import org.schabi.newpipe.extractor.exceptions.ParsingException;
45
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
56
import org.schabi.newpipe.extractor.utils.Parser;
67

78
import java.util.List;
89

10+
/**
11+
* Since MediaCCC does not really have channel tabs (i.e. it only has one single "tab" with videos),
12+
* this link handler acts both as the channel link handler and the channel tab link handler. That's
13+
* why {@link #getAvailableContentFilter()} has been overridden.
14+
*/
915
public final class MediaCCCConferenceLinkHandlerFactory extends ListLinkHandlerFactory {
1016

1117
private static final MediaCCCConferenceLinkHandlerFactory INSTANCE
@@ -46,4 +52,15 @@ public boolean onAcceptUrl(final String url) {
4652
return false;
4753
}
4854
}
55+
56+
/**
57+
* @see MediaCCCConferenceLinkHandlerFactory
58+
* @return MediaCCC's only channel "tab", i.e. {@link ChannelTabs#VIDEOS}
59+
*/
60+
@Override
61+
public String[] getAvailableContentFilter() {
62+
return new String[]{
63+
ChannelTabs.VIDEOS,
64+
};
65+
}
4966
}

0 commit comments

Comments
 (0)