Skip to content

Commit b4e1913

Browse files
committed
[media.ccc.de] Play live streams
1 parent 80f4d42 commit b4e1913

8 files changed

Lines changed: 423 additions & 1957 deletions

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
2121
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
2222

23-
import java.io.IOException;
24-
2523
import static java.util.Arrays.asList;
2624
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
2725
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
@@ -58,6 +56,9 @@ public SearchQueryHandlerFactory getSearchQHFactory() {
5856

5957
@Override
6058
public StreamExtractor getStreamExtractor(final LinkHandler linkHandler) {
59+
if (MediaCCCParsingHelper.isLiveStreamId(linkHandler.getId())) {
60+
return new MediaCCCLiveStreamExtractor(this, linkHandler);
61+
}
6162
return new MediaCCCStreamExtractor(this, linkHandler);
6263
}
6364

@@ -108,9 +109,9 @@ public KioskExtractor createNewKiosk(final StreamingService streamingService,
108109
final String url, final String kioskId)
109110
throws ExtractionException {
110111
return new MediaCCCLiveStreamKiosk(MediaCCCService.this,
111-
new MediaCCCLiveStreamListLinkHandlerFactory().fromUrl(url), kioskId);
112+
new MediaCCCLiveListLinkHandlerFactory().fromUrl(url), kioskId);
112113
}
113-
}, new MediaCCCLiveStreamListLinkHandlerFactory(), "live");
114+
}, new MediaCCCLiveListLinkHandlerFactory(), "live");
114115

