Skip to content

Commit 41af0de

Browse files
authored
Merge pull request #976 from mediathekview/feature/942
ZDF add episode to title
2 parents 9630cb8 + c71545b commit 41af0de

File tree

5 files changed

+1425
-41
lines changed

5 files changed

+1425
-41
lines changed

src/main/java/de/mediathekview/mserver/base/utils/JsonUtils.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,18 @@ public static Optional<String> getElementValueAsString(final JsonElement aJsonEl
8383
}
8484
return Optional.empty();
8585
}
86-
86+
87+
public static Optional<Integer> getElementValueAsInteger(final JsonElement aJsonElement, final String... aElementIds) {
88+
Optional<JsonElement> rs = JsonUtils.getElement(aJsonElement, aElementIds);
89+
if (rs.isPresent()) {
90+
return Optional.of(rs.get().getAsInt());
91+
}
92+
return Optional.empty();
93+
}
94+
8795
public static Optional<JsonElement> getElement(final JsonElement aJsonElement, final String... aElementIds) {
8896
Optional<JsonElement> rs = Optional.empty();
89-
if (aElementIds == null || aElementIds.length == 0) {
97+
if (aElementIds == null || aElementIds.length == 0 || aJsonElement == null || !aJsonElement.isJsonObject()) {
9098
return rs;
9199
}
92100
JsonObject aJsonObject = aJsonElement.getAsJsonObject();

src/main/java/de/mediathekview/mserver/crawler/zdf/json/ZdfFilmDetailDeserializer.java

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,12 @@ public class ZdfFilmDetailDeserializer implements JsonDeserializer<Optional<ZdfF
5959
private static final DateTimeFormatter DATE_FORMATTER_AIRTIME =
6060
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); // 2016-10-29T16:15:00+02:00
6161

62+
private static final String EPISODENUMBER = "episodeNumber";
63+
private static final String[] SEASONNUMBER = {"http://zdf.de/rels/cmdm/season", "seasonNumber"};
64+
6265
private final String apiUrlBase;
6366
private final Sender sender;
64-
67+
6568
public ZdfFilmDetailDeserializer(final String apiUrlBase, final Sender sender) {
6669
this.apiUrlBase = apiUrlBase;
6770
this.sender = sender;
@@ -98,9 +101,9 @@ public Optional<ZdfFilmDto> deserialize(
98101
final Optional<String> website = parseWebsiteUrl(rootNode);
99102
final Optional<LocalDateTime> time = parseAirtime(rootNode, programItemTarget);
100103
final Optional<Duration> duration = parseDuration(mainVideoTarget);
101-
104+
102105
final Map<String, String> downloadUrl = parseDownloadUrls(mainVideoTarget);
103-
106+
104107
if (title.isPresent()) {
105108
final Optional<Film> film =
106109
createFilm(topic, title.get(), description, website, time, duration);
@@ -260,36 +263,50 @@ private Optional<String> parseDescription(final JsonObject aRootNode) {
260263
}
261264

262265
private Optional<String> parseTitle(final JsonObject aRootNode, final JsonObject aTarget) {
263-
final Optional<String> title = parseTitleValue(aRootNode, aTarget);
264-
return title.map(s -> s.replaceAll("\\(CC.*\\) - .* Creative Commons.*", ""));
266+
final Optional<String> programmTitle = JsonUtils.getElementValueAsString(aRootNode, JSON_ELEMENT_TITLE);
267+
final Optional<String> programmSubtitle = JsonUtils.getElementValueAsString(aRootNode, JSON_ELEMENT_SUBTITLE);
268+
Optional<String> resultingTitle = formatTitle(programmTitle, programmSubtitle);
269+
if (resultingTitle.isEmpty()) {
270+
final Optional<String> targetTitle = JsonUtils.getElementValueAsString(aTarget, JSON_ELEMENT_TITLE);
271+
final Optional<String> targetSubtitle = JsonUtils.getElementValueAsString(aTarget, JSON_ELEMENT_SUBTITLE);
272+
resultingTitle = formatTitle(targetTitle, targetSubtitle);
273+
}
274+
if (resultingTitle.isPresent()) {
275+
final Optional<Integer> season = JsonUtils.getElementValueAsInteger(aTarget, SEASONNUMBER);
276+
final Optional<Integer> episode = JsonUtils.getElementValueAsInteger(aTarget, EPISODENUMBER);
277+
final Optional<String> seasonEpisodeTitle = formatEpisodeTitle(season, episode);
278+
return cleanupTitle((resultingTitle.get() + " " + seasonEpisodeTitle.orElse("")).trim());
279+
}
280+
return Optional.empty();
265281
}
266-
267-
private Optional<String> parseTitleValue(final JsonObject aRootNode, final JsonObject aTarget) {
268-
// use property "title" if found
269-
final JsonElement titleElement = aRootNode.get(JSON_ELEMENT_TITLE);
270-
if (titleElement != null) {
271-
final JsonElement subTitleElement = aRootNode.get(JSON_ELEMENT_SUBTITLE);
272-
if (subTitleElement != null && !subTitleElement.getAsString().isBlank()) {
273-
return Optional.of(
274-
titleElement.getAsString().trim() + " - " + subTitleElement.getAsString());
275-
} else {
276-
return Optional.of(titleElement.getAsString());
277-
}
282+
283+
private Optional<String> cleanupTitle(String title) {
284+
return Optional.of(title.replaceAll("\\(CC.*\\) - .* Creative Commons.*", ""));
285+
}
286+
287+
private Optional<String> formatTitle(Optional<String> title, Optional<String> sub) {
288+
if (title.isEmpty()) {
289+
return Optional.empty();
290+
}
291+
if (sub.isPresent() && !sub.get().trim().isEmpty()) {
292+
return Optional.of(title.get().trim() + " - " + sub.get().trim());
278293
} else {
279-
// programmItem target required to determine title
280-
if (aTarget != null && aTarget.has(JSON_ELEMENT_TITLE)) {
281-
final String title = aTarget.get(JSON_ELEMENT_TITLE).getAsString();
282-
final String subTitle = aTarget.get(JSON_ELEMENT_SUBTITLE).getAsString();
283-
284-
if (subTitle.isEmpty()) {
285-
return Optional.of(title);
286-
} else {
287-
return Optional.of(title.trim() + " - " + subTitle);
288-
}
289-
}
294+
return Optional.of(title.get().trim());
290295
}
291-
292-
return Optional.empty();
296+
}
297+
298+
private Optional<String> formatEpisodeTitle(Optional<Integer> season, Optional<Integer> episode) {
299+
if (season.isEmpty() && episode.isEmpty()) {
300+
return Optional.empty();
301+
}
302+
String result = "";
303+
if (season.isPresent()) {
304+
result += String.format("S%02d", season.get());
305+
}
306+
if (episode.isPresent()) {
307+
result += String.format("E%02d", episode.get());
308+
}
309+
return Optional.of("("+result+")");
293310
}
294311

295312
private Optional<String> parseTopic(final JsonObject aRootNode) {

src/main/java/de/mediathekview/mserver/crawler/zdf/parser/ZdfTopicsPageHtmlDeserializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ private boolean isRelevant(Element teaserElement) {
3939
if (teaserElement == null) {
4040
return true;
4141
}
42-
return !("ARD".equalsIgnoreCase(teaserElement.text()));
42+
return !("ARD".equalsIgnoreCase(teaserElement.text()) || "funk".equalsIgnoreCase(teaserElement.text()));
4343
}
4444
}

src/test/java/de/mediathekview/mserver/crawler/zdf/json/ZdfFilmDetailDeserializerTest.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
package de.mediathekview.mserver.crawler.zdf.json;
22

3+
import static org.hamcrest.CoreMatchers.equalTo;
4+
import static org.hamcrest.MatcherAssert.assertThat;
5+
36
import com.google.gson.JsonObject;
47
import de.mediathekview.mlib.daten.Film;
58
import de.mediathekview.mlib.daten.Sender;
69
import de.mediathekview.mserver.crawler.zdf.ZdfConstants;
710
import de.mediathekview.mserver.crawler.zdf.ZdfFilmDto;
811
import de.mediathekview.mserver.testhelper.AssertFilm;
912
import de.mediathekview.mserver.testhelper.JsonFileReader;
10-
import org.junit.Test;
11-
import org.junit.runner.RunWith;
12-
import org.junit.runners.Parameterized;
13-
import org.junit.runners.Parameterized.Parameters;
14-
1513
import java.time.Duration;
1614
import java.time.LocalDateTime;
1715
import java.util.Arrays;
1816
import java.util.Collection;
1917
import java.util.Optional;
20-
21-
import static org.hamcrest.CoreMatchers.equalTo;
22-
import static org.hamcrest.MatcherAssert.assertThat;
18+
import org.junit.Test;
19+
import org.junit.runner.RunWith;
20+
import org.junit.runners.Parameterized;
21+
import org.junit.runners.Parameterized.Parameters;
2322

2423
@RunWith(Parameterized.class)
2524
public class ZdfFilmDetailDeserializerTest {
@@ -158,6 +157,18 @@ public static Collection<Object[]> data() {
158157
"https://api.zdf.de/tmd/2/android_native_5/vod/ptmd/mediathek/220505_geliebt_geduldet_getoetet_inf/4",
159158
Optional.of(
160159
"https://api.zdf.de/tmd/2/android_native_5/vod/ptmd/mediathek/220505_geliebt_geduldet_getoetet_inf_dgs/2")
160+
},
161+
{
162+
"/zdf/zdf_film_details_with_episodes.json",
163+
Sender.ZDF,
164+
"The Rookie",
165+
"Der Prozess (S05E01)",
166+
LocalDateTime.of(2024, 4, 11, 20, 15, 0),
167+
Duration.ofMinutes(41).plusSeconds(6),
168+
"Officer John Nolan sieht sich wieder mit der Serienmörderin Rosalind Dyer konfrontiert. Vor Beginn der Gerichtsverhandlung gelingt ihr die Flucht und sie muss erneut gefasst werden.",
169+
"https://www.zdf.de/serien/the-rookie/der-prozess-110.html",
170+
"https://api.zdf.de/tmd/2/android_native_5/vod/ptmd/mediathek/240411_2015_sendung_roo/2",
171+
Optional.empty()
161172
}
162173
});
163174
}

0 commit comments

Comments
 (0)