Skip to content

Commit 758c31b

Browse files
Allow retrieval of a non-approximate DateWrapper in YoutubeStreamExtractor
1 parent 953189f commit 758c31b

2 files changed

Lines changed: 56 additions & 78 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,6 @@
6767
import java.net.MalformedURLException;
6868
import java.net.URL;
6969
import java.nio.charset.StandardCharsets;
70-
import java.time.Instant;
71-
import java.time.LocalDate;
72-
import java.time.OffsetDateTime;
73-
import java.time.ZoneId;
74-
import java.time.format.DateTimeParseException;
7570
import java.util.HashMap;
7671
import java.util.List;
7772
import java.util.Locale;
@@ -295,21 +290,6 @@ public static String getFeedUrlFrom(@Nonnull final String channelIdOrUser) {
295290
}
296291
}
297292

298-
public static Instant parseInstantFrom(final String textualUploadDate)
299-
throws ParsingException {
300-
try {
301-
return OffsetDateTime.parse(textualUploadDate).toInstant();
302-
} catch (final DateTimeParseException e) {
303-
try {
304-
return LocalDate.parse(textualUploadDate).atStartOfDay(ZoneId.systemDefault())
305-
.toInstant();
306-
} catch (final DateTimeParseException e1) {
307-
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"",
308-
e1);
309-
}
310-
}
311-
}
312-
313293
/**
314294
* Checks if the given playlist id is a YouTube Mix (auto-generated playlist)
315295
* Ids from a YouTube Mix start with "RD"

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package org.schabi.newpipe.extractor.services.youtube.extractors;
2222

23+
import static org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager.getTimeAgoParserFor;
2324
import static org.schabi.newpipe.extractor.services.youtube.ItagItem.APPROX_DURATION_MS_UNKNOWN;
2425
import static org.schabi.newpipe.extractor.services.youtube.ItagItem.CONTENT_LENGTH_UNKNOWN;
2526
import static org.schabi.newpipe.extractor.services.youtube.YoutubeDescriptionHelper.attributedDescriptionToHtml;
@@ -59,7 +60,6 @@
5960
import org.schabi.newpipe.extractor.localization.DateWrapper;
6061
import org.schabi.newpipe.extractor.localization.Localization;
6162
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
62-
import org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager;
6363
import org.schabi.newpipe.extractor.services.youtube.ItagItem;
6464
import org.schabi.newpipe.extractor.services.youtube.PoTokenProvider;
6565
import org.schabi.newpipe.extractor.services.youtube.PoTokenResult;
@@ -87,14 +87,17 @@
8787
import java.io.IOException;
8888
import java.nio.charset.StandardCharsets;
8989
import java.time.LocalDate;
90+
import java.time.OffsetDateTime;
9091
import java.time.ZoneId;
9192
import java.time.format.DateTimeFormatter;
93+
import java.time.format.DateTimeParseException;
9294
import java.util.ArrayList;
9395
import java.util.Arrays;
9496
import java.util.Collections;
9597
import java.util.List;
9698
import java.util.Locale;
9799
import java.util.Objects;
100+
import java.util.Optional;
98101
import java.util.stream.Collectors;
99102

100103
import javax.annotation.Nonnull;
@@ -169,77 +172,72 @@ public String getName() throws ParsingException {
169172
@Nullable
170173
@Override
171174
public String getTextualUploadDate() throws ParsingException {
172-
if (!playerMicroFormatRenderer.getString("uploadDate", "").isEmpty()) {
173-
return playerMicroFormatRenderer.getString("uploadDate");
174-
} else if (!playerMicroFormatRenderer.getString("publishDate", "").isEmpty()) {
175-
return playerMicroFormatRenderer.getString("publishDate");
175+
final var uploadDate = getUploadDate();
176+
if (uploadDate == null) {
177+
return null;
176178
}
179+
return LocalDate.ofInstant(uploadDate.getInstant(), ZoneId.systemDefault()).toString();
180+
}
181+
182+
@Override
183+
public DateWrapper getUploadDate() throws ParsingException {
184+
final String dateStr = playerMicroFormatRenderer.getString("uploadDate",
185+
playerMicroFormatRenderer.getString("publishDate", ""));
186+
if (!dateStr.isEmpty()) {
187+
return new DateWrapper(OffsetDateTime.parse(dateStr));
188+
}
189+
190+
final var liveDetails = playerMicroFormatRenderer.getObject("liveBroadcastDetails");
191+
final String timestamp = liveDetails.getString("endTimestamp", // an ended live stream
192+
liveDetails.getString("startTimestamp", "")); // a running live stream
177193

178-
final JsonObject liveDetails = playerMicroFormatRenderer.getObject(
179-
"liveBroadcastDetails");
180-
if (!liveDetails.getString("endTimestamp", "").isEmpty()) {
181-
// an ended live stream
182-
return liveDetails.getString("endTimestamp");
183-
} else if (!liveDetails.getString("startTimestamp", "").isEmpty()) {
184-
// a running live stream
185-
return liveDetails.getString("startTimestamp");
194+
if (!timestamp.isEmpty()) {
195+
return new DateWrapper(OffsetDateTime.parse(timestamp));
186196
} else if (getStreamType() == StreamType.LIVE_STREAM) {
187197
// this should never be reached, but a live stream without upload date is valid
188198
return null;
189199
}
190200

191-
final String videoPrimaryInfoRendererDateText =
192-
getTextFromObject(getVideoPrimaryInfoRenderer().getObject("dateText"));
201+
final var textObject = getVideoPrimaryInfoRenderer().getObject("dateText");
202+
return Optional.ofNullable(getTextFromObject(textObject))
203+
.flatMap(rendererDateText -> {
204+
final Optional<LocalDate> dateOptional;
193205

194-
if (videoPrimaryInfoRendererDateText != null) {
195-
if (videoPrimaryInfoRendererDateText.startsWith("Premiered")) {
196-
final String time = videoPrimaryInfoRendererDateText.substring(13);
206+
if (rendererDateText.startsWith("Premiered")) {
207+
final String time = rendererDateText.substring(13);
197208

198-
try { // Premiered 20 hours ago
199-
final var timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor(
200-
new Localization("en"));
201-
final var instant = timeAgoParser.parse(time).getInstant();
202-
return LocalDate.ofInstant(instant, ZoneId.systemDefault()).toString();
203-
} catch (final Exception ignored) {
204-
}
205-
206-
try { // Premiered Feb 21, 2020
207-
final var formatter = DateTimeFormatter.ofPattern("MMM dd, yyyy",
208-
Locale.ENGLISH);
209-
return LocalDate.parse(time, formatter).toString();
210-
} catch (final Exception ignored) {
211-
}
212-
213-
try { // Premiered on 21 Feb 2020
214-
final var formatter = DateTimeFormatter.ofPattern("dd MMM yyyy",
215-
Locale.ENGLISH);
216-
return LocalDate.parse(time, formatter).toString();
217-
} catch (final Exception ignored) {
218-
}
219-
}
209+
try { // Premiered 20 hours ago
210+
final var localization = new Localization("en");
211+
return Optional.of(getTimeAgoParserFor(localization).parse(time));
212+
} catch (final Exception e) {
213+
}
220214

221-
try {
222-
// TODO: this parses English formatted dates only, we need a better approach to
223-
// parse the textual date
224-
final var formatter = DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH);
225-
return LocalDate.parse(videoPrimaryInfoRendererDateText, formatter).toString();
226-
} catch (final Exception e) {
227-
throw new ParsingException("Could not get upload date", e);
228-
}
229-
}
215+
// Premiered Feb 21, 2020
216+
dateOptional = parseOptionalDate(time, "MMM dd, yyyy")
217+
// Premiered on 21 Feb 2020
218+
.or(() -> parseOptionalDate(time, "dd MMM yyyy"));
219+
} else {
220+
// Premiered on 21 Feb 2020
221+
dateOptional = parseOptionalDate(rendererDateText, "dd MMM yyyy");
222+
}
230223

231-
throw new ParsingException("Could not get upload date");
224+
return dateOptional.map(date -> {
225+
final var instant = date.atStartOfDay(ZoneId.systemDefault()).toInstant();
226+
return new DateWrapper(instant, true);
227+
});
228+
})
229+
.orElseThrow(() -> new ParsingException("Could not get upload date"));
232230
}
233231

234-
@Override
235-
public DateWrapper getUploadDate() throws ParsingException {
236-
final String textualUploadDate = getTextualUploadDate();
237-
238-
if (isNullOrEmpty(textualUploadDate)) {
239-
return null;
232+
private Optional<LocalDate> parseOptionalDate(String date, String pattern) {
233+
try {
234+
// TODO: this parses English formatted dates only, we need a better approach to
235+
// parse the textual date
236+
final var formatter = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH);
237+
return Optional.of(LocalDate.parse(date, formatter));
238+
} catch (DateTimeParseException e) {
239+
return Optional.empty();
240240
}
241-
242-
return new DateWrapper(YoutubeParsingHelper.parseInstantFrom(textualUploadDate), true);
243241
}
244242

245243
@Nonnull

0 commit comments

Comments
 (0)