This commit is contained in:
Isira Seneviratne 2023-12-11 04:24:53 -08:00 committed by GitHub
commit 84ec30dded
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 176 additions and 154 deletions

View File

@ -4,15 +4,12 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.timeago.PatternsHolder;
import org.schabi.newpipe.extractor.utils.Parser;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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
@ -68,45 +65,37 @@ public class TimeAgoParser {
}
/**
* Parses a textual duration into a duration computer number.
* Parses a textual duration into a {@link Duration} object.
*
* @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
* @return the textual duration parsed as a {@link Duration}
*/
public long parseDuration(final String textualDuration) throws ParsingException {
public Duration 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<>();
final var matcher = DURATION_PATTERN.matcher(textualDuration);
var duration = Duration.ZERO;
while (matcher.find()) {
results.add(matcher.toMatchResult());
final var match = matcher.toMatchResult();
final String digits = match.group(1);
final String word = match.group(2);
long amount;
try {
amount = Long.parseLong(digits);
} catch (final NumberFormatException ignored) {
amount = 1;
}
try {
duration = duration.plus(amount, parseChronoUnit(word));
} catch (final ParsingException ignored) {
}
}
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 + "\""));
if (duration.isZero()) {
throw new ParsingException("Could not parse duration \"" + textualDuration + "\"");
}
return duration;
}
private int parseTimeAgoAmount(final String textualDate) {

View File

@ -2,21 +2,23 @@
package org.schabi.newpipe.extractor.services.bandcamp.extractors;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImagesFromImageId;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.parseDate;
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.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Duration;
import java.util.List;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.BASE_URL;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImagesFromImageId;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.parseDate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
@ -26,13 +28,14 @@ public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
show = radioShow;
}
@Nonnull
@Override
public long getDuration() {
public Duration getDuration() {
/* Duration is only present in the more detailed information that has to be queried
separately. Therefore, over 300 queries would be needed every time the kiosk is opened if we
were to display the real value. */
//return query(show.getInt("id")).getLong("audio_duration");
return 0;
return Duration.ZERO;
}
@Nullable

View File

@ -1,14 +1,16 @@
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImagesFromImageId;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper;
import javax.annotation.Nonnull;
import java.util.List;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImagesFromImageId;
import javax.annotation.Nonnull;
public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
@ -43,9 +45,4 @@ public class BandcampDiscographStreamInfoItemExtractor extends BandcampStreamInf
public List<Image> getThumbnails() throws ParsingException {
return getImagesFromImageId(discograph.getLong("art_id"), true);
}
@Override
public long getDuration() {
return -1;
}
}

View File

