Extract stream duration as a Java 8 Duration
This commit is contained in:
parent
592f1596e6
commit
0261e637d3
|
@ -11,7 +11,7 @@ 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;
|
||||
|
@ -26,13 +26,14 @@ public class BandcampRadioInfoItemExtractor implements StreamInfoItemExtractor {
|
|||
show = radioShow;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public long getDuration() {
|
||||
public Duration getDurationObject() {
|
||||
/* 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.ofSeconds(query(show.getInt("id")).getLong("audio_duration"));
|
||||
return Duration.ZERO;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -43,9 +43,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ 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;
|
||||
|
||||
|
@ -46,9 +47,10 @@ public class BandcampPlaylistStreamInfoItemExtractor extends BandcampStreamInfoI
|
|||
return getUploaderUrl() + track.getString("title_link");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return track.getLong("duration");
|
||||
public Duration getDurationObject() {
|
||||
return Duration.ofSeconds(track.getLong("duration"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -47,9 +47,4 @@ public class BandcampSearchStreamInfoItemExtractor extends BandcampStreamInfoIte
|
|||
public List<Image> getThumbnails() throws ParsingException {
|
||||
return getImagesFromSearchResult(searchResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,11 +60,6 @@ public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() throws ParsingException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getViewCount() throws ParsingException {
|
||||
return -1;
|
||||
|
|
|
@ -64,7 +64,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.getDurationObject().isZero())
|
||||
.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;
|
||||
|
@ -53,10 +54,11 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
@Nonnull
|
||||
public Duration getDurationObject() {
|
||||
// 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
|
||||
|
|
|
@ -10,6 +10,7 @@ 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;
|
||||
|
@ -32,8 +33,9 @@ public class MediaCCCStreamInfoItemExtractor implements StreamInfoItemExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return event.getInt("length");
|
||||
@Nonnull
|
||||
public Duration getDurationObject() {
|
||||
return Duration.ofSeconds(event.getLong("length"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,6 +10,7 @@ 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;
|
||||
|
@ -100,8 +101,9 @@ public class PeertubeStreamInfoItemExtractor implements StreamInfoItemExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return item.getLong("duration");
|
||||
@Nonnull
|
||||
public Duration getDurationObject() {
|
||||
return Duration.ofSeconds(item.getLong("duration"));
|
||||
}
|
||||
|
||||
protected void setBaseUrl(final String baseUrl) {
|
||||
|
|
|
@ -9,6 +9,7 @@ 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;
|
||||
|
@ -35,8 +36,9 @@ public class SoundcloudStreamInfoItemExtractor implements StreamInfoItemExtracto
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return itemObject.getLong("duration") / 1000L;
|
||||
@Nonnull
|
||||
public Duration getDurationObject() {
|
||||
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.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
|
||||
import org.schabi.newpipe.extractor.downloader.Response;
|
||||
|
@ -56,10 +56,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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -310,21 +312,22 @@ 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)
|
||||
throws ParsingException, NumberFormatException {
|
||||
public static Duration parseDurationString(@Nonnull final String input)
|
||||
throws ParsingException {
|
||||
// If time separator : is not detected, try . instead
|
||||
final String[] splitInput = input.contains(":")
|
||||
? input.split(":")
|
||||
: input.split("\\.");
|
||||
|
||||
final int[] units = {24, 60, 60, 1};
|
||||
final int offset = units.length - splitInput.length;
|
||||
final var units = List.of(ChronoUnit.DAYS, ChronoUnit.HOURS, ChronoUnit.MINUTES,
|
||||
ChronoUnit.SECONDS);
|
||||
final int offset = units.size() - 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.get(i + offset));
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
@ -341,11 +344,7 @@ public final class YoutubeParsingHelper {
|
|||
* @return The converted integer or 0 if the conversion failed.
|
||||
*/
|
||||
private static int convertDurationToInt(final String input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final String clearedInput = Utils.removeNonDigitCharacters(input);
|
||||
final String clearedInput = input != null ? Utils.removeNonDigitCharacters(input) : "";
|
||||
try {
|
||||
return Integer.parseInt(clearedInput);
|
||||
} catch (final NumberFormatException ex) {
|
||||
|
|
|
@ -33,12 +33,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()
|
||||
|
|
|
@ -12,6 +12,7 @@ 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;
|
||||
|
@ -67,7 +68,8 @@ public class YoutubeMusicSongOrVideoInfoItemExtractor implements StreamInfoItemE
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() throws ParsingException {
|
||||
@Nonnull
|
||||
public Duration getDurationObject() throws ParsingException {
|
||||
final String duration = descriptionElements.getObject(descriptionElements.size() - 1)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(duration)) {
|
||||
|
|
|
@ -5,7 +5,6 @@ import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper
|
|||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
|
||||
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;
|
||||
|
@ -90,11 +89,6 @@ public class YoutubeReelInfoItemExtractor implements StreamInfoItemExtractor {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() throws ParsingException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUploaderName() throws ParsingException {
|
||||
return null;
|
||||
|
|
|
@ -41,7 +41,7 @@ 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;
|
||||
|
@ -136,10 +136,11 @@ public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
|||
throw new ParsingException("Could not get name");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public long getDuration() throws ParsingException {
|
||||
public Duration getDurationObject() throws ParsingException {
|
||||
if (getStreamType() == StreamType.LIVE_STREAM) {
|
||||
return -1;
|
||||
return Duration.ZERO;
|
||||
}
|
||||
|
||||
String duration = getTextFromObject(videoInfo.getObject("lengthText"));
|
||||
|
@ -169,7 +170,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");
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.schabi.newpipe.extractor.localization.DateWrapper;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,8 @@ public class StreamInfoItem extends InfoItem {
|
|||
@Nullable
|
||||
private DateWrapper uploadDate;
|
||||
private long viewCount = -1;
|
||||
private long duration = -1;
|
||||
@Nonnull
|
||||
private Duration duration = Duration.ZERO;
|
||||
|
||||
private String uploaderUrl = null;
|
||||
@Nonnull
|
||||
|
@ -76,12 +78,21 @@ public class StreamInfoItem extends InfoItem {
|
|||
this.viewCount = viewCount;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
@Nonnull
|
||||
public Duration getDurationObject() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDurationObject(@Nonnull final Duration durationObject) {
|
||||
this.duration = durationObject;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration.toSeconds();
|
||||
}
|
||||
|
||||
public void setDuration(final long duration) {
|
||||
this.duration = duration;
|
||||
this.duration = Duration.ofSeconds(duration);
|
||||
}
|
||||
|
||||
public String getUploaderUrl() {
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.schabi.newpipe.extractor.localization.DateWrapper;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public interface StreamInfoItemExtractor extends InfoItemExtractor {
|
||||
|
@ -48,12 +49,25 @@ public interface StreamInfoItemExtractor extends InfoItemExtractor {
|
|||
boolean isAd() throws ParsingException;
|
||||
|
||||
/**
|
||||
* Get the stream duration in seconds
|
||||
* Get the stream duration as a {@link Duration}.
|
||||
*
|
||||
* @return the stream duration in seconds or -1 if no duration is available
|
||||
* @return the stream duration in seconds or {@link Duration#ZERO} if no duration is available
|
||||
* @throws ParsingException if there is an error in the extraction
|
||||
*/
|
||||
long getDuration() throws ParsingException;
|
||||
@Nonnull
|
||||
default Duration getDurationObject() throws ParsingException {
|
||||
return Duration.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stream duration in seconds.
|
||||
*
|
||||
* @return the stream duration in seconds or 0 if no duration is available
|
||||
* @throws ParsingException if there is an error in the extraction
|
||||
*/
|
||||
default long getDuration() throws ParsingException {
|
||||
return getDurationObject().toSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the number of views
|
||||
|
|
|
@ -44,12 +44,12 @@ public class StreamInfoItemsCollector
|
|||
throw new FoundAdException("Found ad");
|
||||
}
|
||||
|
||||
final StreamInfoItem resultItem = new StreamInfoItem(
|
||||
getServiceId(), extractor.getUrl(), extractor.getName(), extractor.getStreamType());
|
||||
final var resultItem = new StreamInfoItem(getServiceId(), extractor.getUrl(),
|
||||
extractor.getName(), extractor.getStreamType());
|
||||
|
||||
// optional information
|
||||
try {
|
||||
resultItem.setDuration(extractor.getDuration());
|
||||
resultItem.setDurationObject(extractor.getDurationObject());
|
||||
} catch (final Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -99,12 +100,9 @@ public class ExtractorAsserts {
|
|||
assertGreater(expected, actual, actual + " is not > " + expected);
|
||||
}
|
||||
|
||||
public static void assertGreater(
|
||||
final long expected,
|
||||
final long actual,
|
||||
final String message
|
||||
) {
|
||||
assertTrue(actual > expected, message);
|
||||
public static <T extends Comparable<T>> void assertGreater(final T expected, final T actual,
|
||||
final String message) {
|
||||
assertTrue(actual.compareTo(expected) > 0, message);
|
||||
}
|
||||
|
||||
public static void assertGreaterOrEqual(final long expected, final long actual) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
|||
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -41,9 +42,9 @@ public class MediaCCCRecentListExtractorTest {
|
|||
isNullOrEmpty(item.getName()),
|
||||
"Name=[" + item.getName() + "] of " + item + " is empty or null"
|
||||
),
|
||||
() -> assertGreater(0,
|
||||
item.getDuration(),
|
||||
"Duration[=" + item.getDuration() + "] of " + item + " is <= 0"
|
||||
() -> assertGreater(Duration.ZERO,
|
||||
item.getDurationObject(),
|
||||
"Duration[=" + item.getDurationObject() + "] of " + item + " is <= 0"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|||
import org.schabi.newpipe.extractor.stream.AudioTrackType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
@ -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