Skip to content

Commit e846616

Browse files
committed
fix 992-01-990-989 and remove java preview feature
1 parent 547e423 commit e846616

File tree

9 files changed

+1120
-36
lines changed

9 files changed

+1120
-36
lines changed

src/main/java/de/mediathekview/mserver/crawler/ard/ArdConstants.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.HashMap;
44
import java.util.Map;
55

6+
import de.mediathekview.mlib.daten.Resolution;
67
import de.mediathekview.mlib.daten.Sender;
78

89
public class ArdConstants {
@@ -67,6 +68,17 @@ public class ArdConstants {
6768
public static final String WEBSITE_URL = "https://www.ardmediathek.de/video/%s";
6869

6970
public static final String BASE_URL_SUBTITLES = "https://classic.ardmediathek.de";
71+
72+
// provide the same as master until crawler release
73+
public static Resolution getResolutionFromWidth(final int width) {
74+
if (width > 1280) {
75+
return Resolution.HD;
76+
} else if (width > 640) {
77+
return Resolution.NORMAL;
78+
} else {
79+
return Resolution.SMALL;
80+
}
81+
}
7082

7183
private ArdConstants() {}
7284
}

src/main/java/de/mediathekview/mserver/crawler/ard/json/ArdFilmDeserializer.java

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import de.mediathekview.mlib.daten.Sender;
88
import de.mediathekview.mserver.base.utils.GeoLocationGuesser;
99
import de.mediathekview.mserver.base.utils.JsonUtils;
10+
import de.mediathekview.mserver.base.utils.UrlUtils;
1011
import de.mediathekview.mserver.crawler.ard.ArdConstants;
1112
import de.mediathekview.mserver.crawler.ard.ArdFilmDto;
1213
import de.mediathekview.mserver.crawler.ard.ArdFilmInfoDto;
@@ -57,13 +58,16 @@ public class ArdFilmDeserializer implements JsonDeserializer<List<ArdFilmDto>> {
5758
private static final String ATTRIBUTE_RESOLUTION_H = "maxHResolutionPx";
5859
private static final String ATTRIBUTE_MIME = "mimeType";
5960
private static final String ATTRIBUTE_KIND = "kind";
61+
private static final String ATTRIBUTE_ADUIO_LANG = "languageCode";
6062

6163
private static final String MARKER_VIDEO_MP4 = "video/mp4";
6264
private static final String MARKER_VIDEO_STANDARD = "standard";
6365
private static final String MARKER_VIDEO_CATEGORY_MAIN = "main";
6466
private static final String MARKER_VIDEO_CATEGORY_MPEG = "application/vnd.apple.mpegurl";
6567
private static final String MARKER_VIDEO_AD = "audio-description";
66-
private static final String MARKER_VIDEO_DGS = "sign-language";
68+
private static final String MARKER_VIDEO_DGS = "sign-language";
69+
private static final String MARKER_VIDEO_OV = "OV";
70+
private static final String MARKER_VIDEO_DE = "deu";
6771

6872
private final ArdVideoInfoJsonDeserializer videoDeserializer;
6973
private final AbstractCrawler crawler;
@@ -292,7 +296,6 @@ private Film createFilm(
292296
duration == null ? Duration.ofSeconds(0) : duration);
293297

294298
Optional.ofNullable(description).ifPresent(film::setBeschreibung);
295-
296299

297300
film.setGeoLocations(GeoLocationGuesser.getGeoLocations(Sender.ARD, videoInfo.getDefaultVideoUrl()));
298301

@@ -354,12 +357,19 @@ private void addDGSUrls(final Film film, final Map<Resolution, String> videoUrls
354357
private Optional<ArdVideoInfoDto> parseVideos(final JsonObject playerPageObject, final String title) {
355358
ArdVideoInfoDto allVideoUrls = new ArdVideoInfoDto();
356359
//
357-
final Optional<Map<Resolution, String>> videoInfoStandard = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_STANDARD, MARKER_VIDEO_MP4);
358-
final Optional<Map<Resolution, String>> videoInfoAdaptive = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_STANDARD, MARKER_VIDEO_CATEGORY_MPEG);
359-
final Optional<Map<Resolution, String>> videoInfoAD = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_AD, MARKER_VIDEO_MP4);
360-
final Optional<Map<Resolution, String>> videoInfoDGS = parseVideoUrls(playerPageObject, MARKER_VIDEO_DGS, MARKER_VIDEO_STANDARD, MARKER_VIDEO_MP4);
360+
final Optional<Map<Resolution, String>> videoInfoStandard = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_STANDARD, MARKER_VIDEO_MP4, MARKER_VIDEO_DE);
361+
final Optional<Map<Resolution, String>> videoInfoAdaptive = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_STANDARD, MARKER_VIDEO_CATEGORY_MPEG, MARKER_VIDEO_DE);
362+
final Optional<Map<Resolution, String>> videoInfoAD = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_AD, MARKER_VIDEO_MP4, MARKER_VIDEO_DE);
363+
final Optional<Map<Resolution, String>> videoInfoDGS = parseVideoUrls(playerPageObject, MARKER_VIDEO_DGS, MARKER_VIDEO_STANDARD, MARKER_VIDEO_MP4, MARKER_VIDEO_DE);
364+
final Optional<Map<Resolution, String>> videoInfoOV = parseVideoUrls(playerPageObject, MARKER_VIDEO_CATEGORY_MAIN, MARKER_VIDEO_STANDARD, MARKER_VIDEO_MP4, MARKER_VIDEO_OV);
361365
final Optional<Set<String>> subtitles = prepareSubtitleUrl(playerPageObject);
362366
//
367+
// OV is a single film but also included in the standard film
368+
if ((title.toUpperCase().contains("(OV)") || title.toUpperCase().contains("(ORIGINALVERSION)")) && videoInfoOV.isPresent()) {
369+
allVideoUrls.putAll(videoInfoOV.get());
370+
return Optional.of(allVideoUrls);
371+
}
372+
//
363373
if (subtitles.isPresent()) {
364374
allVideoUrls.setSubtitleUrl(subtitles);
365375
}
@@ -385,16 +395,38 @@ private Optional<ArdVideoInfoDto> parseVideos(final JsonObject playerPageObject,
385395
}
386396
return Optional.of(allVideoUrls);
387397
}
398+
399+
private Optional<Map<Resolution, String>> parseVideoUrls(final JsonObject playerPageObject, String streamType, String aduioType, String mimeType, String language) {
400+
Optional<Map<Integer, String>> urls = parseVideoUrlMap(playerPageObject, streamType, aduioType, mimeType, language);
401+
if (urls.isEmpty()) {
402+
return Optional.empty();
403+
}
404+
Map<Resolution, String> videoInfo = new EnumMap<>(Resolution.class);
405+
for (Map.Entry<Integer, String> entry : urls.get().entrySet()) {
406+
Resolution resolution = ArdConstants.getResolutionFromWidth(entry.getKey());
407+
if(!videoInfo.containsKey(resolution)) {
408+
videoInfo.put(resolution, entry.getValue());
409+
}
410+
}
411+
// issue if we do not have normal res
412+
// TODO: FIXME
413+
if (!videoInfo.containsKey(Resolution.NORMAL)) {
414+
Resolution anyResolution = videoInfo.keySet().stream().findFirst().get();
415+
videoInfo.put(Resolution.NORMAL, videoInfo.get(anyResolution));
416+
videoInfo.remove(anyResolution);
417+
}
418+
return Optional.of(videoInfo);
419+
}
388420

