Skip to content

Commit

Permalink
[media.ccc.de] Live stream kiosk: detect break "talks" segements
Browse files Browse the repository at this point in the history
Add and improve tests for MediaCCCLiveStreamKioskExtractor:
- test stream items if a live stream is running
- use mock tests to check live talk extraction and testing conferences
  • Loading branch information
TobiGr committed Aug 19, 2023
1 parent 55a2af2 commit 8d83786
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.OffsetDateTime;
import java.util.List;

import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromLiveStreamItem;
Expand All @@ -19,17 +20,25 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
private final String group;
private final JsonObject roomInfo;

@Nonnull
private final JsonObject currentTalk;

public MediaCCCLiveStreamKioskExtractor(final JsonObject conferenceInfo,
final String group,
final JsonObject roomInfo) {
this.conferenceInfo = conferenceInfo;
this.group = group;
this.roomInfo = roomInfo;
this.currentTalk = roomInfo.getObject("talks").getObject("current");
}

@Override
public String getName() throws ParsingException {
return roomInfo.getObject("talks").getObject("current").getString("title");
if (isBreak()) {
return roomInfo.getString("display") + " - Pause";
} else {
return currentTalk.getString("title");
}
}

@Override
Expand Down Expand Up @@ -95,6 +104,18 @@ public String getTextualUploadDate() throws ParsingException {
@Nullable
@Override
public DateWrapper getUploadDate() throws ParsingException {
return null;
if (isBreak()) {
return new DateWrapper(OffsetDateTime.parse(currentTalk.getString("fstart")));
} else {
return new DateWrapper(OffsetDateTime.parse(conferenceInfo.getString("startsAt")));
}
}

/**
* Whether the current "talk" is a talk or a pause.
*/
private boolean isBreak() {
return OffsetDateTime.parse(currentTalk.getString("fstart")).isBefore(OffsetDateTime.now())
|| "gap".equals(currentTalk.getString("special"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,103 @@

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.schabi.newpipe.downloader.DownloaderFactory;
import org.schabi.newpipe.downloader.DownloaderTestImpl;
import org.schabi.newpipe.downloader.MockOnly;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCLiveStreamKiosk;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestListOfItems;

public class MediaCCCLiveStreamListExtractorTest {
private static KioskExtractor extractor;

@BeforeAll
public static void setUpClass() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getKioskList().getExtractorById("live", null);
extractor.fetchPage();
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH
+ "services/media.ccc.de/kiosk/live/";
private static final String LIVE_KIOSK_ID = MediaCCCLiveStreamKiosk.KIOSK_ID;

/**
* Test against the media.ccc.de livestream API endpoint
* and ensure that no exceptions are thrown.
*/
public static class LiveDataTest {
private static KioskExtractor extractor;

@BeforeAll
public static void setUpClass() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = MediaCCC.getKioskList().getExtractorById(LIVE_KIOSK_ID, null);
extractor.fetchPage();
}

@Test
void getConferencesListTest() throws Exception {
final ListExtractor.InfoItemsPage liveStreamPage = extractor.getInitialPage();
final List<InfoItem> items = liveStreamPage.getItems();
if (items.isEmpty()) {
// defaultTestListOfItems() fails, if items is empty.
// This can happen if there are no current live streams.
// In this case, we just check if an exception was thrown
assertTrue(liveStreamPage.getErrors().isEmpty());
} else {
defaultTestListOfItems(MediaCCC, items, liveStreamPage.getErrors());
}
}
}

@Test
public void getConferencesListTest() throws Exception {
final List<InfoItem> items = extractor.getInitialPage().getItems();
// just test if there is an exception thrown
/**
* Test conferences which are available via the API for C3voc internal testing,
* but not intended to be shown to users.
*/
@MockOnly("The live stream API returns different data depending on if and what conferences"
+ " are running. The PreparationTest tests a conference which is used "
+ "for internal testing.")
public static class PreparationTest {
private static KioskExtractor extractor;

@BeforeAll
public static void setUpClass() throws Exception {
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "preparation"));
extractor = MediaCCC.getKioskList().getExtractorById(LIVE_KIOSK_ID, null);
extractor.fetchPage();
}

@Test
void getConferencesListTest() throws Exception {
// Testing conferences and the corresponding talks should not be extracted.
assertTrue(extractor.getInitialPage().getItems().isEmpty());
}
}

/**
* Test a running conference.
*/
@MockOnly("The live stream API returns different data depending on if and what conferences"
+ " are running. Using mocks to ensure that there are conferences & talks to extract.")
public static class LiveConferenceTest {
private static KioskExtractor extractor;

@BeforeAll
public static void setUpClass() throws Exception {
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "running"));
extractor = MediaCCC.getKioskList().getExtractorById(LIVE_KIOSK_ID, null);
extractor.fetchPage();
}

@Test
void getConferencesListTest() throws Exception {
final ListExtractor.InfoItemsPage liveStreamPage = extractor.getInitialPage();
final List<InfoItem> items = liveStreamPage.getItems();
assertEquals(6, items.size());
defaultTestListOfItems(MediaCCC, items, liveStreamPage.getErrors());

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"request": {
"httpMethod": "GET",
"url": "https://streaming.media.ccc.de/streams/v2.json",
"headers": {
"Accept-Language": [
"en-GB, en;q\u003d0.9"
]
},
"localization": {
"languageCode": "en",
"countryCode": "GB"
}
},
"response": {
"responseCode": 200,
"responseMessage": "OK",
"responseHeaders": {
"access-control-allow-origin": [
"*"
],
"content-type": [
"application/json"
],
"date": [
"Sat, 05 Aug 2023 10:59:09 GMT"
],
"server": [
"nginx"
],
"strict-transport-security": [
"max-age\u003d31536000"
],
"transfer-encoding": [
"chunked"
],
"vary": [
"Accept-Encoding"
],
"x-cache": [
"HIT origin"
]
},
"responseBody": "[\n {\n \"conference\": \"BornHack 2023\",\n \"slug\": \"bornhack2023\",\n \"author\": \"BornHack ApS\",\n \"description\": \"BornHack is a 7 day outdoor tent camp where hackers, makers and people with an interest in technology or security come together to celebrate technology, socialise, learn and have fun.\",\n \"keywords\": \"\",\n \"schedule\": null,\n \"startsAt\": \"2023-08-02T13:00:00+0000\",\n \"endsAt\": \"2023-08-09T18:00:00+0000\",\n \"isCurrentlyStreaming\": false,\n \"groups\": [\n {\n \"group\": \"Lecture Rooms\",\n \"rooms\": [\n {\n \"slug\": \"bornhack1\",\n \"schedulename\": \"Speakers Tent\",\n \"thumb\": \"https://cdn.c3voc.de/thumbnail/bornhack23s1/thumb.jpeg\",\n \"poster\": \"https://cdn.c3voc.de/thumbnail/bornhack23s1/poster.jpeg\",\n \"link\": \"https://streaming.media.ccc.de/bornhack2023/bornhack1\",\n \"display\": \"Speakers Tent\",\n \"stream\": \"bornhack23s1\",\n \"talks\": {\n \"current\": null,\n \"next\": null\n },\n \"streams\": [\n {\n \"slug\": \"hls-native\",\n \"display\": \"Speakers Tent \",\n \"type\": \"hls\",\n \"isTranslated\": false,\n \"videoSize\": null,\n \"urls\": {}\n },\n {\n \"slug\": \"hls-translated\",\n \"display\": \"Speakers Tent (Translated)\",\n \"type\": \"hls\",\n \"isTranslated\": true,\n \"videoSize\": null,\n \"urls\": {}\n },\n {\n \"slug\": \"hd-native\",\n \"display\": \"Speakers Tent FullHD Video\",\n \"type\": \"video\",\n \"isTranslated\": false,\n \"videoSize\": [\n 1920,\n 1080\n ],\n \"urls\": {\n \"hls\": {\n \"display\": \"HLS\",\n \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n \"url\": \"https://cdn.c3voc.de/hls/bornhack23s1/native_hd.m3u8\"\n }\n }\n },\n {\n \"slug\": \"hd-translated\",\n \"display\": \"Speakers Tent FullHD Video (Translated)\",\n \"type\": \"video\",\n \"isTranslated\": true,\n \"videoSize\": [\n 1920,\n 1080\n ],\n \"urls\": {\n \"hls\": {\n \"display\": \"HLS\",\n \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n \"url\": \"https://cdn.c3voc.de/hls/bornhack23s1/translated_hd.m3u8\"\n }\n }\n }\n ]\n },\n {\n \"slug\": \"bornhack2\",\n \"schedulename\": \"Bar Area\",\n \"thumb\": \"https://cdn.c3voc.de/thumbnail/bornhack23s2/thumb.jpeg\",\n \"poster\": \"https://cdn.c3voc.de/thumbnail/bornhack23s2/poster.jpeg\",\n \"link\": \"https://streaming.media.ccc.de/bornhack2023/bornhack2\",\n \"display\": \"Bar Area\",\n \"stream\": \"bornhack23s2\",\n \"talks\": {\n \"current\": null,\n \"next\": null\n },\n \"streams\": [\n {\n \"slug\": \"hls-native\",\n \"display\": \"Bar Area \",\n \"type\": \"hls\",\n \"isTranslated\": false,\n \"videoSize\": null,\n \"urls\": {}\n },\n {\n \"slug\": \"hls-translated\",\n \"display\": \"Bar Area (Translated)\",\n \"type\": \"hls\",\n \"isTranslated\": true,\n \"videoSize\": null,\n \"urls\": {}\n },\n {\n \"slug\": \"hd-native\",\n \"display\": \"Bar Area FullHD Video\",\n \"type\": \"video\",\n \"isTranslated\": false,\n \"videoSize\": [\n 1920,\n 1080\n ],\n \"urls\": {\n \"hls\": {\n \"display\": \"HLS\",\n \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n \"url\": \"https://cdn.c3voc.de/hls/bornhack23s2/native_hd.m3u8\"\n }\n }\n },\n {\n \"slug\": \"hd-translated\",\n \"display\": \"Bar Area FullHD Video (Translated)\",\n \"type\": \"video\",\n \"isTranslated\": true,\n \"videoSize\": [\n 1920,\n 1080\n ],\n \"urls\": {\n \"hls\": {\n \"display\": \"HLS\",\n \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n \"url\": \"https://cdn.c3voc.de/hls/bornhack23s2/translated_hd.m3u8\"\n }\n }\n }\n ]\n },\n {\n \"slug\": \"bornhack3\",\n \"schedulename\": \"Bar Meetup Area\",\n \"thumb\": \"https://cdn.c3voc.de/thumbnail/bornhack23s3/thumb.jpeg\",\n \"poster\": \"https://cdn.c3voc.de/thumbnail/bornhack23s3/poster.jpeg\",\n \"link\": \"https://streaming.media.ccc.de/bornhack2023/bornhack3\",\n \"display\": \"Bar Meetup Area\",\n \"stream\": \"bornhack23s3\",\n \"talks\": {\n \"current\": null,\n \"next\": null\n },\n \"streams\": [\n {\n \"slug\": \"hls-native\",\n \"display\": \"Bar Meetup Area \",\n \"type\": \"hls\",\n \"isTranslated\": false,\n \"videoSize\": null,\n \"urls\": {}\n },\n {\n \"slug\": \"hls-translated\",\n \"display\": \"Bar Meetup Area (Translated)\",\n \"type\": \"hls\",\n \"isTranslated\": true,\n \"videoSize\": null,\n \"urls\": {}\n },\n {\n \"slug\": \"hd-native\",\n \"display\": \"Bar Meetup Area FullHD Video\",\n \"type\": \"video\",\n \"isTranslated\": false,\n \"videoSize\": [\n 1920,\n 1080\n ],\n \"urls\": {\n \"hls\": {\n \"display\": \"HLS\",\n \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n \"url\": \"https://cdn.c3voc.de/hls/bornhack23s3/native_hd.m3u8\"\n }\n }\n },\n {\n \"slug\": \"hd-translated\",\n \"display\": \"Bar Meetup Area FullHD Video (Translated)\",\n \"type\": \"video\",\n \"isTranslated\": true,\n \"videoSize\": [\n 1920,\n 1080\n ],\n \"urls\": {\n \"hls\": {\n \"display\": \"HLS\",\n \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n \"url\": \"https://cdn.c3voc.de/hls/bornhack23s3/translated_hd.m3u8\"\n }\n }\n }\n ]\n }\n ]\n }\n ]\n }\n]",
"latestUrl": "https://streaming.media.ccc.de/streams/v2.json"
}
}

Large diffs are not rendered by default.

0 comments on commit 8d83786

Please sign in to comment.