Merge pull request #1212 from Isira-Seneviratne/TimeAgoParser-unused

Remove unused method in TimeAgoParser
This commit is contained in:
litetex 2025-01-23 21:54:55 +01:00 committed by GitHub
commit 485a77d885
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 141 additions and 233 deletions

View File

@ -7,21 +7,14 @@ import org.schabi.newpipe.extractor.utils.Parser;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.regex.MatchResult;
/** /**
* A helper class that is meant to be used by services that need to parse durations such as * A helper class that is meant to be used by services that need to parse durations such as
* {@code 23 seconds} and/or upload dates in the format {@code 2 days ago} or similar. * {@code 23 seconds} and/or upload dates in the format {@code 2 days ago} or similar.
*/ */
public class TimeAgoParser { public class TimeAgoParser {
private static final Pattern DURATION_PATTERN = Pattern.compile("(?:(\\d+) )?([A-z]+)");
private final PatternsHolder patternsHolder; private final PatternsHolder patternsHolder;
private final OffsetDateTime now; private final OffsetDateTime now;
@ -35,8 +28,22 @@ public class TimeAgoParser {
* language word separator. * language word separator.
*/ */
public TimeAgoParser(final PatternsHolder patternsHolder) { public TimeAgoParser(final PatternsHolder patternsHolder) {
this(patternsHolder, OffsetDateTime.now(ZoneOffset.UTC));
}
/**
* 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.
* @param now The current time
*/
public TimeAgoParser(final PatternsHolder patternsHolder, final OffsetDateTime now) {
this.patternsHolder = patternsHolder; this.patternsHolder = patternsHolder;
now = OffsetDateTime.now(ZoneOffset.UTC); this.now = now;
} }
/** /**
@ -50,13 +57,11 @@ public class TimeAgoParser {
* @throws ParsingException if the time unit could not be recognized * @throws ParsingException if the time unit could not be recognized
*/ */
public DateWrapper parse(final String textualDate) throws ParsingException { public DateWrapper parse(final String textualDate) throws ParsingException {
for (final Map.Entry<ChronoUnit, Map<String, Integer>> caseUnitEntry for (final var caseUnitEntry : patternsHolder.specialCases().entrySet()) {
: patternsHolder.specialCases().entrySet()) {
final ChronoUnit chronoUnit = caseUnitEntry.getKey(); final ChronoUnit chronoUnit = caseUnitEntry.getKey();
for (final Map.Entry<String, Integer> caseMapToAmountEntry for (final var caseMapToAmountEntry : caseUnitEntry.getValue().entrySet()) {
: caseUnitEntry.getValue().entrySet()) {
final String caseText = caseMapToAmountEntry.getKey(); final String caseText = caseMapToAmountEntry.getKey();
final Integer caseAmount = caseMapToAmountEntry.getValue(); final int caseAmount = caseMapToAmountEntry.getValue();
if (textualDateMatches(textualDate, caseText)) { if (textualDateMatches(textualDate, caseText)) {
return getResultFor(caseAmount, chronoUnit); return getResultFor(caseAmount, chronoUnit);
@ -67,48 +72,6 @@ public class TimeAgoParser {
return getResultFor(parseTimeAgoAmount(textualDate), parseChronoUnit(textualDate)); return getResultFor(parseTimeAgoAmount(textualDate), parseChronoUnit(textualDate));
} }
/**
* Parses a textual duration into a duration computer number.
*
* @param textualDuration the textual duration to parse
* @return the textual duration parsed, as a primitive {@code long}
* @throws ParsingException if the textual duration could not be parsed
*/
public long parseDuration(final String textualDuration) throws ParsingException {
// We can't use Matcher.results, as it is only available on Android 14 and above
final Matcher matcher = DURATION_PATTERN.matcher(textualDuration);
final List<MatchResult> results = new ArrayList<>();
while (matcher.find()) {
results.add(matcher.toMatchResult());
}
return results.stream()
.map(match -> {
final String digits = match.group(1);
final String word = match.group(2);
int amount;
try {
amount = Integer.parseInt(digits);
} catch (final NumberFormatException ignored) {
amount = 1;
}
final ChronoUnit unit;
try {
unit = parseChronoUnit(word);
} catch (final ParsingException ignored) {
return 0L;
}
return amount * unit.getDuration().getSeconds();
})
.filter(n -> n > 0)
.reduce(Long::sum)
.orElseThrow(() -> new ParsingException(
"Could not parse duration \"" + textualDuration + "\""));
}
private int parseTimeAgoAmount(final String textualDate) { private int parseTimeAgoAmount(final String textualDate) {
try { try {
return Integer.parseInt(textualDate.replaceAll("\\D+", "")); return Integer.parseInt(textualDate.replaceAll("\\D+", ""));

View File

@ -3,6 +3,8 @@ package org.schabi.newpipe.extractor.localization;
import org.schabi.newpipe.extractor.timeago.PatternsHolder; import org.schabi.newpipe.extractor.timeago.PatternsHolder;
import org.schabi.newpipe.extractor.timeago.PatternsManager; import org.schabi.newpipe.extractor.timeago.PatternsManager;
import java.time.OffsetDateTime;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -26,4 +28,17 @@ public final class TimeAgoPatternsManager {
return new TimeAgoParser(holder); return new TimeAgoParser(holder);
} }
@Nullable
public static TimeAgoParser getTimeAgoParserFor(
@Nonnull final Localization localization,
@Nonnull final OffsetDateTime now) {
final PatternsHolder holder = getPatternsFor(localization);
if (holder == null) {
return null;
}
return new TimeAgoParser(holder, now);
}
} }

View File

@ -239,8 +239,8 @@ public final class YoutubeParsingHelper {
private static final String IOS_OS_VERSION = "18.1.0.22B83"; private static final String IOS_OS_VERSION = "18.1.0.22B83";
/** /**
* Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app. To be * Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app.
* used in the user agent for requests. * To be used in the user agent for requests.
* *
* @see #IOS_OS_VERSION * @see #IOS_OS_VERSION
*/ */
@ -1412,7 +1412,8 @@ public final class YoutubeParsingHelper {
*/ */
@Nonnull @Nonnull
public static String getIosUserAgent(@Nullable final Localization localization) { public static String getIosUserAgent(@Nullable final Localization localization) {
// Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app // Spoofing an iPhone 15 Pro Max running iOS 18.1.0
// with the hardcoded version of the iOS app
return "com.google.ios.youtube/" + IOS_YOUTUBE_CLIENT_VERSION return "com.google.ios.youtube/" + IOS_YOUTUBE_CLIENT_VERSION
+ "(" + IOS_DEVICE_MODEL + "; U; CPU iOS " + "(" + IOS_DEVICE_MODEL + "; U; CPU iOS "
+ IOS_USER_AGENT_VERSION + " like Mac OS X; " + IOS_USER_AGENT_VERSION + " like Mac OS X; "

View File

@ -1,31 +1,114 @@
package org.schabi.newpipe.extractor.localization; package org.schabi.newpipe.extractor.localization;
import org.junit.jupiter.api.BeforeAll; import static org.junit.jupiter.api.Assertions.assertAll;
import org.junit.jupiter.api.Test;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.schabi.newpipe.extractor.localization.TimeAgoParserTest.ParseTimeAgoTestData.greaterThanDay;
import static org.schabi.newpipe.extractor.localization.TimeAgoParserTest.ParseTimeAgoTestData.lessThanDay;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
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.temporal.ChronoUnit;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
class TimeAgoParserTest { class TimeAgoParserTest {
private static TimeAgoParser timeAgoParser; public static Stream<Arguments> parseTimeAgo() {
return Stream.of(
@BeforeAll lessThanDay(Duration.ofSeconds(1), "1 second", "1 sec"),
static void setUp() { lessThanDay(Duration.ofSeconds(12), "12 second", "12 sec"),
timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT); lessThanDay(Duration.ofMinutes(1), "1 minute", "1 min"),
lessThanDay(Duration.ofMinutes(23), "23 minutes", "23 min"),
lessThanDay(Duration.ofHours(1), "1 hour", "1 hr"),
lessThanDay(Duration.ofHours(8), "8 hour", "8 hr"),
greaterThanDay(d -> d.minusDays(1), "1 day", "1 day"),
greaterThanDay(d -> d.minusDays(3), "3 days", "3 day"),
greaterThanDay(d -> d.minusWeeks(1), "1 week", "1 wk"),
greaterThanDay(d -> d.minusWeeks(3), "3 weeks", "3 wk"),
greaterThanDay(d -> d.minusMonths(1), "1 month", "1 mo"),
greaterThanDay(d -> d.minusMonths(3), "3 months", "3 mo"),
greaterThanDay(d -> d.minusYears(1).minusDays(1), "1 year", "1 yr"),
greaterThanDay(d -> d.minusYears(3).minusDays(1), "3 years", "3 yr")
).map(Arguments::of);
} }
@Test @ParameterizedTest
void testGetDuration() throws ParsingException { @MethodSource
assertEquals(1, timeAgoParser.parseDuration("one second")); void parseTimeAgo(final ParseTimeAgoTestData testData) {
assertEquals(1, timeAgoParser.parseDuration("second")); final OffsetDateTime now = OffsetDateTime.of(
assertEquals(49, timeAgoParser.parseDuration("49 seconds")); LocalDateTime.of(2020, 1, 1, 1, 1, 1),
assertEquals(61, timeAgoParser.parseDuration("1 minute, 1 second")); ZoneOffset.UTC);
final TimeAgoParser parser = Objects.requireNonNull(
TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT, now));
final OffsetDateTime expected = testData.getExpectedApplyToNow().apply(now);
assertAll(
Stream.of(
testData.getTextualDateLong(),
testData.getTextualDateShort())
.map(textualDate -> () -> assertEquals(
expected,
parser.parse(textualDate).offsetDateTime(),
"Expected " + expected + " for " + textualDate
))
);
} }
@Test static class ParseTimeAgoTestData {
void testGetDurationError() { public static final String AGO_SUFFIX = " ago";
assertThrows(ParsingException.class, () -> timeAgoParser.parseDuration("abcd")); private final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow;
assertThrows(ParsingException.class, () -> timeAgoParser.parseDuration("12 abcd")); private final String textualDateLong;
private final String textualDateShort;
ParseTimeAgoTestData(
final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow,
final String textualDateLong,
final String textualDateShort
) {
this.expectedApplyToNow = expectedApplyToNow;
this.textualDateLong = textualDateLong;
this.textualDateShort = textualDateShort;
}
public static ParseTimeAgoTestData lessThanDay(
final Duration duration,
final String textualDateLong,
final String textualDateShort
) {
return new ParseTimeAgoTestData(
d -> d.minus(duration),
textualDateLong + AGO_SUFFIX,
textualDateShort + AGO_SUFFIX);
}
public static ParseTimeAgoTestData greaterThanDay(
final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow,
final String textualDateLong,
final String textualDateShort
) {
return new ParseTimeAgoTestData(
d -> expectedApplyToNow.apply(d).truncatedTo(ChronoUnit.HOURS),
textualDateLong + AGO_SUFFIX,
textualDateShort + AGO_SUFFIX);
}
public Function<OffsetDateTime, OffsetDateTime> getExpectedApplyToNow() {
return expectedApplyToNow;
}
public String getTextualDateLong() {
return textualDateLong;
}
public String getTextualDateShort() {
return textualDateShort;
}
} }
} }

View File

@ -1,154 +0,0 @@
package org.schabi.newpipe.extractor.utils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.localization.TimeAgoPatternsManager;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TimeagoTest {
private static TimeAgoParser parser;
private static OffsetDateTime now;
@BeforeAll
public static void setUp() {
parser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT);
now = OffsetDateTime.now(ZoneOffset.UTC);
}
@Test
void parseTimeago() throws ParsingException {
assertTimeWithin1s(
now.minus(1, ChronoUnit.SECONDS),
parser.parse("1 second ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(12, ChronoUnit.SECONDS),
parser.parse("12 second ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(1, ChronoUnit.MINUTES),
parser.parse("1 minute ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(23, ChronoUnit.MINUTES),
parser.parse("23 minutes ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(1, ChronoUnit.HOURS),
parser.parse("1 hour ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(8, ChronoUnit.HOURS),
parser.parse("8 hours ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 day ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.DAYS).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 days ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.WEEKS).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 week ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.WEEKS).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 weeks ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.MONTHS).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 month ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.MONTHS).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 months ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.YEARS).minusDays(1).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 year ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.YEARS).minusDays(1).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 years ago").offsetDateTime()
);
}
@Test
void parseTimeagoShort() throws ParsingException {
final TimeAgoParser parser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT);
final OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
assertTimeWithin1s(
now.minus(1, ChronoUnit.SECONDS),
parser.parse("1 sec ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(12, ChronoUnit.SECONDS),
parser.parse("12 sec ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(1, ChronoUnit.MINUTES),
parser.parse("1 min ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(23, ChronoUnit.MINUTES),
parser.parse("23 min ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(1, ChronoUnit.HOURS),
parser.parse("1 hr ago").offsetDateTime()
);
assertTimeWithin1s(
now.minus(8, ChronoUnit.HOURS),
parser.parse("8 hr ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 day ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.DAYS).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 days ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.WEEKS).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 wk ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.WEEKS).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 wk ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.MONTHS).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 mo ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.MONTHS).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 mo ago").offsetDateTime()
);
assertEquals(
now.minus(1, ChronoUnit.YEARS).minusDays(1).truncatedTo(ChronoUnit.HOURS),
parser.parse("1 yr ago").offsetDateTime()
);
assertEquals(
now.minus(3, ChronoUnit.YEARS).minusDays(1).truncatedTo(ChronoUnit.HOURS),
parser.parse("3 yr ago").offsetDateTime()
);
}
void assertTimeWithin1s(final OffsetDateTime expected, final OffsetDateTime actual) {
final long delta = Math.abs(expected.toEpochSecond() - actual.toEpochSecond());
assertTrue(delta <= 1, String.format("Expected: %s\nActual: %s", expected, actual));
}
}