115116
list.setDefaultKiosk("recent");
116117
} catch (Exception e) {
Lines changed: 246 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,300 @@
11
package org.schabi.newpipe.extractor.services.media_ccc.extractors;
22

3+
import com.grack.nanojson.JsonArray;
34
import com.grack.nanojson.JsonObject;
5+
import org.schabi.newpipe.extractor.MediaFormat;
6+
import org.schabi.newpipe.extractor.MetaInfo;
7+
import org.schabi.newpipe.extractor.StreamingService;
8+
import org.schabi.newpipe.extractor.downloader.Downloader;
9+
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
410
import org.schabi.newpipe.extractor.exceptions.ParsingException;
11+
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
512
import org.schabi.newpipe.extractor.localization.DateWrapper;
6-
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
7-
import org.schabi.newpipe.extractor.stream.StreamType;
13+
import org.schabi.newpipe.extractor.stream.*;
814

15+
import javax.annotation.Nonnull;
916
import javax.annotation.Nullable;
17+
import java.io.IOException;
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.Locale;
1022

11-
public class MediaCCCLiveStreamExtractor implements StreamInfoItemExtractor {
23+
public class MediaCCCLiveStreamExtractor extends StreamExtractor {
24+
private JsonArray doc = null;
25+
private JsonObject conference = null;
26+
private String group = "";
27+
private JsonObject room = null;
1228

13-
private final JsonObject conferenceInfo;
14-
private final String group;
15-
private final JsonObject roomInfo;
29+
public MediaCCCLiveStreamExtractor(StreamingService service, LinkHandler linkHandler) {
30+
super(service, linkHandler);
31+
}
1632

17-
public MediaCCCLiveStreamExtractor(JsonObject conferenceInfo, String group, JsonObject roomInfo) {
18-
this.conferenceInfo = conferenceInfo;
19-
this.group = group;
20-
this.roomInfo = roomInfo;
33+
@Override
34+
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
35+
doc = MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization());
36+
// find correct room
37+
for (int c = 0; c < doc.size(); c++) {
38+
final JsonObject conference = doc.getObject(c);
39+
final JsonArray groups = conference.getArray("groups");
40+
for (int g = 0; g < groups.size(); g++) {
41+
final String group = groups.getObject(g).getString("group");
42+
final JsonArray rooms = groups.getObject(g).getArray("rooms");
43+
for (int r = 0; r < rooms.size(); r++) {
44+
final JsonObject room = rooms.getObject(r);
45+
if (getId().equals(conference.getString("slug") + "/" + room.getString("slug"))) {
46+
this.conference = conference;
47+
this.group = group;
48+
this.room = room;
49+
return;
50+
}
51+
}
52+
}
53+
}
54+
throw new ExtractionException("Could not find room matching id: '" + getId() + "'");
2155
}
2256

57+
@Nonnull
2358
@Override
2459
public String getName() throws ParsingException {
25-
return roomInfo.getString("schedulename");
60+
return room.getString("display");
61+
}
62+
63+
@Nullable
64+
@Override
65+
public String getTextualUploadDate() throws ParsingException {
66+
return null;
2667
}
2768

69+
@Nullable
2870
@Override
29-
public String getUrl() throws ParsingException {
30-
return roomInfo.getString("link");
71+
public DateWrapper getUploadDate() throws ParsingException {
72+
return null;
3173
}
3274

75+
@Nonnull
3376
@Override
3477
public String getThumbnailUrl() throws ParsingException {
35-
return roomInfo.getString("thumb");
78+
return room.getString("thumb");
3679
}
3780

81+
@Nonnull
3882
@Override
39-
public StreamType getStreamType() throws ParsingException {
40-
boolean isVideo = false;
41-
for (Object stream : roomInfo.getArray("streams")) {
42-
if ("video".equals(((JsonObject) stream).getString("type"))) {
43-
isVideo = true;
44-
break;
45-
}
46-
}
47-
return isVideo ? StreamType.LIVE_STREAM : StreamType.AUDIO_LIVE_STREAM;
83+
public Description getDescription() throws ParsingException {
84+
return new Description(conference.getString("description") + " - " + group, Description.PLAIN_TEXT);
4885
}
4986

5087
@Override
51-
public boolean isAd() throws ParsingException {
52-
return false;
88+
public int getAgeLimit() {
89+
return 0;
90+
}
91+
92+
@Override
93+
public long getLength() {
94+
return 0;
5395
}
5496

5597
@Override
56-
public long getDuration() throws ParsingException {
98+
public long getTimeStamp() throws ParsingException {
5799
return 0;
58100
}
59101

60102
@Override
61-
public long getViewCount() throws ParsingException {
103+
public long getViewCount() {
62104
return -1;
63105
}
64106

65107
@Override
66-
public String getUploaderName() throws ParsingException {
67-
return conferenceInfo.getString("conference");
108+
public long getLikeCount() {
109+
return -1;
110+
}
111+
112+
@Override
113+
public long getDislikeCount() {
114+
return -1;
68115
}
69116

117+
@Nonnull
70118
@Override
71119
public String getUploaderUrl() throws ParsingException {
72-
return "https://media.ccc.de/c/" + conferenceInfo.getString("slug");
120+
return "https://streaming.media.ccc.de/" + conference.getString("slug");
121+
}
122+
123+
@Nonnull
124+
@Override
125+
public String getUploaderName() throws ParsingException {
126+
return conference.getString("conference");
127+
}
128+
129+
@Nonnull
130+
@Override
131+
public String getUploaderAvatarUrl() {
132+
return "";
133+
}
134+
135+
@Nonnull
136+
@Override
137+
public String getSubChannelUrl() {
138+
return "";
139+
}
140+
141+
@Nonnull
142+
@Override
143+
public String getSubChannelName() {
144+
return "";
145+
}
146+
147+
@Nonnull
148+
@Override
149+
public String getSubChannelAvatarUrl() {
150+
return "";
151+
}
152+
153+
@Nonnull
154+
@Override
155+
public String getDashMpdUrl() throws ParsingException {
156+
return "";
157+
}
158+
159+
@Nonnull
160+
@Override
161+
public String getHlsUrl() {
162+
// TODO: There are multiple HLS streams.
163+
// Make getHlsUrl() and getDashMpdUrl() return lists of VideoStreams, so the user can choose a resolution.
164+
for (int s = 0; s < room.getArray("streams").size(); s++) {
165+
final JsonObject stream = room.getArray("streams").getObject(s);
166+
if (stream.getString("type").equals("video")) {
167+
final String resolution = stream.getArray("videoSize").getInt(0) + "x"
168+
+ stream.getArray("videoSize").getInt(1);
169+
if (stream.has("hls")) {
170+
return stream.getObject("urls").getObject("hls").getString("url");
171+
}
172+
}
173+
}
174+
return "";
175+
}
176+
177+
@Override
178+
public List<AudioStream> getAudioStreams() throws IOException, ExtractionException {
179+
final List<AudioStream> audioStreams = new ArrayList<>();
180+
for (int s = 0; s < room.getArray("streams").size(); s++) {
181+
final JsonObject stream = room.getArray("streams").getObject(s);
182+
if (stream.getString("type").equals("audio")) {
183+
for (final String type :stream.getObject("urls").keySet()) {
184+
final JsonObject url = stream.getObject("urls").getObject(type);
185+
audioStreams.add(new AudioStream(url.getString("url"), MediaFormat.getFromSuffix(type), -1));
186+
}
187+
}
188+
}
189+
return audioStreams;
190+
}
191+
192+
@Override
193+
public List<VideoStream> getVideoStreams() throws IOException, ExtractionException {
194+
final List<VideoStream> videoStreams = new ArrayList<>();
195+
for (int s = 0; s < room.getArray("streams").size(); s++) {
196+
final JsonObject stream = room.getArray("streams").getObject(s);
197+
if (stream.getString("type").equals("video")) {
198+
final String resolution = stream.getArray("videoSize").getInt(0) + "x"
199+
+ stream.getArray("videoSize").getInt(1);
200+
for (final String type :stream.getObject("urls").keySet()) {
201+
if (!type.equals("hls")) {
202+
final JsonObject url = stream.getObject("urls").getObject(type);
203+
videoStreams.add(new VideoStream(
204+
url.getString("url"),
205+
MediaFormat.getFromSuffix(type),
206+
resolution));
207+
}
208+
}
209+
}
210+
}
211+
return videoStreams;
212+
}
213+
214+
@Override
215+
public List<VideoStream> getVideoOnlyStreams() throws IOException, ExtractionException {
216+
return null;
217+
}
218+
219+
@Nonnull
220+
@Override
221+
public List<SubtitlesStream> getSubtitlesDefault(){
222+
return Collections.emptyList();
223+
}
224+
225+
@Nonnull
226+
@Override
227+
public List<SubtitlesStream> getSubtitles(MediaFormat format) {
228+
return Collections.emptyList();
229+
}
230+
231+
@Override
232+
public StreamType getStreamType() throws ParsingException {
233+
return StreamType.LIVE_STREAM; // TODO: video and audio only streams are both available
73234
}
74235

75236
@Nullable
76237
@Override
77-
public String getTextualUploadDate() throws ParsingException {
238+
public StreamInfoItemsCollector getRelatedStreams() {
239+
return null;
240+
}
241+
242+
@Override
243+
public String getErrorMessage() {
78244
return null;
79245
}
80246

247+
@Nonnull
248+
@Override
249+
public String getHost() throws ParsingException {
250+
return null;
251+
}
252+
253+
@Nonnull
254+
@Override
255+
public String getPrivacy() {
256+
return "Public";
257+
}
258+
259+
@Nonnull
260+
@Override
261+
public String getCategory() {
262+
return group;
263+
}
264+
265+
@Nonnull
266+
@Override
267+
public String getLicence() {
268+
return "";
269+
}
270+
81271
@Nullable
82272
@Override
83-
public DateWrapper getUploadDate() throws ParsingException {
273+
public Locale getLanguageInfo() {
84274
return null;
85275
}
276+
277+
@Nonnull
278+
@Override
279+
public List<String> getTags() {
280+
return Collections.emptyList();
281+
}
282+
283+
@Nonnull
284+
@Override
285+
public String getSupportInfo() {
286+
return "";
287+
}
288+
289+
@Nonnull
290+
@Override
291+
public List<StreamSegment> getStreamSegments() {
292+
return Collections.emptyList();
293+
}
294+
295+
@Nonnull
296+
@Override
297+
public List<MetaInfo> getMetaInfo() {
298+
return Collections.emptyList();
299+
}
86300
}

0 commit comments

Comments
 (0)