Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Utils;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
Expand All @@ -30,20 +34,24 @@
* The following features are currently not implemented because they have never been observed:
* <ul>
* <li>Shorts</li>
* <li>Premieres</li>
* <li>Paid content (Premium, members first or only)</li>
* </ul>
*/
public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtractor {

private static final String NO_VIEWS_LOWERCASE = "no views";
// This approach is language dependant (en-GB)
// Leading end space is voluntary included
private static final String PREMIERES_TEXT = "Premieres ";
private static final DateTimeFormatter PREMIERES_DATE_FORMATTER =
DateTimeFormatter.ofPattern("dd/MM/yyyy, HH:mm");

private final JsonObject lockupViewModel;
private final TimeAgoParser timeAgoParser;

private StreamType cachedStreamType;
private String cachedName;
private Optional<String> cachedTextualUploadDate;
private Optional<String> cachedDateText;

private ChannelImageViewModel cachedChannelImageViewModel;
private JsonArray cachedMetadataRows;
Expand Down Expand Up @@ -137,7 +145,9 @@ public String getName() throws ParsingException {
@Override
public long getDuration() throws ParsingException {
// Duration cannot be extracted for live streams, but only for normal videos
if (isLive()) {
// Exact duration cannot be extracted for premieres, an approximation is only available in
// accessibility context label
if (isLive() || isPremiere()) {
return -1;
}

Expand Down Expand Up @@ -237,20 +247,37 @@ public boolean isUploaderVerified() throws ParsingException {
@Nullable
@Override
public String getTextualUploadDate() throws ParsingException {
if (cachedTextualUploadDate != null) {
return cachedTextualUploadDate.orElse(null);
}

// Live streams have no upload date
if (isLive()) {
cachedTextualUploadDate = Optional.empty();
return null;
}

// This might be null e.g. for live streams
this.cachedTextualUploadDate = metadataPart(1, 1)
.map(this::getTextContentFromMetadataPart);
return cachedTextualUploadDate.orElse(null);
Comment thread
Stypox marked this conversation as resolved.
// Date string might be null e.g. for live streams
final Optional<String> dateText = getDateText();

if (isPremiere()) {
final LocalDateTime premiereDate = getDateFromPremiere(dateText);
if (premiereDate == null) {
return null;
}
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(premiereDate);
}

return dateText.orElse(null);
}

private LocalDateTime getDateFromPremiere(final Optional<String> dateText) {
// This approach is language dependent
// Remove the premieres text from the upload date metadata part
final String trimmedTextUploadDate =
dateText.map(str -> str.replace(PREMIERES_TEXT, ""))
.orElse(null);
if (trimmedTextUploadDate == null) {
return null;
}

// As we request a UTC offset of 0 minutes, we get the UTC date
return LocalDateTime.parse(trimmedTextUploadDate, PREMIERES_DATE_FORMATTER);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few lines above you write "This approach is language dependent"

I think parsing this might crash on different languages, so maybe add a try catch block here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not support other localizations than en-GB.

This is still interesting to do a try catch and throw a ParsingException when the date could not be parsed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a try-catch

}

@Nullable
Expand All @@ -265,11 +292,26 @@ public DateWrapper getUploadDate() throws ParsingException {
if (textualUploadDate == null) {
return null;
}

if (isPremiere()) {
final LocalDateTime premiereDate = getDateFromPremiere(getDateText());
if (premiereDate == null) {
throw new ParsingException("Could not get upload date from premiere");
}

return new DateWrapper(OffsetDateTime.of(premiereDate, ZoneOffset.UTC));
}

return timeAgoParser.parse(textualUploadDate);
}

@Override
public long getViewCount() throws ParsingException {
if (isPremiere()) {
// The number of people returned for premieres is the one currently waiting
return -1;
}

final Optional<String> optTextContent = metadataPart(1, 0)
.map(this::getTextContentFromMetadataPart);
// We could do this inline if the ParsingException would be a RuntimeException -.-
Expand Down Expand Up @@ -357,6 +399,20 @@ private boolean isLive() throws ParsingException {
return getStreamType() != StreamType.VIDEO_STREAM;
}

private Optional<String> getDateText() throws ParsingException {
if (cachedDateText == null) {
cachedDateText = metadataPart(1, 1)
.map(this::getTextContentFromMetadataPart);
}
return cachedDateText;
}

private boolean isPremiere() throws ParsingException {
return getDateText().map(dateText -> dateText.contains(PREMIERES_TEXT))
// If we can't get date text, assume it is not a premiere, it should be a livestream
.orElse(false);
}
Comment thread
Stypox marked this conversation as resolved.

abstract static class ChannelImageViewModel {
protected JsonObject viewModel;

Expand Down