Skip to content

Commit d02edd5

Browse files
committed
[media.ccc.de] Live stream kiosk: detect break "talks" segements
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
1 parent 1de6f45 commit d02edd5

10 files changed

Lines changed: 341 additions & 6 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKioskExtractor.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import javax.annotation.Nonnull;
1111
import javax.annotation.Nullable;
12+
import java.time.OffsetDateTime;
1213
import java.util.List;
1314

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

23+
@Nonnull
24+
private final JsonObject currentTalk;
25+
2226
public MediaCCCLiveStreamKioskExtractor(final JsonObject conferenceInfo,
2327
final String group,
2428
final JsonObject roomInfo) {
2529
this.conferenceInfo = conferenceInfo;
2630
this.group = group;
2731
this.roomInfo = roomInfo;
32+
this.currentTalk = roomInfo.getObject("talks").getObject("current");
2833
}
2934

3035
@Override
3136
public String getName() throws ParsingException {
32-
return roomInfo.getObject("talks").getObject("current").getString("title");
37+
if (isBreak()) {
38+
return roomInfo.getString("display") + " - Pause";
39+
} else {
40+
return currentTalk.getString("title");
41+
}
3342
}
3443

3544
@Override
@@ -95,6 +104,18 @@ public String getTextualUploadDate() throws ParsingException {
95104
@Nullable
96105
@Override
97106
public DateWrapper getUploadDate() throws ParsingException {
98-
return null;
107+
if (isBreak()) {
108+
return new DateWrapper(OffsetDateTime.parse(currentTalk.getString("fstart")));
109+
} else {
110+
return new DateWrapper(OffsetDateTime.parse(conferenceInfo.getString("startsAt")));
111+
}
112+
}
113+
114+
/**
115+
* Whether the current "talk" is a talk or a pause.
116+
*/
117+
private boolean isBreak() {
118+
return OffsetDateTime.parse(currentTalk.getString("fstart")).isBefore(OffsetDateTime.now())
119+
|| "gap".equals(currentTalk.getString("special"));
99120
}
100121
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ public static JsonArray getLiveStreams(final Downloader downloader,
7070
return liveStreams;
7171
}
7272

73+
/**
74+
* Reset cached live stream data.
75+
*
76+
* <p>
77+
* This is a temporary method which can be used to reset the cached live stream data until a
78+
* caching policy for {@link #getLiveStreams(Downloader, Localization)} is implemented.
79+
* </p>
80+
*/
81+
public static void resetCachedLiveStreamInfo() {
82+
liveStreams = null;
83+
}
84+
7385
/**
7486
* Get an {@link Image} list from a given image logo URL.
7587
*

extractor/src/test/java/org/schabi/newpipe/downloader/DownloaderFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class DownloaderFactory {
1414

1515
private static DownloaderType cachedDownloaderType;
1616

17-
private static DownloaderType getDownloaderType() {
17+
static DownloaderType getDownloaderType() {
1818
if (cachedDownloaderType == null) {
1919
cachedDownloaderType = determineDownloaderType();
2020
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.schabi.newpipe.downloader;
2+
3+
import org.junit.jupiter.api.extension.ExtendWith;
4+
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
8+
/**
9+
* Use this to annotate tests methods/classes that should only be run when the downloader is of type
10+
* {@link DownloaderType#MOCK} or {@link DownloaderType#RECORDING}. This should be used when e.g. an
11+
* extractor returns different results each time because the underlying service web page does so. In
12+
* that case it makes sense to only run the tests with the mock downloader, since the real web page
13+
* is not reliable, but we still want to make sure that the code correctly interprets the stored and
14+
* mocked web page data.
15+
* @see MockOnlyCondition
16+
*/
17+
@Retention(RetentionPolicy.RUNTIME)
18+
@ExtendWith(MockOnlyCondition.class)
19+
public @interface MockOnly {
20+
21+
/**
22+
* The reason why the test is mockonly.
23+
*/
24+
String value();
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.schabi.newpipe.downloader;
2+
3+
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
4+
import org.junit.jupiter.api.extension.ExecutionCondition;
5+
import org.junit.jupiter.api.extension.ExtensionContext;
6+
7+
/**
8+
* @see MockOnly
9+
*/
10+
public class MockOnlyCondition implements ExecutionCondition {
11+
private static final String MOCK_ONLY_REASON = "Mock only";
12+
13+
@Override
14+
public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext context) {
15+
if (DownloaderFactory.getDownloaderType() == DownloaderType.REAL) {
16+
return ConditionEvaluationResult.disabled(MOCK_ONLY_REASON);
17+
} else {
18+
return ConditionEvaluationResult.enabled(MOCK_ONLY_REASON);
19+
}
20+
}
21+
}

extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCLiveStreamListExtractorTest.java

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

3-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4-
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
5-
3+
import org.junit.jupiter.api.BeforeAll;
64
import org.junit.jupiter.api.Test;
5+
import org.schabi.newpipe.downloader.MockOnly;
6+
import org.schabi.newpipe.extractor.InfoItem;
7+
import org.schabi.newpipe.extractor.ListExtractor;
78
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
89
import org.schabi.newpipe.extractor.services.DefaultSimpleExtractorTest;
10+
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCLiveStreamKiosk;
11+
12+
import java.util.List;
13+
14+
import static org.junit.jupiter.api.Assertions.*;
15+
import static org.schabi.newpipe.extractor.ServiceList.MediaCCC;
16+
import static org.schabi.newpipe.extractor.services.DefaultTests.defaultTestListOfItems;
917

1018
public class MediaCCCLiveStreamListExtractorTest extends DefaultSimpleExtractorTest<KioskExtractor> {
19+
private static final String LIVE_KIOSK_ID = MediaCCCLiveStreamKiosk.KIOSK_ID;
1120

1221
@Override
1322
protected KioskExtractor createExtractor() throws Exception {
@@ -19,4 +28,95 @@ public void getConferencesListTest() {
1928
assertDoesNotThrow(() -> extractor().getInitialPage().getItems());
2029
}
2130

31+
/**
32+
* Test against the media.ccc.de livestream API endpoint
33+
* and ensure that no exceptions are thrown.
34+
*/
35+
public static class LiveDataTest extends DefaultSimpleExtractorTest<KioskExtractor> {
36+
37+
@BeforeAll
38+
@Override
39+
public void setUp() throws Exception {
40+
MediaCCCTestUtils.ensureStateless();
41+
super.setUp();
42+
}
43+
44+
@Override
45+
protected KioskExtractor createExtractor() throws Exception {
46+
return MediaCCC.getKioskList().getExtractorById(LIVE_KIOSK_ID, null);
47+
}
48+
49+
@Test
50+
void getConferencesListTest() throws Exception {
51+
final ListExtractor.InfoItemsPage liveStreamPage = extractor().getInitialPage();
52+
final List<InfoItem> items = liveStreamPage.getItems();
53+
if (items.isEmpty()) {
54+
// defaultTestListOfItems() fails, if items is empty.
55+
// This can happen if there are no current live streams.
56+
// In this case, we just check if an exception was thrown
57+
assertTrue(liveStreamPage.getErrors().isEmpty());
58+
} else {
59+
defaultTestListOfItems(MediaCCC, items, liveStreamPage.getErrors());
60+
}
61+
}
62+
63+
}
64+
65+
/**
66+
* Test conferences which are available via the API for C3voc internal testing,
67+
* but not intended to be shown to users.
68+
*/
69+
@MockOnly("The live stream API returns different data depending on if and what conferences"
70+
+ " are running. The PreparationTest tests a conference which is used "
71+
+ "for internal testing.")
72+
public static class PreparationTest extends DefaultSimpleExtractorTest<KioskExtractor> {
73+
74+
@BeforeAll
75+
@Override
76+
public void setUp() throws Exception {
77+
MediaCCCTestUtils.ensureStateless();
78+
super.setUp();
79+
}
80+
81+
@Override
82+
protected KioskExtractor createExtractor() throws Exception {
83+
return MediaCCC.getKioskList().getExtractorById(LIVE_KIOSK_ID, null);
84+
}
85+
86+
@Test
87+
void getConferencesListTest() throws Exception {
88+
// Testing conferences and the corresponding talks should not be extracted.
89+
assertTrue(extractor().getInitialPage().getItems().isEmpty());
90+
}
91+
}
92+
93+
/**
94+
* Test a running conference.
95+
*/
96+
@MockOnly("The live stream API returns different data depending on if and what conferences"
97+
+ " are running. Using mocks to ensure that there are conferences & talks to extract.")
98+
public static class LiveConferenceTest extends DefaultSimpleExtractorTest<KioskExtractor> {
99+
100+
@BeforeAll
101+
@Override
102+
public void setUp() throws Exception {
103+
MediaCCCTestUtils.ensureStateless();
104+
super.setUp();
105+
}
106+
107+
@Override
108+
protected KioskExtractor createExtractor() throws Exception {
109+
return MediaCCC.getKioskList().getExtractorById(LIVE_KIOSK_ID, null);
110+
}
111+
112+
@Test
113+
void getConferencesListTest() throws Exception {
114+
final ListExtractor.InfoItemsPage liveStreamPage = extractor().getInitialPage();
115+
final List<InfoItem> items = liveStreamPage.getItems();
116+
assertEquals(6, items.size());
117+
defaultTestListOfItems(MediaCCC, items, liveStreamPage.getErrors());
118+
119+
}
120+
}
121+
22122
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.schabi.newpipe.extractor.services.media_ccc;
2+
3+
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper;
4+
5+
public final class MediaCCCTestUtils {
6+
7+
/**
8+
* Clears static MediaCCC states.
9+
*
10+
* <p>
11+
* This method needs to be called in every class before running and recording mock tests.
12+
* </p>
13+
*/
14+
public static void ensureStateless() {
15+
MediaCCCParsingHelper.resetCachedLiveStreamInfo();
16+
}
17+
18+
}

extractor/src/test/resources/mocks/v1/org/schabi/newpipe/extractor/services/media_ccc/mediaccclivestreamlistextractor/liveconference/generated_mock_0.json

Lines changed: 47 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)