Skip to content

Commit 485a77d

Browse files
authored
Merge pull request #1212 from Isira-Seneviratne/TimeAgoParser-unused
Remove unused method in TimeAgoParser
2 parents a7154c3 + 17f0b0d commit 485a77d

5 files changed

Lines changed: 141 additions & 233 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoParser.java

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@
77
import java.time.OffsetDateTime;
88
import java.time.ZoneOffset;
99
import java.time.temporal.ChronoUnit;
10-
import java.util.ArrayList;
11-
import java.util.List;
1210
import java.util.Map;
1311
import java.util.regex.Pattern;
14-
import java.util.regex.Matcher;
15-
import java.util.regex.MatchResult;
1612

1713
/**
1814
* A helper class that is meant to be used by services that need to parse durations such as
1915
* {@code 23 seconds} and/or upload dates in the format {@code 2 days ago} or similar.
2016
*/
2117
public class TimeAgoParser {
22-
23-
private static final Pattern DURATION_PATTERN = Pattern.compile("(?:(\\d+) )?([A-z]+)");
24-
2518
private final PatternsHolder patternsHolder;
2619
private final OffsetDateTime now;
2720

@@ -35,8 +28,22 @@ public class TimeAgoParser {
3528
* language word separator.
3629
*/
3730
public TimeAgoParser(final PatternsHolder patternsHolder) {
31+
this(patternsHolder, OffsetDateTime.now(ZoneOffset.UTC));
32+
}
33+
34+
/**
35+
* Creates a helper to parse upload dates in the format '2 days ago'.
36+
* <p>
37+
* Instantiate a new {@link TimeAgoParser} every time you extract a new batch of items.
38+
* </p>
39+
*
40+
* @param patternsHolder An object that holds the "time ago" patterns, special cases, and the
41+
* language word separator.
42+
* @param now The current time
43+
*/
44+
public TimeAgoParser(final PatternsHolder patternsHolder, final OffsetDateTime now) {
3845
this.patternsHolder = patternsHolder;
39-
now = OffsetDateTime.now(ZoneOffset.UTC);
46+
this.now = now;
4047
}
4148

4249
/**
@@ -50,13 +57,11 @@ public TimeAgoParser(final PatternsHolder patternsHolder) {
5057
* @throws ParsingException if the time unit could not be recognized
5158
*/
5259
public DateWrapper parse(final String textualDate) throws ParsingException {
53-
for (final Map.Entry<ChronoUnit, Map<String, Integer>> caseUnitEntry
54-
: patternsHolder.specialCases().entrySet()) {
60+
for (final var caseUnitEntry : patternsHolder.specialCases().entrySet()) {
5561
final ChronoUnit chronoUnit = caseUnitEntry.getKey();
56-
for (final Map.Entry<String, Integer> caseMapToAmountEntry
57-
: caseUnitEntry.getValue().entrySet()) {
62+
for (final var caseMapToAmountEntry : caseUnitEntry.getValue().entrySet()) {
5863
final String caseText = caseMapToAmountEntry.getKey();
59-
final Integer caseAmount = caseMapToAmountEntry.getValue();
64+
final int caseAmount = caseMapToAmountEntry.getValue();
6065

6166
if (textualDateMatches(textualDate, caseText)) {
6267
return getResultFor(caseAmount, chronoUnit);
@@ -67,48 +72,6 @@ public DateWrapper parse(final String textualDate) throws ParsingException {
6772
return getResultFor(parseTimeAgoAmount(textualDate), parseChronoUnit(textualDate));
6873
}
6974

70-
/**
71-
* Parses a textual duration into a duration computer number.
72-
*
73-
* @param textualDuration the textual duration to parse
74-
* @return the textual duration parsed, as a primitive {@code long}
75-
* @throws ParsingException if the textual duration could not be parsed
76-
*/
77-
public long parseDuration(final String textualDuration) throws ParsingException {
78-
// We can't use Matcher.results, as it is only available on Android 14 and above
79-
final Matcher matcher = DURATION_PATTERN.matcher(textualDuration);
80-
final List<MatchResult> results = new ArrayList<>();
81-
while (matcher.find()) {
82-
results.add(matcher.toMatchResult());
83-
}
84-
85-
return results.stream()
86-
.map(match -> {
87-
final String digits = match.group(1);
88-
final String word = match.group(2);
89-
90-
int amount;
91-
try {
92-
amount = Integer.parseInt(digits);
93-
} catch (final NumberFormatException ignored) {
94-
amount = 1;
95-
}
96-
97-
final ChronoUnit unit;
98-
try {
99-
unit = parseChronoUnit(word);
100-
} catch (final ParsingException ignored) {
101-
return 0L;
102-
}
103-
104-
return amount * unit.getDuration().getSeconds();
105-
})
106-
.filter(n -> n > 0)
107-
.reduce(Long::sum)
108-
.orElseThrow(() -> new ParsingException(
109-
"Could not parse duration \"" + textualDuration + "\""));
110-
}
111-
11275
private int parseTimeAgoAmount(final String textualDate) {
11376
try {
11477
return Integer.parseInt(textualDate.replaceAll("\\D+", ""));

extractor/src/main/java/org/schabi/newpipe/extractor/localization/TimeAgoPatternsManager.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
44
import org.schabi.newpipe.extractor.timeago.PatternsManager;
55

6+
import java.time.OffsetDateTime;
7+
68
import javax.annotation.Nonnull;
79
import javax.annotation.Nullable;
810

@@ -26,4 +28,17 @@ public static TimeAgoParser getTimeAgoParserFor(@Nonnull final Localization loca
2628

2729
return new TimeAgoParser(holder);
2830
}
31+
32+
@Nullable
33+
public static TimeAgoParser getTimeAgoParserFor(
34+
@Nonnull final Localization localization,
35+
@Nonnull final OffsetDateTime now) {
36+
final PatternsHolder holder = getPatternsFor(localization);
37+
38+
if (holder == null) {
39+
return null;
40+
}
41+
42+
return new TimeAgoParser(holder, now);
43+
}
2944
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ private YoutubeParsingHelper() {
239239
private static final String IOS_OS_VERSION = "18.1.0.22B83";
240240

241241
/**
242-
* Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app. To be
243-
* used in the user agent for requests.
242+
* Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app.
243+
* To be used in the user agent for requests.
244244
*
245245
* @see #IOS_OS_VERSION
246246
*/
@@ -1412,7 +1412,8 @@ public static String getAndroidUserAgent(@Nullable final Localization localizati
14121412
*/
14131413
@Nonnull
14141414
public static String getIosUserAgent(@Nullable final Localization localization) {
1415-
// Spoofing an iPhone 15 Pro Max running iOS 18.1.0 with the hardcoded version of the iOS app
1415+
// Spoofing an iPhone 15 Pro Max running iOS 18.1.0
1416+
// with the hardcoded version of the iOS app
14161417
return "com.google.ios.youtube/" + IOS_YOUTUBE_CLIENT_VERSION
14171418
+ "(" + IOS_DEVICE_MODEL + "; U; CPU iOS "
14181419
+ IOS_USER_AGENT_VERSION + " like Mac OS X; "
Lines changed: 104 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,114 @@
11
package org.schabi.newpipe.extractor.localization;
22

3-
import org.junit.jupiter.api.BeforeAll;
4-
import org.junit.jupiter.api.Test;
5-
import org.schabi.newpipe.extractor.exceptions.ParsingException;
6-
3+
import static org.junit.jupiter.api.Assertions.assertAll;
74
import static org.junit.jupiter.api.Assertions.assertEquals;
8-
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.schabi.newpipe.extractor.localization.TimeAgoParserTest.ParseTimeAgoTestData.greaterThanDay;
6+
import static org.schabi.newpipe.extractor.localization.TimeAgoParserTest.ParseTimeAgoTestData.lessThanDay;
97

10-
class TimeAgoParserTest {
11-
private static TimeAgoParser timeAgoParser;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.Arguments;
10+
import org.junit.jupiter.params.provider.MethodSource;
11+
12+
import java.time.Duration;
13+
import java.time.LocalDateTime;
14+
import java.time.OffsetDateTime;
15+
import java.time.ZoneOffset;
16+
import java.time.temporal.ChronoUnit;
17+
import java.util.Objects;
18+
import java.util.function.Function;
19+
import java.util.stream.Stream;
1220

13-
@BeforeAll
14-
static void setUp() {
15-
timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT);
21+
class TimeAgoParserTest {
22+
public static Stream<Arguments> parseTimeAgo() {
23+
return Stream.of(
24+
lessThanDay(Duration.ofSeconds(1), "1 second", "1 sec"),
25+
lessThanDay(Duration.ofSeconds(12), "12 second", "12 sec"),
26+
lessThanDay(Duration.ofMinutes(1), "1 minute", "1 min"),
27+
lessThanDay(Duration.ofMinutes(23), "23 minutes", "23 min"),
28+
lessThanDay(Duration.ofHours(1), "1 hour", "1 hr"),
29+
lessThanDay(Duration.ofHours(8), "8 hour", "8 hr"),
30+
greaterThanDay(d -> d.minusDays(1), "1 day", "1 day"),
31+
greaterThanDay(d -> d.minusDays(3), "3 days", "3 day"),
32+
greaterThanDay(d -> d.minusWeeks(1), "1 week", "1 wk"),
33+
greaterThanDay(d -> d.minusWeeks(3), "3 weeks", "3 wk"),
34+
greaterThanDay(d -> d.minusMonths(1), "1 month", "1 mo"),
35+
greaterThanDay(d -> d.minusMonths(3), "3 months", "3 mo"),
36+
greaterThanDay(d -> d.minusYears(1).minusDays(1), "1 year", "1 yr"),
37+
greaterThanDay(d -> d.minusYears(3).minusDays(1), "3 years", "3 yr")
38+
).map(Arguments::of);
1639
}
1740

18-
@Test
19-
void testGetDuration() throws ParsingException {
20-
assertEquals(1, timeAgoParser.parseDuration("one second"));
21-
assertEquals(1, timeAgoParser.parseDuration("second"));
22-
assertEquals(49, timeAgoParser.parseDuration("49 seconds"));
23-
assertEquals(61, timeAgoParser.parseDuration("1 minute, 1 second"));
41+
@ParameterizedTest
42+
@MethodSource
43+
void parseTimeAgo(final ParseTimeAgoTestData testData) {
44+
final OffsetDateTime now = OffsetDateTime.of(
45+
LocalDateTime.of(2020, 1, 1, 1, 1, 1),
46+
ZoneOffset.UTC);
47+
final TimeAgoParser parser = Objects.requireNonNull(
48+
TimeAgoPatternsManager.getTimeAgoParserFor(Localization.DEFAULT, now));
49+
50+
final OffsetDateTime expected = testData.getExpectedApplyToNow().apply(now);
51+
52+
assertAll(
53+
Stream.of(
54+
testData.getTextualDateLong(),
55+
testData.getTextualDateShort())
56+
.map(textualDate -> () -> assertEquals(
57+
expected,
58+
parser.parse(textualDate).offsetDateTime(),
59+
"Expected " + expected + " for " + textualDate
60+
))
61+
);
2462
}
2563

26-
@Test
27-
void testGetDurationError() {
28-
assertThrows(ParsingException.class, () -> timeAgoParser.parseDuration("abcd"));
29-
assertThrows(ParsingException.class, () -> timeAgoParser.parseDuration("12 abcd"));
64+
static class ParseTimeAgoTestData {
65+
public static final String AGO_SUFFIX = " ago";
66+
private final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow;
67+
private final String textualDateLong;
68+
private final String textualDateShort;
69+
70+
ParseTimeAgoTestData(
71+
final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow,
72+
final String textualDateLong,
73+
final String textualDateShort
74+
) {
75+
this.expectedApplyToNow = expectedApplyToNow;
76+
this.textualDateLong = textualDateLong;
77+
this.textualDateShort = textualDateShort;
78+
}
79+
80+
public static ParseTimeAgoTestData lessThanDay(
81+
final Duration duration,
82+
final String textualDateLong,
83+
final String textualDateShort
84+
) {
85+
return new ParseTimeAgoTestData(
86+
d -> d.minus(duration),
87+
textualDateLong + AGO_SUFFIX,
88+
textualDateShort + AGO_SUFFIX);
89+
}
90+
91+
public static ParseTimeAgoTestData greaterThanDay(
92+
final Function<OffsetDateTime, OffsetDateTime> expectedApplyToNow,
93+
final String textualDateLong,
94+
final String textualDateShort
95+
) {
96+
return new ParseTimeAgoTestData(
97+
d -> expectedApplyToNow.apply(d).truncatedTo(ChronoUnit.HOURS),
98+
textualDateLong + AGO_SUFFIX,
99+
textualDateShort + AGO_SUFFIX);
100+
}
101+
102+
public Function<OffsetDateTime, OffsetDateTime> getExpectedApplyToNow() {
103+
return expectedApplyToNow;
104+
}
105+
106+
public String getTextualDateLong() {
107+
return textualDateLong;
108+
}
109+
110+
public String getTextualDateShort() {
111+
return textualDateShort;
112+
}
30113
}
31-
}
114+
}

0 commit comments

Comments
 (0)