Obtain stream length as a Duration
This commit is contained in:
parent
7c7ceaceab
commit
534e2a3731
|
@ -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<>();
|
||||
while (matcher.find()) {
|
||||
results.add(matcher.toMatchResult());
|
||||
}
|
||||
final var matcher = DURATION_PATTERN.matcher(textualDuration);
|
||||
|
||||
return results.stream()
|
||||
.map(match -> {
|
||||
var duration = Duration.ZERO;
|
||||
while (matcher.find()) {
|
||||
final var match = matcher.toMatchResult();
|
||||
final String digits = match.group(1);
|
||||
final String word = match.group(2);
|
||||
|
||||
int amount;
|
||||
long amount;
|
||||
try {
|
||||
amount = Integer.parseInt(digits);
|
||||
amount = Long.parseLong(digits);
|
||||
} catch (final NumberFormatException ignored) {
|
||||
amount = 1;
|
||||
}
|
||||
|
||||
final ChronoUnit unit;
|
||||
try {
|
||||
unit = parseChronoUnit(word);
|
||||
duration = duration.plus(amount, 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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -316,21 +318,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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue