mirror of
https://github.com/TeamNewPipe/NewPipeExtractor.git
synced 2025-12-05 01:10:15 +00:00
Merge pull request #1372 from Isira-Seneviratne/Refactor-date-parsing
Some checks failed
CI / build-and-test (push) Has been cancelled
Some checks failed
CI / build-and-test (push) Has been cancelled
Refactor date parsing
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
package org.schabi.newpipe.extractor.localization;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
/**
|
||||
* A wrapper class that provides a field to describe if the date/time is precise or just an
|
||||
@@ -14,52 +18,61 @@ import java.util.GregorianCalendar;
|
||||
*/
|
||||
public class DateWrapper implements Serializable {
|
||||
@Nonnull
|
||||
private final OffsetDateTime offsetDateTime;
|
||||
private final Instant instant;
|
||||
private final boolean isApproximation;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #DateWrapper(OffsetDateTime)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public DateWrapper(@Nonnull final Calendar calendar) {
|
||||
//noinspection deprecation
|
||||
this(calendar, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #DateWrapper(OffsetDateTime, boolean)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public DateWrapper(@Nonnull final Calendar calendar, final boolean isApproximation) {
|
||||
this(OffsetDateTime.ofInstant(calendar.toInstant(), ZoneOffset.UTC), isApproximation);
|
||||
}
|
||||
|
||||
public DateWrapper(@Nonnull final OffsetDateTime offsetDateTime) {
|
||||
this(offsetDateTime, false);
|
||||
}
|
||||
|
||||
public DateWrapper(@Nonnull final OffsetDateTime offsetDateTime,
|
||||
final boolean isApproximation) {
|
||||
this.offsetDateTime = offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC);
|
||||
this(offsetDateTime.toInstant(), isApproximation);
|
||||
}
|
||||
|
||||
public DateWrapper(@Nonnull final Instant instant) {
|
||||
this(instant, false);
|
||||
}
|
||||
|
||||
public DateWrapper(@Nonnull final Instant instant, final boolean isApproximation) {
|
||||
this.instant = instant;
|
||||
this.isApproximation = isApproximation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the wrapped date/time as a {@link Calendar}.
|
||||
* @deprecated use {@link #offsetDateTime()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public Calendar date() {
|
||||
return GregorianCalendar.from(offsetDateTime.toZonedDateTime());
|
||||
public DateWrapper(@Nonnull final LocalDateTime dateTime, final boolean isApproximation) {
|
||||
this(dateTime.atZone(ZoneId.systemDefault()).toInstant(), isApproximation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the wrapped date/time.
|
||||
* @return the wrapped {@link Instant}
|
||||
*/
|
||||
@Nonnull
|
||||
public Instant getInstant() {
|
||||
return instant;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the wrapped {@link Instant} as an {@link OffsetDateTime} set to UTC.
|
||||
*/
|
||||
@Nonnull
|
||||
public OffsetDateTime offsetDateTime() {
|
||||
return offsetDateTime;
|
||||
return instant.atOffset(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the wrapped {@link Instant} as a {@link LocalDateTime} in the current time zone.
|
||||
*/
|
||||
@Nonnull
|
||||
public LocalDateTime getLocalDateTime() {
|
||||
return getLocalDateTime(ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the wrapped {@link Instant} as a {@link LocalDateTime} in the given time zone.
|
||||
*/
|
||||
@Nonnull
|
||||
public LocalDateTime getLocalDateTime(@Nonnull final ZoneId zoneId) {
|
||||
return LocalDateTime.ofInstant(instant, zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,8 +86,42 @@ public class DateWrapper implements Serializable {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DateWrapper{"
|
||||
+ "offsetDateTime=" + offsetDateTime
|
||||
+ "instant=" + instant
|
||||
+ ", isApproximation=" + isApproximation
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date string that matches the ISO-8601 {@link OffsetDateTime} pattern, e.g.
|
||||
* "2011-12-03T10:15:30+01:00".
|
||||
*
|
||||
* @param date The date string
|
||||
* @return a non-approximate {@link DateWrapper}, or null if the string is null
|
||||
* @throws ParsingException if the string does not match the expected format
|
||||
*/
|
||||
@Nullable
|
||||
public static DateWrapper fromOffsetDateTime(final String date) throws ParsingException {
|
||||
try {
|
||||
return date != null ? new DateWrapper(OffsetDateTime.parse(date)) : null;
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ParsingException("Could not parse date: \"" + date + "\"", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date string that matches the ISO-8601 {@link Instant} pattern, e.g.
|
||||
* "2011-12-03T10:15:30Z".
|
||||
*
|
||||
* @param date The date string
|
||||
* @return a non-approximate {@link DateWrapper}, or null if the string is null
|
||||
* @throws ParsingException if the string does not match the expected format
|
||||
*/
|
||||
@Nullable
|
||||
public static DateWrapper fromInstant(final String date) throws ParsingException {
|
||||
try {
|
||||
return date != null ? new DateWrapper(Instant.parse(date)) : null;
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ParsingException("Could not parse date: \"" + date + "\"", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -16,20 +15,7 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class TimeAgoParser {
|
||||
private final PatternsHolder patternsHolder;
|
||||
private final OffsetDateTime now;
|
||||
|
||||
/**
|
||||
* Creates a helper to parse upload dates in the format '2 days ago'.
|
||||
* <p>
|
||||
* Instantiate a new {@link TimeAgoParser} every time you extract a new batch of items.
|
||||
* </p>
|
||||
*
|
||||
* @param patternsHolder An object that holds the "time ago" patterns, special cases, and the
|
||||
* language word separator.
|
||||
*/
|
||||
public TimeAgoParser(final PatternsHolder patternsHolder) {
|
||||
this(patternsHolder, OffsetDateTime.now(ZoneOffset.UTC));
|
||||
}
|
||||
private final LocalDateTime now;
|
||||
|
||||
/**
|
||||
* Creates a helper to parse upload dates in the format '2 days ago'.
|
||||
@@ -41,7 +27,7 @@ public class TimeAgoParser {
|
||||
* language word separator.
|
||||
* @param now The current time
|
||||
*/
|
||||
public TimeAgoParser(final PatternsHolder patternsHolder, final OffsetDateTime now) {
|
||||
public TimeAgoParser(final PatternsHolder patternsHolder, final LocalDateTime now) {
|
||||
this.patternsHolder = patternsHolder;
|
||||
this.now = now;
|
||||
}
|
||||
@@ -118,34 +104,13 @@ public class TimeAgoParser {
|
||||
}
|
||||
|
||||
private DateWrapper getResultFor(final int timeAgoAmount, final ChronoUnit chronoUnit) {
|
||||
OffsetDateTime offsetDateTime = now;
|
||||
boolean isApproximation = false;
|
||||
|
||||
switch (chronoUnit) {
|
||||
case SECONDS:
|
||||
case MINUTES:
|
||||
case HOURS:
|
||||
offsetDateTime = offsetDateTime.minus(timeAgoAmount, chronoUnit);
|
||||
break;
|
||||
|
||||
case DAYS:
|
||||
case WEEKS:
|
||||
case MONTHS:
|
||||
offsetDateTime = offsetDateTime.minus(timeAgoAmount, chronoUnit);
|
||||
isApproximation = true;
|
||||
break;
|
||||
|
||||
case YEARS:
|
||||
final var localDateTime = chronoUnit == ChronoUnit.YEARS
|
||||
// minusDays is needed to prevent `PrettyTime` from showing '12 months ago'.
|
||||
offsetDateTime = offsetDateTime.minusYears(timeAgoAmount).minusDays(1);
|
||||
isApproximation = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isApproximation) {
|
||||
offsetDateTime = offsetDateTime.truncatedTo(ChronoUnit.HOURS);
|
||||
}
|
||||
|
||||
return new DateWrapper(offsetDateTime, isApproximation);
|
||||
? now.minusYears(timeAgoAmount).minusDays(1)
|
||||
: now.minus(timeAgoAmount, chronoUnit);
|
||||
final boolean isApproximate = chronoUnit.isDateBased();
|
||||
final var resolvedDateTime =
|
||||
isApproximate ? localDateTime.truncatedTo(ChronoUnit.DAYS) : localDateTime;
|
||||
return new DateWrapper(resolvedDateTime, isApproximate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.localization;
|
||||
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
|
||||
import org.schabi.newpipe.extractor.timeago.PatternsManager;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -20,25 +20,13 @@ public final class TimeAgoPatternsManager {
|
||||
|
||||
@Nullable
|
||||
public static TimeAgoParser getTimeAgoParserFor(@Nonnull final Localization localization) {
|
||||
final PatternsHolder holder = getPatternsFor(localization);
|
||||
|
||||
if (holder == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TimeAgoParser(holder);
|
||||
return getTimeAgoParserFor(localization, LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static TimeAgoParser getTimeAgoParserFor(
|
||||
@Nonnull final Localization localization,
|
||||
@Nonnull final OffsetDateTime now) {
|
||||
public static TimeAgoParser getTimeAgoParserFor(@Nonnull final Localization localization,
|
||||
@Nonnull final LocalDateTime now) {
|
||||
final PatternsHolder holder = getPatternsFor(localization);
|
||||
|
||||
if (holder == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TimeAgoParser(holder, now);
|
||||
return holder == null ? null : new TimeAgoParser(holder, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ public final class BandcampExtractorHelper {
|
||||
try {
|
||||
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(textDate,
|
||||
DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH));
|
||||
return new DateWrapper(zonedDateTime.toOffsetDateTime(), false);
|
||||
return new DateWrapper(zonedDateTime.toInstant());
|
||||
} catch (final DateTimeException e) {
|
||||
throw new ParsingException("Could not parse date '" + textDate + "'", e);
|
||||
}
|
||||
|
||||
@@ -8,15 +8,12 @@ import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.localization.Localization;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -33,15 +30,6 @@ public final class MediaCCCParsingHelper {
|
||||
|
||||
private MediaCCCParsingHelper() { }
|
||||
|
||||
public static OffsetDateTime parseDateFrom(final String textualUploadDate)
|
||||
throws ParsingException {
|
||||
try {
|
||||
return OffsetDateTime.parse(textualUploadDate);
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an id is a live stream id
|
||||
* @param id the {@code id} to check
|
||||
|
||||
@@ -52,12 +52,10 @@ public class MediaCCCRecentKiosk extends KioskExtractor<StreamInfoItem> {
|
||||
|
||||
// Streams in the recent kiosk are not ordered by the release date.
|
||||
// Sort them to have the latest stream at the beginning of the list.
|
||||
final Comparator<StreamInfoItem> comparator = Comparator
|
||||
.comparing(StreamInfoItem::getUploadDate, Comparator
|
||||
.nullsLast(Comparator.comparing(DateWrapper::offsetDateTime)))
|
||||
final var comparator = Comparator.comparing(StreamInfoItem::getUploadDate,
|
||||
Comparator.nullsLast(Comparator.comparing(DateWrapper::getInstant)))
|
||||
.reversed();
|
||||
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId(),
|
||||
comparator);
|
||||
final var collector = new StreamInfoItemsCollector(getServiceId(), comparator);
|
||||
|
||||
events.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
|
||||
@@ -92,6 +92,6 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
final ZonedDateTime zonedDateTime = ZonedDateTime.parse(event.getString("date"),
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSzzzz"));
|
||||
return new DateWrapper(zonedDateTime.toOffsetDateTime(), false);
|
||||
return new DateWrapper(zonedDateTime.toInstant());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getImageListFromLogoImageUrl;
|
||||
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromStreamItem;
|
||||
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.parseDateFrom;
|
||||
import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE;
|
||||
import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN;
|
||||
|
||||
@@ -37,6 +36,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MediaCCCStreamExtractor extends StreamExtractor {
|
||||
private JsonObject data;
|
||||
@@ -46,16 +46,16 @@ public class MediaCCCStreamExtractor extends StreamExtractor {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return data.getString("release_date");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
return new DateWrapper(parseDateFrom(getTextualUploadDate()));
|
||||
return DateWrapper.fromOffsetDateTime(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
@@ -65,11 +64,8 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
final String date = getTextualUploadDate();
|
||||
if (date == null) {
|
||||
return null; // event is in the future...
|
||||
}
|
||||
return new DateWrapper(MediaCCCParsingHelper.parseDateFrom(date));
|
||||
// if null, event is in the future...
|
||||
return DateWrapper.fromOffsetDateTime(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,10 +18,6 @@ import org.schabi.newpipe.extractor.utils.JsonUtils;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -48,15 +44,6 @@ public final class PeertubeParsingHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static OffsetDateTime parseDateFrom(final String textualUploadDate)
|
||||
throws ParsingException {
|
||||
try {
|
||||
return OffsetDateTime.ofInstant(Instant.parse(textualUploadDate), ZoneOffset.UTC);
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Page getNextPage(final String prevPageUrl, final long total) {
|
||||
final String prevStart;
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.Objects;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.peertube.extractors.PeertubeCommentsExtractor.CHILDREN;
|
||||
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.getAvatarsFromOwnerAccountOrVideoChannelObject;
|
||||
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.parseDateFrom;
|
||||
|
||||
public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
|
||||
@Nonnull
|
||||
@@ -73,8 +72,7 @@ public class PeertubeCommentsInfoItemExtractor implements CommentsInfoItemExtrac
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
final String textualUploadDate = getTextualUploadDate();
|
||||
return new DateWrapper(parseDateFrom(textualUploadDate));
|
||||
return DateWrapper.fromInstant(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@@ -79,13 +79,7 @@ public class PeertubeStreamExtractor extends StreamExtractor {
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
final String textualUploadDate = getTextualUploadDate();
|
||||
|
||||
if (textualUploadDate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DateWrapper(PeertubeParsingHelper.parseDateFrom(textualUploadDate));
|
||||
return DateWrapper.fromInstant(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.getAvatarsFromOwnerAccountOrVideoChannelObject;
|
||||
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.getThumbnailsFromPlaylistOrVideoItem;
|
||||
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.parseDateFrom;
|
||||
|
||||
public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
|
||||
@@ -85,13 +84,7 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
final String textualUploadDate = getTextualUploadDate();
|
||||
|
||||
if (textualUploadDate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DateWrapper(parseDateFrom(textualUploadDate));
|
||||
return DateWrapper.fromInstant(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.schabi.newpipe.extractor.downloader.Response;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudChannelInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudPlaylistInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudLikesInfoItemExtractor;
|
||||
@@ -133,17 +134,17 @@ public final class SoundcloudParsingHelper {
|
||||
throw new ExtractionException("Couldn't extract client id");
|
||||
}
|
||||
|
||||
public static OffsetDateTime parseDateFrom(final String textualUploadDate)
|
||||
throws ParsingException {
|
||||
@Nullable
|
||||
public static DateWrapper parseDate(final String uploadDate) throws ParsingException {
|
||||
try {
|
||||
return OffsetDateTime.parse(textualUploadDate);
|
||||
} catch (final DateTimeParseException e1) {
|
||||
return DateWrapper.fromInstant(uploadDate);
|
||||
} catch (final DateTimeParseException e) {
|
||||
try {
|
||||
return OffsetDateTime.parse(textualUploadDate, DateTimeFormatter
|
||||
.ofPattern("yyyy/MM/dd HH:mm:ss +0000"));
|
||||
} catch (final DateTimeParseException e2) {
|
||||
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\""
|
||||
+ ", " + e1.getMessage(), e2);
|
||||
return new DateWrapper(OffsetDateTime.parse(uploadDate,
|
||||
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss +0000")));
|
||||
} catch (final DateTimeParseException e1) {
|
||||
e1.addSuppressed(e);
|
||||
throw new ParsingException("Could not parse date: \"" + uploadDate + "\"", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromArtworkOrAvatarUrl;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDateFrom;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDate;
|
||||
|
||||
public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtractor {
|
||||
private final JsonObject json;
|
||||
@@ -69,7 +69,7 @@ public class SoundcloudCommentsInfoItemExtractor implements CommentsInfoItemExtr
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
return new DateWrapper(parseDateFrom(getTextualUploadDate()));
|
||||
return parseDate(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,7 +5,7 @@ import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsing
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromArtworkOrAvatarUrl;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromTrackObject;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAvatarUrl;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDateFrom;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDate;
|
||||
import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
@@ -88,18 +88,16 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
|
||||
return track.getString("title");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() {
|
||||
return track.getString("created_at")
|
||||
.replace("T", " ")
|
||||
.replace("Z", "");
|
||||
return track.getString("created_at");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
return new DateWrapper(parseDateFrom(track.getString("created_at")));
|
||||
return parseDate(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromArtworkOrAvatarUrl;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.getAllImagesFromTrackObject;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDateFrom;
|
||||
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.parseDate;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
|
||||
|
||||
public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
@@ -68,7 +68,7 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
return new DateWrapper(parseDateFrom(getTextualUploadDate()));
|
||||
return parseDate(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,10 +67,6 @@ import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -294,20 +290,6 @@ public final class YoutubeParsingHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static OffsetDateTime parseDateFrom(final String textualUploadDate)
|
||||
throws ParsingException {
|
||||
try {
|
||||
return OffsetDateTime.parse(textualUploadDate);
|
||||
} catch (final DateTimeParseException e) {
|
||||
try {
|
||||
return LocalDate.parse(textualUploadDate).atStartOfDay().atOffset(ZoneOffset.UTC);
|
||||
} catch (final DateTimeParseException e1) {
|
||||
throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"",
|
||||
e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given playlist id is a YouTube Mix (auto-generated playlist)
|
||||
* Ids from a YouTube Mix start with "RD"
|
||||
|
||||
@@ -10,8 +10,6 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.List;
|
||||
|
||||
public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
@@ -69,12 +67,7 @@ public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
try {
|
||||
return new DateWrapper(OffsetDateTime.parse(getTextualUploadDate()));
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ParsingException("Could not parse date (\"" + getTextualUploadDate() + "\")",
|
||||
e);
|
||||
}
|
||||
return DateWrapper.fromOffsetDateTime(getTextualUploadDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -87,20 +87,23 @@ import org.schabi.newpipe.extractor.utils.Utils;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class YoutubeStreamExtractor extends StreamExtractor {
|
||||
private static final String PREMIERED = "Premiered ";
|
||||
private static final String PREMIERED_ON = "Premiered on ";
|
||||
|
||||
@Nullable
|
||||
private static PoTokenProvider poTokenProvider;
|
||||
@@ -168,79 +171,74 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() throws ParsingException {
|
||||
if (!playerMicroFormatRenderer.getString("uploadDate", "").isEmpty()) {
|
||||
return playerMicroFormatRenderer.getString("uploadDate");
|
||||
} else if (!playerMicroFormatRenderer.getString("publishDate", "").isEmpty()) {
|
||||
return playerMicroFormatRenderer.getString("publishDate");
|
||||
public String getTextualUploadDate() {
|
||||
String timestamp = playerMicroFormatRenderer.getString("uploadDate", "");
|
||||
if (timestamp.isEmpty()) {
|
||||
timestamp = playerMicroFormatRenderer.getString("publishDate", "");
|
||||
}
|
||||
if (!timestamp.isEmpty()) {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
final JsonObject liveDetails = playerMicroFormatRenderer.getObject(
|
||||
"liveBroadcastDetails");
|
||||
if (!liveDetails.getString("endTimestamp", "").isEmpty()) {
|
||||
// an ended live stream
|
||||
return liveDetails.getString("endTimestamp");
|
||||
} else if (!liveDetails.getString("startTimestamp", "").isEmpty()) {
|
||||
final var liveDetails = playerMicroFormatRenderer.getObject("liveBroadcastDetails");
|
||||
timestamp = liveDetails.getString("endTimestamp", ""); // an ended live stream
|
||||
if (timestamp.isEmpty()) {
|
||||
// a running live stream
|
||||
return liveDetails.getString("startTimestamp");
|
||||
timestamp = liveDetails.getString("startTimestamp", "");
|
||||
}
|
||||
if (!timestamp.isEmpty()) {
|
||||
return timestamp;
|
||||
} else if (getStreamType() == StreamType.LIVE_STREAM) {
|
||||
// this should never be reached, but a live stream without upload date is valid
|
||||
return null;
|
||||
}
|
||||
|
||||
final String videoPrimaryInfoRendererDateText =
|
||||
getTextFromObject(getVideoPrimaryInfoRenderer().getObject("dateText"));
|
||||
|
||||
if (videoPrimaryInfoRendererDateText != null) {
|
||||
if (videoPrimaryInfoRendererDateText.startsWith("Premiered")) {
|
||||
final String time = videoPrimaryInfoRendererDateText.substring(13);
|
||||
|
||||
try { // Premiered 20 hours ago
|
||||
final TimeAgoParser timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor(
|
||||
new Localization("en"));
|
||||
final OffsetDateTime parsedTime = timeAgoParser.parse(time).offsetDateTime();
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(parsedTime);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
|
||||
try { // Premiered Feb 21, 2020
|
||||
final LocalDate localDate = LocalDate.parse(time,
|
||||
DateTimeFormatter.ofPattern("MMM dd, yyyy", Locale.ENGLISH));
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
|
||||
try { // Premiered on 21 Feb 2020
|
||||
final LocalDate localDate = LocalDate.parse(time,
|
||||
DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH));
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: this parses English formatted dates only, we need a better approach to
|
||||
// parse the textual date
|
||||
final LocalDate localDate = LocalDate.parse(videoPrimaryInfoRendererDateText,
|
||||
DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH));
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get upload date", e);
|
||||
}
|
||||
final var textObject = getVideoPrimaryInfoRenderer().getObject("dateText");
|
||||
final String rendererDateText = getTextFromObject(textObject);
|
||||
if (rendererDateText == null) {
|
||||
return null;
|
||||
} else if (rendererDateText.startsWith(PREMIERED_ON)) { // Premiered on 21 Feb 2020
|
||||
return rendererDateText.substring(PREMIERED_ON.length());
|
||||
} else if (rendererDateText.startsWith(PREMIERED)) {
|
||||
// Premiered 20 hours ago / Premiered Feb 21, 2020
|
||||
return rendererDateText.substring(PREMIERED.length());
|
||||
} else {
|
||||
return rendererDateText;
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get upload date");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
final String textualUploadDate = getTextualUploadDate();
|
||||
|
||||
if (isNullOrEmpty(textualUploadDate)) {
|
||||
return null;
|
||||
final String dateText = getTextualUploadDate();
|
||||
try {
|
||||
return DateWrapper.fromOffsetDateTime(dateText);
|
||||
} catch (final ParsingException e) {
|
||||
// Try other patterns first
|
||||
}
|
||||
|
||||
return new DateWrapper(YoutubeParsingHelper.parseDateFrom(textualUploadDate), true);
|
||||
try { // Premiered 20 hours ago
|
||||
final var localization = new Localization("en");
|
||||
return TimeAgoPatternsManager.getTimeAgoParserFor(localization).parse(dateText);
|
||||
} catch (final ParsingException e) {
|
||||
// Try other patterns first
|
||||
}
|
||||
|
||||
return parseOptionalDate(dateText, "MMM dd, yyyy")
|
||||
.or(() -> parseOptionalDate(dateText, "dd MMM yyyy"))
|
||||
.map(date -> new DateWrapper(date.atStartOfDay(), true))
|
||||
.orElseThrow(() ->
|
||||
new ParsingException("Could not parse upload date \"" + dateText + "\""));
|
||||
}
|
||||
|
||||
private Optional<LocalDate> parseOptionalDate(final String date, final String pattern) {
|
||||
try {
|
||||
// TODO: this parses English formatted dates only, we need a better approach to parse
|
||||
// the textual date
|
||||
final var formatter = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH);
|
||||
return Optional.of(LocalDate.parse(date, formatter));
|
||||
} catch (final DateTimeParseException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@@ -44,8 +44,8 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -247,12 +247,14 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTextualUploadDate() throws ParsingException {
|
||||
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
|
||||
if (getStreamType() == StreamType.LIVE_STREAM) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isPremiere()) {
|
||||
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(getDateFromPremiere());
|
||||
final var localDateTime = LocalDateTime.ofInstant(getInstantFromPremiere(),
|
||||
ZoneId.systemDefault());
|
||||
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(localDateTime);
|
||||
}
|
||||
|
||||
String publishedTimeText = getTextFromObject(videoInfo.getObject("publishedTimeText"));
|
||||
@@ -273,12 +275,12 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
@Nullable
|
||||
@Override
|
||||
public DateWrapper getUploadDate() throws ParsingException {
|
||||
if (getStreamType().equals(StreamType.LIVE_STREAM)) {
|
||||
if (getStreamType() == StreamType.LIVE_STREAM) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isPremiere()) {
|
||||
return new DateWrapper(getDateFromPremiere());
|
||||
return new DateWrapper(getInstantFromPremiere());
|
||||
}
|
||||
|
||||
final String textualUploadDate = getTextualUploadDate();
|
||||
@@ -402,15 +404,15 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
return isPremiere;
|
||||
}
|
||||
|
||||
private OffsetDateTime getDateFromPremiere() throws ParsingException {
|
||||
private Instant getInstantFromPremiere() throws ParsingException {
|
||||
final JsonObject upcomingEventData = videoInfo.getObject("upcomingEventData");
|
||||
final String startTime = upcomingEventData.getString("startTime");
|
||||
|
||||
try {
|
||||
return OffsetDateTime.ofInstant(Instant.ofEpochSecond(Long.parseLong(startTime)),
|
||||
ZoneOffset.UTC);
|
||||
return Instant.ofEpochSecond(Long.parseLong(startTime));
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not parse date from premiere: \"" + startTime + "\"");
|
||||
final String message = "Could not parse date from premiere: \"" + startTime + "\"";
|
||||
throw new ParsingException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ 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.time.format.DateTimeParseException;
|
||||
@@ -292,8 +291,8 @@ public class YoutubeStreamInfoItemLockupExtractor implements StreamInfoItemExtra
|
||||
|
||||
try {
|
||||
// As we request a UTC offset of 0 minutes, we get the UTC date
|
||||
return new DateWrapper(OffsetDateTime.of(LocalDateTime.parse(
|
||||
premiereDate, PREMIERES_DATE_FORMATTER), ZoneOffset.UTC));
|
||||
final var dateTime = LocalDateTime.parse(premiereDate, PREMIERES_DATE_FORMATTER);
|
||||
return new DateWrapper(dateTime.atZone(ZoneOffset.UTC).toInstant(), false);
|
||||
} catch (final DateTimeParseException e) {
|
||||
throw new ParsingException("Could not parse premiere upload date", e);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -203,20 +203,15 @@ abstract class YoutubeChartsBaseKioskExtractor extends KioskExtractor<StreamInfo
|
||||
@Nonnull
|
||||
@Override
|
||||
public DateWrapper getUploadDate() {
|
||||
final JsonObject releaseDate = videoObject.getObject("releaseDate");
|
||||
return new DateWrapper(OffsetDateTime.of(
|
||||
releaseDate.getInt("year"),
|
||||
releaseDate.getInt("month"),
|
||||
releaseDate.getInt("day"),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
// We request that times should be returned with 0 offset to UTC timezone in
|
||||
// the JSON body, but YouTube charts does it only in its HTTP headers
|
||||
ZoneOffset.UTC),
|
||||
// We don't have more info than the release day
|
||||
true);
|
||||
final var releaseDate = videoObject.getObject("releaseDate");
|
||||
final var localDate = LocalDate.of(releaseDate.getInt("year"),
|
||||
releaseDate.getInt("month"), releaseDate.getInt("day"));
|
||||
// We request that times should be returned with 0 offset to UTC timezone in
|
||||
// the JSON body, but YouTube charts does it only in its HTTP headers
|
||||
final var instant = localDate.atStartOfDay(ZoneOffset.UTC).toInstant();
|
||||
|
||||
// We don't have more info than the release day, hence isApproximate=true
|
||||
return new DateWrapper(instant, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,6 +39,7 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -71,7 +72,7 @@ public abstract class StreamExtractor extends Extractor {
|
||||
}
|
||||
|
||||
/**
|
||||
* A more general {@code Calendar} instance set to the date provided by the service.<br>
|
||||
* A more general {@link Instant} instance set to the date provided by the service.<br>
|
||||
* Implementations usually will just parse the date returned from the {@link
|
||||
* #getTextualUploadDate()}.
|
||||
*
|
||||
|
||||
@@ -11,10 +11,8 @@ import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.Month;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -41,13 +39,9 @@ class TimeAgoParserTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void parseTimeAgo(final ParseTimeAgoTestData testData) {
|
||||
final OffsetDateTime now = OffsetDateTime.of(
|
||||
LocalDateTime.of(2020, 1, 1, 1, 1, 1),
|
||||
ZoneOffset.UTC);
|
||||
final TimeAgoParser parser = Objects.requireNonNull(
|
||||
TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT, now));
|
||||
|
||||
final OffsetDateTime expected = testData.getExpectedApplyToNow().apply(now);
|
||||
final var now = LocalDateTime.of(2020, Month.JANUARY, 1, 1, 1, 1);
|
||||
final var parser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT, now);
|
||||
final var expected = testData.getExpectedApplyToNow().apply(now);
|
||||
|
||||
assertAll(
|
||||
Stream.of(
|
||||
@@ -55,7 +49,7 @@ class TimeAgoParserTest {
|
||||
testData.getTextualDateShort())
|
||||
.map(textualDate -> () -> assertEquals(
|
||||
expected,
|
||||
parser.parse(textualDate).offsetDateTime(),
|
||||
parser.parse(textualDate).getLocalDateTime(),
|
||||
"Expected " + expected + " for " + textualDate
|
||||
))
|
||||
);
|
||||
@@ -63,12 +57,12 @@ class TimeAgoParserTest {
|
||||
|
||||
static class ParseTimeAgoTestData {
|
||||
public static final String AGO_SUFFIX = " ago";
|
||||
private final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow;
|
||||
private final Function<LocalDateTime, LocalDateTime> expectedApplyToNow;
|
||||
private final String textualDateLong;
|
||||
private final String textualDateShort;
|
||||
|
||||
ParseTimeAgoTestData(
|
||||
final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow,
|
||||
final Function<LocalDateTime, LocalDateTime> expectedApplyToNow,
|
||||
final String textualDateLong,
|
||||
final String textualDateShort
|
||||
) {
|
||||
@@ -89,17 +83,17 @@ class TimeAgoParserTest {
|
||||
}
|
||||
|
||||
public static ParseTimeAgoTestData greaterThanDay(
|
||||
final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow,
|
||||
final Function<LocalDateTime, LocalDateTime> expectedApplyToNow,
|
||||
final String textualDateLong,
|
||||
final String textualDateShort
|
||||
) {
|
||||
return new ParseTimeAgoTestData(
|
||||
d -> expectedApplyToNow.apply(d).truncatedTo(ChronoUnit.HOURS),
|
||||
expectedApplyToNow.andThen(d -> d.truncatedTo(ChronoUnit.DAYS)),
|
||||
textualDateLong + AGO_SUFFIX,
|
||||
textualDateShort + AGO_SUFFIX);
|
||||
}
|
||||
|
||||
public Function<OffsetDateTime, OffsetDateTime> getExpectedApplyToNow() {
|
||||
public Function<LocalDateTime, LocalDateTime> getExpectedApplyToNow() {
|
||||
return expectedApplyToNow;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import org.schabi.newpipe.extractor.ExtractorAsserts;
|
||||
import org.schabi.newpipe.extractor.InfoItemsCollector;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.MetaInfo;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.ContentAvailability;
|
||||
import org.schabi.newpipe.extractor.stream.Description;
|
||||
@@ -17,6 +16,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -195,18 +195,18 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
|
||||
@Test
|
||||
@Override
|
||||
public void testUploadDate() throws Exception {
|
||||
final DateWrapper dateWrapper = extractor().getUploadDate();
|
||||
final var dateWrapper = extractor().getUploadDate();
|
||||
final var expectedDate = expectedUploadDate();
|
||||
|
||||
if (expectedUploadDate() == null) {
|
||||
if (expectedDate == null) {
|
||||
assertNull(dateWrapper);
|
||||
} else {
|
||||
assertNotNull(dateWrapper);
|
||||
|
||||
final LocalDateTime expectedDateTime = LocalDateTime.parse(expectedUploadDate(),
|
||||
final var expectedDateTime = LocalDateTime.parse(expectedDate,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
|
||||
final LocalDateTime actualDateTime = dateWrapper.offsetDateTime().toLocalDateTime();
|
||||
|
||||
assertEquals(expectedDateTime, actualDateTime);
|
||||
assertEquals(expectedDateTime, dateWrapper.getLocalDateTime(ZoneOffset.UTC));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,11 @@ import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class BandcampRadioStreamExtractorTest extends DefaultStreamExtractorTest {
|
||||
|
||||
@@ -82,14 +83,10 @@ public class BandcampRadioStreamExtractorTest extends DefaultStreamExtractorTest
|
||||
@Override
|
||||
@Test
|
||||
public void testUploadDate() throws ParsingException {
|
||||
final Calendar expectedCalendar = Calendar.getInstance();
|
||||
|
||||
// 16 May 2017 00:00:00 GMT
|
||||
expectedCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
expectedCalendar.setTimeInMillis(0);
|
||||
expectedCalendar.set(2017, Calendar.MAY, 16);
|
||||
|
||||
assertEquals(expectedCalendar.getTimeInMillis(), extractor().getUploadDate().offsetDateTime().toInstant().toEpochMilli());
|
||||
final var expectedDate = LocalDate.of(2017, Month.MAY, 16);
|
||||
final var actualDate = extractor().getUploadDate().getLocalDateTime(ZoneOffset.UTC)
|
||||
.toLocalDate();
|
||||
assertEquals(expectedDate, actualDate);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.extractor.ExtractorAsserts;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
@@ -27,7 +28,8 @@ import javax.annotation.Nullable;
|
||||
public class SoundcloudStreamExtractorTest {
|
||||
private static final String SOUNDCLOUD = "https://soundcloud.com/";
|
||||
|
||||
public static class SoundcloudGeoRestrictedTrack extends DefaultStreamExtractorTest {
|
||||
@Nested
|
||||
class SoundcloudGeoRestrictedTrack extends DefaultStreamExtractorTest {
|
||||
private static final String ID = "one-touch";
|
||||
private static final String UPLOADER = SOUNDCLOUD + "jessglynne";
|
||||
private static final int TIMESTAMP = 0;
|
||||
@@ -63,7 +65,7 @@ public class SoundcloudStreamExtractorTest {
|
||||
@Override public long expectedTimestamp() { return TIMESTAMP; }
|
||||
@Override public long expectedViewCountAtLeast() { return 43000; }
|
||||
@Nullable @Override public String expectedUploadDate() { return "2019-05-16 16:28:45.000"; }
|
||||
@Nullable @Override public String expectedTextualUploadDate() { return "2019-05-16 16:28:45"; }
|
||||
@Nullable @Override public String expectedTextualUploadDate() { return "2019-05-16T16:28:45Z"; }
|
||||
@Override public long expectedLikeCountAtLeast() { return 600; }
|
||||
@Override public long expectedDislikeCountAtLeast() { return -1; }
|
||||
@Override public boolean expectedHasAudioStreams() { return false; }
|
||||
@@ -82,7 +84,8 @@ public class SoundcloudStreamExtractorTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static class SoundcloudGoPlusTrack extends DefaultStreamExtractorTest {
|
||||
@Nested
|
||||
class SoundcloudGoPlusTrack extends DefaultStreamExtractorTest {
|
||||
private static final String ID = "places";
|
||||
private static final String UPLOADER = SOUNDCLOUD + "martinsolveig";
|
||||
private static final int TIMESTAMP = 0;
|
||||
@@ -127,7 +130,7 @@ public class SoundcloudStreamExtractorTest {
|
||||
@Override public long expectedTimestamp() { return TIMESTAMP; }
|
||||
@Override public long expectedViewCountAtLeast() { return 386000; }
|
||||
@Nullable @Override public String expectedUploadDate() { return "2016-11-11 01:16:37.000"; }
|
||||
@Nullable @Override public String expectedTextualUploadDate() { return "2016-11-11 01:16:37"; }
|
||||
@Nullable @Override public String expectedTextualUploadDate() { return "2016-11-11T01:16:37Z"; }
|
||||
@Override public long expectedLikeCountAtLeast() { return 7350; }
|
||||
@Override public long expectedDislikeCountAtLeast() { return -1; }
|
||||
@Override public boolean expectedHasAudioStreams() { return false; }
|
||||
@@ -139,7 +142,8 @@ public class SoundcloudStreamExtractorTest {
|
||||
@Override public String expectedCategory() { return "Dance"; }
|
||||
}
|
||||
|
||||
static class CreativeCommonsOpenMindsEp21 extends DefaultStreamExtractorTest {
|
||||
@Nested
|
||||
class CreativeCommonsOpenMindsEp21 extends DefaultStreamExtractorTest {
|
||||
private static final String ID = "open-minds-ep-21-dr-beth-harris-and-dr-steven-zucker-of-smarthistory";
|
||||
private static final String UPLOADER = SOUNDCLOUD + "wearecc";
|
||||
private static final int TIMESTAMP = 69;
|
||||
@@ -167,7 +171,7 @@ public class SoundcloudStreamExtractorTest {
|
||||
@Override public long expectedTimestamp() { return TIMESTAMP; }
|
||||
@Override public long expectedViewCountAtLeast() { return 15000; }
|
||||
@Nullable @Override public String expectedUploadDate() { return "2022-10-03 18:49:49.000"; }
|
||||
@Nullable @Override public String expectedTextualUploadDate() { return "2022-10-03 18:49:49"; }
|
||||
@Nullable @Override public String expectedTextualUploadDate() { return "2022-10-03T18:49:49Z"; }
|
||||
@Override public long expectedLikeCountAtLeast() { return 10; }
|
||||
@Override public long expectedDislikeCountAtLeast() { return -1; }
|
||||
@Override public boolean expectedHasRelatedItems() { return false; }
|
||||
|
||||
@@ -73,9 +73,8 @@ public class YoutubeChannelLocalizationTest implements InitYoutubeTest {
|
||||
+ "\n:::: " + item.getStreamType() + ", views = " + item.getViewCount();
|
||||
final DateWrapper uploadDate = item.getUploadDate();
|
||||
if (uploadDate != null) {
|
||||
final String dateAsText = dateTimeFormatter.format(uploadDate.offsetDateTime());
|
||||
debugMessage += "\n:::: " + item.getTextualUploadDate() +
|
||||
"\n:::: " + dateAsText;
|
||||
final String dateStr = dateTimeFormatter.format(uploadDate.getLocalDateTime());
|
||||
debugMessage += "\n:::: " + item.getTextualUploadDate() + "\n:::: " + dateStr;
|
||||
}
|
||||
if (DEBUG) System.out.println(debugMessage + "\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user