@ -3,17 +3,20 @@
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
private final JsonObject track;
@ -46,9 +49,10 @@ public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoI
return getUploaderUrl() + track.getString("title_link");
}
@Nonnull
@Override
public long getDuration() {
return track.getLong("duration");
public Duration getDuration() {
return Duration.ofSeconds(track.getLong("duration"));
}
@Override

View File

@ -1,13 +1,14 @@
package org.schabi.newpipe.extractor.services.bandcamp.extractors.streaminfoitem;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImagesFromSearchResult;
import org.jsoup.nodes.Element;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import javax.annotation.Nonnull;
import java.util.List;
import static org.schabi.newpipe.extractor.services.bandcamp.extractors.BandcampExtractorHelper.getImagesFromSearchResult;
import javax.annotation.Nonnull;
public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoItemExtractor {
@ -47,9 +48,4 @@ public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoIte
public List<Image> getThumbnails() throws ParsingException {
return getImagesFromSearchResult(searchResult);
}
@Override
public long getDuration() {
return -1;
}
}

View File

@ -1,17 +1,19 @@
package org.schabi.newpipe.extractor.services.media_ccc.extractors;
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromLiveStreamItem;
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.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromLiveStreamItem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor {
@ -60,11 +62,6 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
return false;
}
@Override
public long getDuration() throws ParsingException {
return 0;
}
@Override
public long getViewCount() throws ParsingException {
return -1;

View File

@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import java.io.IOException;
import java.time.Duration;
import java.util.Comparator;
import javax.annotation.Nonnull;
@ -64,7 +65,7 @@ public class MediaCCCRecentKiosk extends KioskExtractor<StreamInfoItem> {
.map(JsonObject.class::cast)
.map(MediaCCCRecentKioskExtractor::new)
// #813 / voc/voctoweb#609 -> returns faulty data -> filter it out
.filter(extractor -> extractor.getDuration() > 0)
.filter(extractor -> extractor.getDuration().compareTo(Duration.ZERO) > 0)
.forEach(collector::commit);
return new InfoItemsPage<>(collector, null);

View File

@ -9,6 +9,7 @@ import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConfe
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@ -52,11 +53,12 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
return false;
}
@Nonnull
@Override
public long getDuration() {
public Duration getDuration() {
// duration and length have the same value, see
// https://github.com/voc/voctoweb/blob/master/app/views/public/shared/_event.json.jbuilder
return event.getInt("duration");
return Duration.ofSeconds(event.getLong("duration"));
}
@Override

View File

@ -1,6 +1,9 @@
package org.schabi.newpipe.extractor.services.media_ccc.extractors.infoItems;
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromStreamItem;
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;
@ -8,11 +11,11 @@ import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsin
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Duration;
import java.util.List;
import static org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper.getThumbnailsFromStreamItem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor {
private final JsonObject event;
@ -31,9 +34,10 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
return false;
}
@Nonnull
@Override
public long getDuration() {
return event.getInt("length");
public Duration getDuration() {
return Duration.ofSeconds(event.getInt("length"));
}
@Override

View File

@ -1,6 +1,11 @@
package org.schabi.newpipe.extractor.services.peertube.extractors;
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;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
@ -9,12 +14,10 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import javax.annotation.Nonnull;
import java.time.Duration;
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;
import javax.annotation.Nonnull;
public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
@ -99,9 +102,10 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
return item.getBoolean("isLive") ? StreamType.LIVE_STREAM : StreamType.VIDEO_STREAM;
}
@Nonnull
@Override
public long getDuration() {
return item.getLong("duration");
public Duration getDuration() {
return Duration.ofSeconds(item.getLong("duration"));
}
protected void setBaseUrl(final String baseUrl) {

View File

@ -1,5 +1,10 @@
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
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.utils.Utils.replaceHttpWithHttps;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Image;
@ -8,13 +13,10 @@ import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import javax.annotation.Nonnull;
import java.time.Duration;
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.utils.Utils.replaceHttpWithHttps;
import javax.annotation.Nonnull;
public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtractor {
@ -34,9 +36,10 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
return itemObject.getString("title");
}
@Nonnull
@Override
public long getDuration() {
return itemObject.getLong("duration") / 1000L;
public Duration getDuration() {
return Duration.ofMillis(itemObject.getLong("duration"));
}
@Override

View File

@ -32,8 +32,8 @@ import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import com.grack.nanojson.JsonWriter;
import org.jsoup.nodes.Entities;
import org.jsoup.nodes.Entities;
import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
import org.schabi.newpipe.extractor.MetaInfo;
@ -58,10 +58,12 @@ import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -301,21 +303,21 @@ public final class YoutubeParsingHelper {
* @return the duration in seconds
* @throws ParsingException when more than 3 separators are found
*/
public static int parseDurationString(@Nonnull final String input)
@Nonnull
public static Duration parseDurationString(@Nonnull final String input)
throws ParsingException, NumberFormatException {
// If time separator : is not detected, try . instead
final String[] splitInput = input.contains(":")
? input.split(":")
: input.split("\\.");
final var splitInput = input.contains(":") ? input.split(":") : input.split("\\.");
final int[] units = {24, 60, 60, 1};
final var units = new ChronoUnit[]{ChronoUnit.DAYS, ChronoUnit.HOURS, ChronoUnit.MINUTES,
ChronoUnit.SECONDS};
final int offset = units.length - splitInput.length;
if (offset < 0) {
throw new ParsingException("Error duration string with unknown format: " + input);
}
int duration = 0;
Duration duration = Duration.ZERO;
for (int i = 0; i < splitInput.length; i++) {
duration = units[i + offset] * (duration + convertDurationToInt(splitInput[i]));
duration = duration.plus(convertDurationToInt(splitInput[i]), units[i + offset]);
}
return duration;
}

View File

@ -8,12 +8,13 @@ import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
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;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
private final Element entryElement;
@ -33,12 +34,6 @@ public class YoutubeFeedInfoItemExtractor implements StreamInfoItemExtractor {
return false;
}
@Override
public long getDuration() {
// Not available when fetching through the feed endpoint.
return -1;
}
@Override
public long getViewCount() {
return Long.parseLong(entryElement.getElementsByTag("media:statistics").first()

View File

@ -1,7 +1,15 @@
package org.schabi.newpipe.extractor.services.youtube.extractors;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_SONGS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_VIDEOS;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import com.grack.nanojson.JsonArray;
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;
@ -11,15 +19,10 @@ import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import java.time.Duration;
import java.util.List;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_SONGS;
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_VIDEOS;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import javax.annotation.Nonnull;
public class YoutubeMusicSongOrVideoInfoItemExtractor implements StreamInfoItemExtractor {
private final JsonObject songOrVideoInfoItem;
@ -66,8 +69,9 @@ public class YoutubeMusicSongOrVideoInfoItemExtractor implements StreamInfoItemE
return false;
}
@Nonnull
@Override
public long getDuration() throws ParsingException {
public Duration getDuration() throws ParsingException {
final String duration = descriptionElements.getObject(descriptionElements.size() - 1)
.getString("text");
if (!isNullOrEmpty(duration)) {

View File

@ -1,5 +1,9 @@
package org.schabi.newpipe.extractor.services.youtube.extractors;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getThumbnailsFromInfoItem;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import com.grack.nanojson.JsonObject;
import org.schabi.newpipe.extractor.Image;
@ -11,15 +15,12 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.utils.Utils;
import java.time.Duration;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getThumbnailsFromInfoItem;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import java.util.List;
/**
* A {@link StreamInfoItemExtractor} for YouTube's {@code reelItemRenderers}.
*
@ -68,15 +69,16 @@ public class YoutubeReelInfoItemExtractor implements StreamInfoItemExtractor {
return StreamType.VIDEO_STREAM;
}
@Nonnull
@Override
public long getDuration() throws ParsingException {
public Duration getDuration() throws ParsingException {
// Duration of reelItems is only provided in the accessibility data
// example: "VIDEO TITLE - 49 seconds - play video"
// "VIDEO TITLE - 1 minute, 1 second - play video"
final String accessibilityLabel = reelInfo.getObject("accessibility")
.getObject("accessibilityData").getString("label");
if (accessibilityLabel == null || timeAgoParser == null) {
return 0;
return Duration.ZERO;
}
// This approach may be language dependent
@ -87,7 +89,7 @@ public class YoutubeReelInfoItemExtractor implements StreamInfoItemExtractor {
return timeAgoParser.parseDuration(textualDuration);
}
return -1;
return Duration.ZERO;
}
@Override

View File

@ -18,9 +18,9 @@
package org.schabi.newpipe.extractor.services.youtube.extractors;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getThumbnailsFromInfoItem;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@ -39,9 +39,7 @@ import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Utils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
@ -49,6 +47,9 @@ import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
private static final Pattern ACCESSIBILITY_DATA_VIEW_COUNT_REGEX =
@ -136,10 +137,11 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
throw new ParsingException("Could not get name");
}
@Nonnull
@Override
public long getDuration() throws ParsingException {
public Duration getDuration() throws ParsingException {
if (getStreamType() == StreamType.LIVE_STREAM) {
return -1;
return Duration.ZERO;
}
String duration = getTextFromObject(videoInfo.getObject("lengthText"));
@ -169,7 +171,7 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
if (isPremiere()) {
// Premieres can be livestreams, so the duration is not available in this
// case
return -1;
return Duration.ZERO;
}
throw new ParsingException("Could not get duration");

View File

@ -24,9 +24,11 @@ import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import java.time.Duration;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
/**
* Info object for previews of unopened videos, e.g. search results, related videos.
@ -40,7 +42,7 @@ public class StreamInfoItem extends InfoItem {
@Nullable
private DateWrapper uploadDate;
private long viewCount = -1;
private long duration = -1;
private Duration duration = Duration.ZERO;
private String uploaderUrl = null;
@Nonnull
@ -76,11 +78,15 @@ public class StreamInfoItem extends InfoItem {
this.viewCount = viewCount;
}
public long getDuration() {
public Duration getDuration() {
return duration;
}
public void setDuration(final long duration) {
public long getDurationInSeconds() {
return duration.toSeconds();
}
public void setDuration(final Duration duration) {
this.duration = duration;
}

View File

@ -25,9 +25,11 @@ import org.schabi.newpipe.extractor.InfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import java.time.Duration;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
public interface StreamInfoItemExtractor extends InfoItemExtractor {
@ -48,12 +50,15 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
boolean isAd() throws ParsingException;
/**
* Get the stream duration in seconds
* Get the stream duration. If it is not available, a zero-length duration is returned.
*
* @return the stream duration in seconds
* @return the stream duration
* @throws ParsingException if there is an error in the extraction
*/
long getDuration() throws ParsingException;
@Nonnull
default Duration getDuration() throws ParsingException {
return Duration.ZERO;
}
/**
* Parses the number of views

View File

@ -1,11 +1,13 @@
package org.schabi.newpipe.extractor.localization;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeAll;
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.assertThrows;
import java.time.Duration;
class TimeAgoParserTest {
private static TimeAgoParser timeAgoParser;
@ -17,10 +19,10 @@ class TimeAgoParserTest {
@Test
void testGetDuration() throws ParsingException {
assertEquals(1, timeAgoParser.parseDuration("one second"));
assertEquals(1, timeAgoParser.parseDuration("second"));
assertEquals(49, timeAgoParser.parseDuration("49 seconds"));
assertEquals(61, timeAgoParser.parseDuration("1 minute, 1 second"));
assertEquals(Duration.ofSeconds(1), timeAgoParser.parseDuration("one second"));
assertEquals(Duration.ofSeconds(1), timeAgoParser.parseDuration("second"));
assertEquals(Duration.ofSeconds(49), timeAgoParser.parseDuration("49 seconds"));
assertEquals(Duration.ofSeconds(61), timeAgoParser.parseDuration("1 minute, 1 second"));
}
@Test

View File

@ -42,8 +42,8 @@ public class MediaCCCRecentListExtractorTest {
"Name=[" + item.getName() + "] of " + item + " is empty or null"
),
() -> assertGreater(0,
item.getDuration(),
"Duration[=" + item.getDuration() + "] of " + item + " is <= 0"
item.getDurationInSeconds(),
"Duration[=" + item.getDurationInSeconds() + "] of " + item + " is <= 0"
)
);
}

View File

@ -1,5 +1,9 @@
package org.schabi.newpipe.extractor.services.youtube;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.schabi.newpipe.downloader.DownloaderFactory;
@ -9,10 +13,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream.AudioTrackType;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
public class YoutubeParsingHelperTest {
@ -38,9 +39,12 @@ public class YoutubeParsingHelperTest {
@Test
void testParseDurationString() throws ParsingException {
assertEquals(1162567, YoutubeParsingHelper.parseDurationString("12:34:56:07"));
assertEquals(4445767, YoutubeParsingHelper.parseDurationString("1,234:56:07"));
assertEquals(754, YoutubeParsingHelper.parseDurationString("12:34 "));
assertEquals(Duration.ofDays(12).plusHours(34).plusMinutes(56).plusSeconds(7),
YoutubeParsingHelper.parseDurationString("12:34:56:07"));
assertEquals(Duration.ofHours(1234).plusMinutes(56).plusSeconds(7),
YoutubeParsingHelper.parseDurationString("1,234:56:07"));
assertEquals(Duration.ofMinutes(12).plusSeconds(34),
YoutubeParsingHelper.parseDurationString("12:34 "));
}
@Test