389-
private Optional<Map<Resolution, String>> parseVideoUrls(final JsonObject playerPageObject, String streamType, String aduioType, String mimeType) {
421+
private Optional<Map<Integer, String>> parseVideoUrlMap(final JsonObject playerPageObject, String streamType, String aduioType, String mimeType, String language) {
390422
final Optional<JsonObject> mediaCollectionObject = getMediaCollectionObject(playerPageObject);
391423
if (mediaCollectionObject.isEmpty())
392424
return Optional.empty();
393425
final Optional<JsonElement> streams = JsonUtils.getElement(mediaCollectionObject.get(), ELEMENT_STREAMS);
394426
if (streams.isEmpty() || !streams.get().isJsonArray() || (streams.get().getAsJsonArray().size() == 0))
395427
return Optional.empty();
396428
//
397-
Map<Resolution, String> videoInfo = new EnumMap<>(Resolution.class);
429+
Map<Integer, String> videoInfo = new TreeMap<>(Comparator.reverseOrder());
398430
for (JsonElement streamsCategory : streams.get().getAsJsonArray()) {
399431
final Optional<String> streamKind = JsonUtils.getElementValueAsString(streamsCategory, ATTRIBUTE_KIND);
400432
final Optional<JsonElement> media = JsonUtils.getElement(streamsCategory, ELEMENT_MEDIA);
@@ -409,9 +441,9 @@ private Optional<Map<Resolution, String>> parseVideoUrls(final JsonObject player
409441
Optional<String> kind = JsonUtils.getElementValueAsString(audios.get().getAsJsonArray().get(0), ATTRIBUTE_KIND);
410442
Optional<String> resh = JsonUtils.getElementValueAsString(video, ATTRIBUTE_RESOLUTION_H);
411443
Optional<String> url = JsonUtils.getElementValueAsString(video, ATTRIBUTE_URL);
412-
if (url.isPresent() && resh.isPresent() && kind.isPresent() && kind.get().equalsIgnoreCase(aduioType)) {
413-
Resolution resolution = Resolution.getResolutionFromWidth(Integer.parseInt(resh.get()));
414-
videoInfo.put(resolution, url.get());
444+
Optional<String> languageCode = JsonUtils.getElementValueAsString(audios.get().getAsJsonArray().get(0), ATTRIBUTE_ADUIO_LANG);
445+
if (url.isPresent() && resh.isPresent() && kind.isPresent() && kind.get().equalsIgnoreCase(aduioType) && languageCode.orElse("").equalsIgnoreCase(language)) {
446+
videoInfo.put(Integer.parseInt(resh.get()), UrlUtils.removeParameters(url.get()));
415447
}
416448
}
417449
}
@@ -421,12 +453,6 @@ private Optional<Map<Resolution, String>> parseVideoUrls(final JsonObject player
421453
if (videoInfo.size() == 0) {
422454
return Optional.empty();
423455
}
424-
// TODO: FIX ME
425-
if (!videoInfo.containsKey(Resolution.NORMAL)) {
426-
Resolution r = videoInfo.keySet().stream().findFirst().get();
427-
videoInfo.put(Resolution.NORMAL, videoInfo.get(r));
428-
videoInfo.remove(r);
429-
}
430456
return Optional.of(videoInfo);
431457
}
432458
}

src/main/java/de/mediathekview/mserver/crawler/ard/json/ArdMediaArrayToDownloadUrlsConverter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ private static Resolution getQualityForNumber(final int qualityIndicator) {
145145
case 1 -> Resolution.SMALL;
146146
case 3, 4 -> Resolution.HD;
147147
case 5 -> Resolution.UHD;
148-
case 2, default -> Resolution.NORMAL;
148+
case 2 -> Resolution.NORMAL;
149+
default -> Resolution.NORMAL;
149150
};
150151
}
151152

src/main/java/de/mediathekview/mserver/crawler/funk/tasks/NexxCloudSessionInitiationTask.java

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,22 @@ public NexxCloudSessionInitiationTask(final AbstractCrawler aCrawler) {
3737
public Long call() {
3838
final Gson gson = createGson();
3939

40-
try(final Client client = createClient()) {
41-
final WebTarget target =
42-
client.target(FunkUrls.NEXX_CLOUD_SESSION_INIT.getAsString(crawler.getRuntimeConfig()));
40+
final Client client = createClient();
41+
final WebTarget target =
42+
client.target(FunkUrls.NEXX_CLOUD_SESSION_INIT.getAsString(crawler.getRuntimeConfig()));
4343

44-
final MultivaluedHashMap<String, String> formData = createForm();
44+
final MultivaluedHashMap<String, String> formData = createForm();
4545

46-
try(final Response response = target.request().post(Entity.form(formData)))
47-
{
48-
if (response.getStatus() == 201) {
49-
final String jsonOutput = response.readEntity(String.class);
50-
return gson.fromJson(jsonOutput, Long.class);
51-
} else {
52-
crawler.printErrorMessage();
53-
LOG.fatal(
54-
"A HTTP error {} occurred when initialising the Nexx cloud session.",
55-
response.getStatus());
56-
}
57-
}
46+
final Response response = target.request().post(Entity.form(formData));
47+
48+
if (response.getStatus() == 201) {
49+
final String jsonOutput = response.readEntity(String.class);
50+
return gson.fromJson(jsonOutput, Long.class);
51+
} else {
52+
crawler.printErrorMessage();
53+
LOG.fatal(
54+
"A HTTP error {} occurred when initialising the Nexx cloud session.",
55+
response.getStatus());
5856
}
5957
return null;
6058
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ private Optional<String> formatEpisodeTitle(Optional<Integer> season, Optional<I
303303
if (season.isPresent()) {
304304
result += String.format("S%02d", season.get());
305305
}
306+
if (season.isPresent() && episode.isPresent()) {
307+
result += "/";
308+
}
306309
if (episode.isPresent()) {
307310
result += String.format("E%02d", episode.get());
308311
}

src/test/java/de/mediathekview/mserver/crawler/ard/json/ArdFilmDeserializerTest.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ public static Collection<Object[]> data() {
258258
/*DGSsmall */ "",
259259
/*DGSnormal */ "",
260260
/*DGShd */ "",
261-
/*sub*/ "https://api.ardmediathek.de/player-service/subtitle/webvtt/urn:ard:subtitle:0567b031db73e4b9.vtt",
261+
/*sub*/ "",
262262
/*hd*/ GeoLocations.GEO_DE,
263263
/*related*/ new ArdFilmInfoDto[0],
264264
/*sender*/ Optional.of(Sender.ONE),
@@ -346,6 +346,48 @@ public static Collection<Object[]> data() {
346346
/*hd*/ GeoLocations.GEO_DE,
347347
/*related*/ new ArdFilmInfoDto[0],
348348
/*sender*/ Optional.of(Sender.SWR),
349+
},
350+
{
351+
/*jsonFile*/ "/ard/ard_item_ignore_OV.json",
352+
/*topic*/ "You shall not lie - Tödliche Geheimnisse",
353+
/*title*/ "Folge 4: Der Verrat (S01/E04)",
354+
/*description*/ "Zwischen Macarena und Iván, der jetzt von ihrer Schwangerschaft weiß, kommt es zu einer dramatischen Auseinandersetzung auf See. Erschöpft schwimmt der 18-Jährige an Land und beschuldigt sie, ihn vom Boot gestoßen zu haben. Die Lehrerin muss nun nicht nur die Folgen ihres Seitensprungs fürchten, sondern auch eine Anzeige wegen versuchten Totschlags. +++ Sechsteilige Thrillerserie, Spanien 2021\n.....",
355+
/*date*/ LocalDateTime.parse("2022-10-15T02:50"),
356+
/*duration*/ Duration.parse("PT42M18S"),
357+
/*small*/ "https://pd-videos.daserste.de/de/2022/10/11/1cb6c5d8-c0f4-4868-ac42-2a06b7a3381f/JOB_461836_sendeton_640x360-50p-1200kbit.mp4",
358+
/*normal*/ "https://pd-videos.daserste.de/de/2022/10/11/1cb6c5d8-c0f4-4868-ac42-2a06b7a3381f/JOB_461836_sendeton_960x540-50p-1600kbit.mp4",
359+
/*hd*/ "https://pd-videos.daserste.de/de/2022/10/11/1cb6c5d8-c0f4-4868-ac42-2a06b7a3381f/JOB_461836_sendeton_1280x720-50p-3200kbit.mp4",
360+
/*ADsmall*/ "https://pd-videos.daserste.de/de/2022/10/11/1cb6c5d8-c0f4-4868-ac42-2a06b7a3381f/JOB_461837_internationalerton_640x360-50p-1200kbit.mp4",
361+
/*ADnormal*/ "https://pd-videos.daserste.de/de/2022/10/11/1cb6c5d8-c0f4-4868-ac42-2a06b7a3381f/JOB_461837_internationalerton_960x540-50p-1600kbit.mp4",
362+
/*ADhd*/ "https://pd-videos.daserste.de/de/2022/10/11/1cb6c5d8-c0f4-4868-ac42-2a06b7a3381f/JOB_461837_internationalerton_1280x720-50p-3200kbit.mp4",
363+
/*DGSsmall */ "",
364+
/*DGSnormal */ "",
365+
/*DGShd */ "",
366+
/*sub*/ "https://api.ardmediathek.de/player-service/subtitle/webvtt/urn:ard:subtitle:efab8bf55007171e.vtt",
367+
/*hd*/ GeoLocations.GEO_DE,
368+
/*related*/ new ArdFilmInfoDto[0],
369+
/*sender*/ Optional.of(Sender.ARD),
370+
},
371+
{
372+
/*jsonFile*/ "/ard/ard_item_OV.json",
373+
/*topic*/ "Murdoch Mysteries",
374+
/*title*/ "Folge 12: Der küssende Bandit (S04/E12) - (Originalversion)",
375+
/*description*/ "Während sich Dr. Ogden auf ihre Hochzeit vorbereitet, muss Murdoch versuchen, den 'Küssenden Banditen' aufzuhalten, einen umstrittenen Bankräuber, der schnell zum Volkshelden aufsteigt.",
376+
/*date*/ LocalDateTime.parse("2024-04-24T22:50"),
377+
/*duration*/ Duration.parse("PT45M44S"),
378+
/*small*/ "https://wdrmedien-a.akamaihd.net/medp/ondemand/de/fsk12/310/3103788/3103788_57016296.mp4",
379+
/*normal*/ "https://wdrmedien-a.akamaihd.net/medp/ondemand/de/fsk12/310/3103788/3103788_57016297.mp4",
380+
/*hd*/ "https://wdrmedien-a.akamaihd.net/medp/ondemand/de/fsk12/310/3103788/3103788_57016298.mp4",
381+
/*ADsmall*/ "",
382+
/*ADnormal*/ "",
383+
/*ADhd*/ "",
384+
/*DGSsmall */ "",
385+
/*DGSnormal */ "",
386+
/*DGShd */ "",
387+
/*sub*/ "",
388+
/*hd*/ GeoLocations.GEO_DE,
389+
/*related*/ new ArdFilmInfoDto[0],
390+
/*sender*/ Optional.of(Sender.ONE),
349391
}
350392
});
351393
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public static Collection<Object[]> data() {
162162
"/zdf/zdf_film_details_with_episodes.json",
163163
Sender.ZDF,
164164
"The Rookie",
165-
"Der Prozess (S05E01)",
165+
"Der Prozess (S05/E01)",
166166
LocalDateTime.of(2024, 4, 11, 20, 15, 0),
167167
Duration.ofMinutes(41).plusSeconds(6),
168168
"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.",

0 commit comments

Comments
 (0)