From c2b9537b6645449cc81d1870ab0836dc7b4b7cbc Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 28 May 2022 23:09:02 +0200 Subject: [PATCH 01/43] Created new basic stream architecture --- .../streamdata/delivery/DASHDeliveryData.java | 5 ++ .../delivery/DASHManifestDeliveryData.java | 18 +++++ .../delivery/DASHUrlDeliveryData.java | 5 ++ .../streamdata/delivery/DeliveryData.java | 5 ++ .../streamdata/delivery/HLSDeliveryData.java | 5 ++ .../delivery/ProgressiveHTTPDeliveryData.java | 5 ++ .../delivery/TorrentDeliveryData.java | 5 ++ .../delivery/UrlBasedDeliveryData.java | 8 ++ .../simpleimpl/AbstractDeliveryDataImpl.java | 7 ++ .../AbstractUrlBasedDeliveryDataImpl.java | 25 ++++++ .../SimpleDASHManifestDeliveryDataImpl.java | 34 ++++++++ .../SimpleDASHUrlDeliveryDataImpl.java | 12 +++ .../simpleimpl/SimpleHLSDeliveryDataImpl.java | 12 +++ ...SimpleProgressiveHTTPDeliveryDataImpl.java | 12 +++ .../SimpleTorrentDeliveryDataImpl.java | 12 +++ .../format/AbstractMediaFormat.java | 51 ++++++++++++ .../streamdata/format/AudioMediaFormat.java | 14 ++++ .../format/SubtitleMediaFormat.java | 12 +++ .../format/VideoAudioMediaFormat.java | 12 +++ .../format/registry/AudioFormatRegistry.java | 23 ++++++ .../format/registry/MediaFormatRegistry.java | 79 +++++++++++++++++++ .../registry/SubtitleFormatRegistry.java | 25 ++++++ .../registry/VideoAudioFormatRegistry.java | 17 ++++ .../streamdata/stream/AudioStream.java | 40 ++++++++++ .../extractor/streamdata/stream/Stream.java | 15 ++++ .../streamdata/stream/SubtitleStream.java | 60 ++++++++++++++ .../streamdata/stream/VideoAudioStream.java | 17 ++++ .../streamdata/stream/VideoStream.java | 38 +++++++++ .../stream/simpleimpl/AbstractStreamImpl.java | 23 ++++++ .../simpleimpl/SimpleAudioStreamImpl.java | 53 +++++++++++++ .../simpleimpl/SimpleSubtitleStreamImpl.java | 71 +++++++++++++++++ .../SimpleVideoAudioStreamImpl.java | 74 +++++++++++++++++ .../simpleimpl/SimpleVideoStreamImpl.java | 50 ++++++++++++ 33 files changed, 844 insertions(+) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHManifestDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHUrlDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/HLSDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/ProgressiveHTTPDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/TorrentDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/UrlBasedDeliveryData.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractUrlBasedDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleDASHManifestDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleDASHUrlDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleHLSDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleProgressiveHTTPDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleTorrentDeliveryDataImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/AbstractMediaFormat.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/AudioMediaFormat.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/SubtitleMediaFormat.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/VideoAudioMediaFormat.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/registry/AudioFormatRegistry.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/registry/MediaFormatRegistry.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/registry/SubtitleFormatRegistry.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/format/registry/VideoAudioFormatRegistry.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/AudioStream.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/Stream.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/SubtitleStream.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoAudioStream.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoStream.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/AbstractStreamImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleAudioStreamImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleSubtitleStreamImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoAudioStreamImpl.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoStreamImpl.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHDeliveryData.java new file mode 100644 index 0000000000..42d7e452d5 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHDeliveryData.java @@ -0,0 +1,5 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +public interface DASHDeliveryData extends DeliveryData { + // Just a marker interface +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHManifestDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHManifestDeliveryData.java new file mode 100644 index 0000000000..ce2452a732 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHManifestDeliveryData.java @@ -0,0 +1,18 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +import javax.annotation.Nonnull; + +public interface DASHManifestDeliveryData extends DASHDeliveryData { + /** + * Returns the base url for the DashManifest. + * + * @return + */ + // TODO: Check removal + @Nonnull + default String getBaseUrl() { + return ""; + } + + String getManifestAsString(); +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHUrlDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHUrlDeliveryData.java new file mode 100644 index 0000000000..ff9ff1b2e7 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DASHUrlDeliveryData.java @@ -0,0 +1,5 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +public interface DASHUrlDeliveryData extends UrlBasedDeliveryData, DASHDeliveryData { + // Nothing to implement additionally +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DeliveryData.java new file mode 100644 index 0000000000..9149d7794c --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/DeliveryData.java @@ -0,0 +1,5 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +public interface DeliveryData { + // Only a marker so far +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/HLSDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/HLSDeliveryData.java new file mode 100644 index 0000000000..4023a32e55 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/HLSDeliveryData.java @@ -0,0 +1,5 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +public interface HLSDeliveryData extends UrlBasedDeliveryData { + // Nothing to implement additionally +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/ProgressiveHTTPDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/ProgressiveHTTPDeliveryData.java new file mode 100644 index 0000000000..3817d47071 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/ProgressiveHTTPDeliveryData.java @@ -0,0 +1,5 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +public interface ProgressiveHTTPDeliveryData extends DeliveryData { + // Nothing to implement additionally +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/TorrentDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/TorrentDeliveryData.java new file mode 100644 index 0000000000..b7112a37b9 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/TorrentDeliveryData.java @@ -0,0 +1,5 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +public interface TorrentDeliveryData extends UrlBasedDeliveryData { + // Nothing to implement additionally +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/UrlBasedDeliveryData.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/UrlBasedDeliveryData.java new file mode 100644 index 0000000000..a0df54f913 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/UrlBasedDeliveryData.java @@ -0,0 +1,8 @@ +package org.schabi.newpipe.extractor.streamdata.delivery; + +import javax.annotation.Nonnull; + +public interface UrlBasedDeliveryData extends DeliveryData { + @Nonnull + String url(); +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractDeliveryDataImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractDeliveryDataImpl.java new file mode 100644 index 0000000000..c6c70163b6 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractDeliveryDataImpl.java @@ -0,0 +1,7 @@ +package org.schabi.newpipe.extractor.streamdata.delivery.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData; + +public abstract class AbstractDeliveryDataImpl implements DeliveryData { + // Nothing to implement so far +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractUrlBasedDeliveryDataImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractUrlBasedDeliveryDataImpl.java new file mode 100644 index 0000000000..81e4d6a4ff --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/AbstractUrlBasedDeliveryDataImpl.java @@ -0,0 +1,25 @@ +package org.schabi.newpipe.extractor.streamdata.delivery.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.UrlBasedDeliveryData; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +public abstract class AbstractUrlBasedDeliveryDataImpl extends AbstractDeliveryDataImpl + implements UrlBasedDeliveryData { + + @Nonnull + private final String url; + + protected AbstractUrlBasedDeliveryDataImpl(@Nonnull final String url) { + this.url = Objects.requireNonNull(url); + } + + @Nonnull + @Override + public String url() { + return url; + } + +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleDASHManifestDeliveryDataImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleDASHManifestDeliveryDataImpl.java new file mode 100644 index 0000000000..767ce3a044 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/delivery/simpleimpl/SimpleDASHManifestDeliveryDataImpl.java @@ -0,0 +1,34 @@ +package org.schabi.newpipe.extractor.streamdata.delivery.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DASHManifestDeliveryData; + +import java.util.Objects; +import java.util.function.Supplier; + +import javax.annotation.Nonnull; + +/** + * Note we build the manifests for YT ourself because the provided ones (according to TiA4f8R) + *
-1
if unknown
+ */
+ default int averageBitrate() {
+ return UNKNOWN_BITRATE;
+ }
+
+ @Override
+ default boolean equalsStream(@Nullable final Stream other) {
+ if (!(other instanceof AudioStream)) {
+ return false;
+ }
+
+ final AudioStream otherAudioStream = (AudioStream) other;
+ return Objects.equals(audioMediaFormat(), otherAudioStream.audioMediaFormat())
+ && averageBitrate() == otherAudioStream.averageBitrate();
+ }
+}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/Stream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/Stream.java
new file mode 100644
index 0000000000..8af60b482a
--- /dev/null
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/Stream.java
@@ -0,0 +1,15 @@
+package org.schabi.newpipe.extractor.streamdata.stream;
+
+import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public interface Stream {
+ @Nonnull
+ DeliveryData deliveryData();
+
+
+ // TODO: May also have to check deliverydata
+ boolean equalsStream(@Nullable final Stream other);
+}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/SubtitleStream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/SubtitleStream.java
new file mode 100644
index 0000000000..f9ee57e631
--- /dev/null
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/SubtitleStream.java
@@ -0,0 +1,60 @@
+package org.schabi.newpipe.extractor.streamdata.stream;
+
+import org.schabi.newpipe.extractor.streamdata.format.SubtitleMediaFormat;
+
+import java.util.Locale;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Represents a subtitle (only) stream.
+ */
+public interface SubtitleStream extends Stream {
+ @Nonnull
+ SubtitleMediaFormat subtitleMediaFormat();
+
+ /**
+ * Return whether if the subtitles are auto-generated.
+ * + * Some streaming services can generate subtitles for their contents, like YouTube. + *
+ * + * @return {@code true} if the subtitles are auto-generated, {@code false} otherwise + */ + default boolean autoGenerated() { + return false; + } + + /** + * Get the language code of the subtitles. + * + * @return the language code of the subtitles + */ + @Nonnull + String languageCode(); + + /** + * Get the {@link Locale locale} of the subtitles. + * + *+ * Note: The locale is directly derived from the language-code. + *
+ * + * @return the {@link Locale locale} of the subtitles + */ + Locale locale(); + + @Override + default boolean equalsStream(@Nullable final Stream other) { + if (!(other instanceof SubtitleStream)) { + return false; + } + + final SubtitleStream otherSubtitleStream = (SubtitleStream) other; + return Objects.equals(subtitleMediaFormat(), otherSubtitleStream.subtitleMediaFormat()) + && autoGenerated() == otherSubtitleStream.autoGenerated() + && Objects.equals(languageCode(), otherSubtitleStream.languageCode()); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoAudioStream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoAudioStream.java new file mode 100644 index 0000000000..b6132ed464 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoAudioStream.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.extractor.streamdata.stream; + +import javax.annotation.Nullable; + +/** + * Represents a combined video+audio stream. + */ +public interface VideoAudioStream extends VideoStream, AudioStream { + @Override + default boolean equalsStream(@Nullable final Stream other) { + if (!(other instanceof VideoAudioStream)) { + return false; + } + + return VideoStream.super.equalsStream(other) && AudioStream.super.equalsStream(other); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoStream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoStream.java new file mode 100644 index 0000000000..63b298bfcc --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/VideoStream.java @@ -0,0 +1,38 @@ +package org.schabi.newpipe.extractor.streamdata.stream; + +import org.schabi.newpipe.extractor.streamdata.format.VideoAudioMediaFormat; + +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents a video (only) stream. + */ +public interface VideoStream extends Stream { + String UNKNOWN_RESOLUTION = ""; + + // TODO: Check if this can be non-null + @Nullable + default VideoAudioMediaFormat videoMediaFormat() { + return null; + } + + // TODO: This should be a separate entity (containing e.g. height x width + fps) + @Nonnull + default String resolution() { + return UNKNOWN_RESOLUTION; + } + + @Override + default boolean equalsStream(@Nullable final Stream other) { + if (!(other instanceof VideoStream)) { + return false; + } + + final VideoStream otherVideoStream = (VideoStream) other; + return Objects.equals(videoMediaFormat(), otherVideoStream.videoMediaFormat()) + && Objects.equals(resolution(), otherVideoStream.resolution()); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/AbstractStreamImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/AbstractStreamImpl.java new file mode 100644 index 0000000000..2a4abe0a52 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/AbstractStreamImpl.java @@ -0,0 +1,23 @@ +package org.schabi.newpipe.extractor.streamdata.stream.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData; +import org.schabi.newpipe.extractor.streamdata.stream.Stream; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +public abstract class AbstractStreamImpl implements Stream { + @Nonnull + private final DeliveryData deliveryData; + + protected AbstractStreamImpl(@Nonnull final DeliveryData deliveryData) { + this.deliveryData = Objects.requireNonNull(deliveryData); + } + + @Nonnull + @Override + public DeliveryData deliveryData() { + return deliveryData; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleAudioStreamImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleAudioStreamImpl.java new file mode 100644 index 0000000000..c360e3cb03 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleAudioStreamImpl.java @@ -0,0 +1,53 @@ +package org.schabi.newpipe.extractor.streamdata.stream.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData; +import org.schabi.newpipe.extractor.streamdata.format.AudioMediaFormat; +import org.schabi.newpipe.extractor.streamdata.stream.AudioStream; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SimpleAudioStreamImpl extends AbstractStreamImpl implements AudioStream { + @Nullable + private final AudioMediaFormat audioMediaFormat; + private final int averageBitrate; + + public SimpleAudioStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nullable final AudioMediaFormat audioMediaFormat, + final int averageBitrate + ) { + super(deliveryData); + this.audioMediaFormat = audioMediaFormat; + this.averageBitrate = averageBitrate; + } + + public SimpleAudioStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nullable final AudioMediaFormat audioMediaFormat + ) { + this(deliveryData, audioMediaFormat, UNKNOWN_BITRATE); + } + + public SimpleAudioStreamImpl( + @Nonnull final DeliveryData deliveryData, + final int averageBitrate + ) { + this(deliveryData, null, averageBitrate); + } + + public SimpleAudioStreamImpl(@Nonnull final DeliveryData deliveryData) { + this(deliveryData, null); + } + + @Nullable + @Override + public AudioMediaFormat audioMediaFormat() { + return audioMediaFormat; + } + + @Override + public int averageBitrate() { + return averageBitrate; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleSubtitleStreamImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleSubtitleStreamImpl.java new file mode 100644 index 0000000000..2f41df6bf5 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleSubtitleStreamImpl.java @@ -0,0 +1,71 @@ +package org.schabi.newpipe.extractor.streamdata.stream.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData; +import org.schabi.newpipe.extractor.streamdata.format.SubtitleMediaFormat; +import org.schabi.newpipe.extractor.streamdata.stream.SubtitleStream; + +import java.util.Locale; +import java.util.Objects; + +import javax.annotation.Nonnull; + +public class SimpleSubtitleStreamImpl extends AbstractStreamImpl implements SubtitleStream { + @Nonnull + private final SubtitleMediaFormat subtitleMediaFormat; + private final boolean autogenerated; + @Nonnull + private final String languageCode; + private final Locale locale; + + public SimpleSubtitleStreamImpl( + @Nonnull final DeliveryData deliveryData, + final SubtitleMediaFormat subtitleMediaFormat, + final boolean autogenerated, + @Nonnull final String languageCode + ) { + super(deliveryData); + this.subtitleMediaFormat = Objects.requireNonNull(subtitleMediaFormat); + this.autogenerated = autogenerated; + this.languageCode = Objects.requireNonNull(languageCode); + /* + * Locale.forLanguageTag only for Android API >= 21 + * Locale.Builder only for Android API >= 21 + * Country codes doesn't work well without + */ + final String[] splits = languageCode.split("-"); + switch (splits.length) { + case 2: + this.locale = new Locale(splits[0], splits[1]); + break; + case 3: + // Complex variants don't work! + this.locale = new Locale(splits[0], splits[1], splits[2]); + break; + default: + this.locale = new Locale(splits[0]); + break; + } + } + + @Nonnull + @Override + public SubtitleMediaFormat subtitleMediaFormat() { + return subtitleMediaFormat; + } + + @Override + public boolean autoGenerated() { + return autogenerated; + } + + @Nonnull + @Override + public String languageCode() { + return languageCode; + } + + @Override + public Locale locale() { + return locale; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoAudioStreamImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoAudioStreamImpl.java new file mode 100644 index 0000000000..5986c1a0b1 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoAudioStreamImpl.java @@ -0,0 +1,74 @@ +package org.schabi.newpipe.extractor.streamdata.stream.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData; +import org.schabi.newpipe.extractor.streamdata.format.AudioMediaFormat; +import org.schabi.newpipe.extractor.streamdata.format.VideoAudioMediaFormat; +import org.schabi.newpipe.extractor.streamdata.stream.VideoAudioStream; + +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SimpleVideoAudioStreamImpl extends AbstractStreamImpl implements VideoAudioStream { + @Nullable + private final AudioMediaFormat audioMediaFormat; + private final int averageBitrate; + + @Nullable + private final VideoAudioMediaFormat videoAudioMediaFormat; + @Nonnull + private final String resolution; + + public SimpleVideoAudioStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nullable final AudioMediaFormat audioMediaFormat, + final int averageBitrate, + @Nullable final VideoAudioMediaFormat videoAudioMediaFormat, + @Nonnull final String resolution + ) { + super(deliveryData); + this.audioMediaFormat = audioMediaFormat; + this.averageBitrate = averageBitrate; + this.videoAudioMediaFormat = videoAudioMediaFormat; + this.resolution = Objects.requireNonNull(resolution); + } + + public SimpleVideoAudioStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nullable final VideoAudioMediaFormat videoAudioMediaFormat, + @Nonnull final String resolution + ) { + this(deliveryData, null, UNKNOWN_BITRATE, videoAudioMediaFormat, resolution); + } + + public SimpleVideoAudioStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nullable final VideoAudioMediaFormat videoAudioMediaFormat + ) { + this(deliveryData, videoAudioMediaFormat, UNKNOWN_RESOLUTION); + } + + @Nullable + @Override + public AudioMediaFormat audioMediaFormat() { + return audioMediaFormat; + } + + @Override + public int averageBitrate() { + return averageBitrate; + } + + @Nullable + @Override + public VideoAudioMediaFormat videoMediaFormat() { + return videoAudioMediaFormat; + } + + @Nonnull + @Override + public String resolution() { + return resolution; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoStreamImpl.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoStreamImpl.java new file mode 100644 index 0000000000..247099216f --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/simpleimpl/SimpleVideoStreamImpl.java @@ -0,0 +1,50 @@ +package org.schabi.newpipe.extractor.streamdata.stream.simpleimpl; + +import org.schabi.newpipe.extractor.streamdata.delivery.DeliveryData; +import org.schabi.newpipe.extractor.streamdata.format.VideoAudioMediaFormat; +import org.schabi.newpipe.extractor.streamdata.stream.VideoStream; + +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SimpleVideoStreamImpl extends AbstractStreamImpl implements VideoStream { + @Nullable + private final VideoAudioMediaFormat videoAudioMediaFormat; + @Nonnull + private final String resolution; + + public SimpleVideoStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nullable final VideoAudioMediaFormat videoAudioMediaFormat, + @Nonnull final String resolution + ) { + super(deliveryData); + this.videoAudioMediaFormat = videoAudioMediaFormat; + this.resolution = Objects.requireNonNull(resolution); + } + + public SimpleVideoStreamImpl( + @Nonnull final DeliveryData deliveryData, + @Nonnull final String resolution + ) { + this(deliveryData, null, resolution); + } + + public SimpleVideoStreamImpl(@Nonnull final DeliveryData deliveryData) { + this(deliveryData, null, UNKNOWN_RESOLUTION); + } + + @Nullable + @Override + public VideoAudioMediaFormat videoMediaFormat() { + return videoAudioMediaFormat; + } + + @Nonnull + @Override + public String resolution() { + return resolution; + } +} From c129d7ba56754c060bf57681a04c86d9a0953485 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 4 Jun 2022 23:31:06 +0200 Subject: [PATCH 02/43] Removed old streaming API --- .../schabi/newpipe/extractor/MediaFormat.java | 168 ------ .../newpipe/extractor/stream/AudioStream.java | 383 -------------- .../extractor/stream/DeliveryMethod.java | 53 -- .../newpipe/extractor/stream/Stream.java | 207 -------- .../extractor/stream/StreamExtractor.java | 79 +-- .../newpipe/extractor/stream/StreamInfo.java | 85 +-- .../newpipe/extractor/stream/StreamType.java | 8 + .../extractor/stream/SubtitlesStream.java | 328 ------------ .../newpipe/extractor/stream/VideoStream.java | 485 ------------------ 9 files changed, 79 insertions(+), 1717 deletions(-) delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/MediaFormat.java delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/DeliveryMethod.java delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/SubtitlesStream.java delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/VideoStream.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/MediaFormat.java b/extractor/src/main/java/org/schabi/newpipe/extractor/MediaFormat.java deleted file mode 100644 index 4423068488..0000000000 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/MediaFormat.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.schabi.newpipe.extractor; - -/* - * Created by Adam Howard on 08/11/15. - * - * Copyright (c) Christian Schabesberger- * It must not be null and should be non empty. - *
- * - *- * If you are not able to get an identifier, use the static constant {@link - * Stream#ID_UNKNOWN ID_UNKNOWN} of the {@link Stream} class. - *
- * - * @param id the identifier of the {@link AudioStream}, which must not be null - * @return this {@link Builder} instance - */ - public Builder setId(@Nonnull final String id) { - this.id = id; - return this; - } - - /** - * Set the content of the {@link AudioStream}. - * - *- * It must not be null, and should be non empty. - *
- * - * @param content the content of the {@link AudioStream} - * @param isUrl whether the content is a URL - * @return this {@link Builder} instance - */ - public Builder setContent(@Nonnull final String content, - final boolean isUrl) { - this.content = content; - this.isUrl = isUrl; - return this; - } - - /** - * Set the {@link MediaFormat} used by the {@link AudioStream}. - * - *- * It should be one of the audio {@link MediaFormat}s ({@link MediaFormat#M4A M4A}, - * {@link MediaFormat#WEBMA WEBMA}, {@link MediaFormat#MP3 MP3}, {@link MediaFormat#OPUS - * OPUS}, {@link MediaFormat#OGG OGG}, or {@link MediaFormat#WEBMA_OPUS WEBMA_OPUS}) but - * can be {@code null} if the media format could not be determined. - *
- * - *- * The default value is {@code null}. - *
- * - * @param mediaFormat the {@link MediaFormat} of the {@link AudioStream}, which can be null - * @return this {@link Builder} instance - */ - public Builder setMediaFormat(@Nullable final MediaFormat mediaFormat) { - this.mediaFormat = mediaFormat; - return this; - } - - /** - * Set the {@link DeliveryMethod} of the {@link AudioStream}. - * - *- * It must not be null. - *
- * - *- * The default delivery method is {@link DeliveryMethod#PROGRESSIVE_HTTP}. - *
- * - * @param deliveryMethod the {@link DeliveryMethod} of the {@link AudioStream}, which must - * not be null - * @return this {@link Builder} instance - */ - public Builder setDeliveryMethod(@Nonnull final DeliveryMethod deliveryMethod) { - this.deliveryMethod = deliveryMethod; - return this; - } - - /** - * Sets the URL of the manifest this stream comes from (if applicable, otherwise null). - * - * @param manifestUrl the URL of the manifest this stream comes from or {@code null} - * @return this {@link Builder} instance - */ - public Builder setManifestUrl(@Nullable final String manifestUrl) { - this.manifestUrl = manifestUrl; - return this; - } - - /** - * Set the average bitrate of the {@link AudioStream}. - * - *- * The default value is {@link #UNKNOWN_BITRATE}. - *
- * - * @param averageBitrate the average bitrate of the {@link AudioStream}, which should - * positive - * @return this {@link Builder} instance - */ - public Builder setAverageBitrate(final int averageBitrate) { - this.averageBitrate = averageBitrate; - return this; - } - - /** - * Set the {@link ItagItem} corresponding to the {@link AudioStream}. - * - *- * {@link ItagItem}s are YouTube specific objects, so they are only known for this service - * and can be null. - *
- * - *- * The default value is {@code null}. - *
- * - * @param itagItem the {@link ItagItem} of the {@link AudioStream}, which can be null - * @return this {@link Builder} instance - */ - public Builder setItagItem(@Nullable final ItagItem itagItem) { - this.itagItem = itagItem; - return this; - } - - /** - * Build an {@link AudioStream} using the builder's current values. - * - *- * The identifier and the content (and so the {@code isUrl} boolean) properties must have - * been set. - *
- * - * @return a new {@link AudioStream} using the builder's current values - * @throws IllegalStateException if {@code id}, {@code content} (and so {@code isUrl}) or - * {@code deliveryMethod} have been not set, or have been set as {@code null} - */ - @Nonnull - public AudioStream build() { - if (id == null) { - throw new IllegalStateException( - "The identifier of the audio stream has been not set or is null. If you " - + "are not able to get an identifier, use the static constant " - + "ID_UNKNOWN of the Stream class."); - } - - if (content == null) { - throw new IllegalStateException("The content of the audio stream has been not set " - + "or is null. Please specify a non-null one with setContent."); - } - - if (deliveryMethod == null) { - throw new IllegalStateException( - "The delivery method of the audio stream has been set as null, which is " - + "not allowed. Pass a valid one instead with setDeliveryMethod."); - } - - return new AudioStream(id, content, isUrl, mediaFormat, deliveryMethod, averageBitrate, - manifestUrl, itagItem); - } - } - - - /** - * Create a new audio stream. - * - * @param id the identifier which uniquely identifies the stream, e.g. for YouTube - * this would be the itag - * @param content the content or the URL of the stream, depending on whether isUrl is - * true - * @param isUrl whether content is the URL or the actual content of e.g. a DASH - * manifest - * @param format the {@link MediaFormat} used by the stream, which can be null - * @param deliveryMethod the {@link DeliveryMethod} of the stream - * @param averageBitrate the average bitrate of the stream (which can be unknown, see - * {@link #UNKNOWN_BITRATE}) - * @param itagItem the {@link ItagItem} corresponding to the stream, which cannot be null - * @param manifestUrl the URL of the manifest this stream comes from (if applicable, - * otherwise null) - */ - @SuppressWarnings("checkstyle:ParameterNumber") - private AudioStream(@Nonnull final String id, - @Nonnull final String content, - final boolean isUrl, - @Nullable final MediaFormat format, - @Nonnull final DeliveryMethod deliveryMethod, - final int averageBitrate, - @Nullable final String manifestUrl, - @Nullable final ItagItem itagItem) { - super(id, content, isUrl, format, deliveryMethod, manifestUrl); - if (itagItem != null) { - this.itagItem = itagItem; - this.itag = itagItem.id; - this.quality = itagItem.getQuality(); - this.bitrate = itagItem.getBitrate(); - this.initStart = itagItem.getInitStart(); - this.initEnd = itagItem.getInitEnd(); - this.indexStart = itagItem.getIndexStart(); - this.indexEnd = itagItem.getIndexEnd(); - this.codec = itagItem.getCodec(); - } - this.averageBitrate = averageBitrate; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equalStats(final Stream cmp) { - return super.equalStats(cmp) && cmp instanceof AudioStream - && averageBitrate == ((AudioStream) cmp).averageBitrate; - } - - /** - * Get the average bitrate of the stream. - * - * @return the average bitrate or {@link #UNKNOWN_BITRATE} if it is unknown - */ - public int getAverageBitrate() { - return averageBitrate; - } - - /** - * Get the itag identifier of the stream. - * - *- * Always equals to {@link #ITAG_NOT_AVAILABLE_OR_NOT_APPLICABLE} for other streams than the - * ones of the YouTube service. - *
- * - * @return the number of the {@link ItagItem} passed in the constructor of the audio stream. - */ - public int getItag() { - return itag; - } - - /** - * Get the bitrate of the stream. - * - * @return the bitrate set from the {@link ItagItem} passed in the constructor of the stream. - */ - public int getBitrate() { - return bitrate; - } - - /** - * Get the initialization start of the stream. - * - * @return the initialization start value set from the {@link ItagItem} passed in the - * constructor of the stream. - */ - public int getInitStart() { - return initStart; - } - - /** - * Get the initialization end of the stream. - * - * @return the initialization end value set from the {@link ItagItem} passed in the constructor - * of the stream. - */ - public int getInitEnd() { - return initEnd; - } - - /** - * Get the index start of the stream. - * - * @return the index start value set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getIndexStart() { - return indexStart; - } - - /** - * Get the index end of the stream. - * - * @return the index end value set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getIndexEnd() { - return indexEnd; - } - - /** - * Get the quality of the stream. - * - * @return the quality label set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public String getQuality() { - return quality; - } - - /** - * Get the codec of the stream. - * - * @return the codec set from the {@link ItagItem} passed in the constructor of the stream. - */ - public String getCodec() { - return codec; - } - - /** - * {@inheritDoc} - */ - @Override - @Nullable - public ItagItem getItagItem() { - return itagItem; - } -} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/DeliveryMethod.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/DeliveryMethod.java deleted file mode 100644 index ed98935725..0000000000 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/DeliveryMethod.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.schabi.newpipe.extractor.stream; - -/** - * An enum to represent the different delivery methods of {@link Stream streams} which are returned - * by the extractor. - */ -public enum DeliveryMethod { - - /** - * Used for {@link Stream}s served using the progressive HTTP streaming method. - */ - PROGRESSIVE_HTTP, - - /** - * Used for {@link Stream}s served using the DASH (Dynamic Adaptive Streaming over HTTP) - * adaptive streaming method. - * - * @see the - * Dynamic Adaptive Streaming over HTTP Wikipedia page and - * DASH Industry Forum's website for more information about the DASH delivery method - */ - DASH, - - /** - * Used for {@link Stream}s served using the HLS (HTTP Live Streaming) adaptive streaming - * method. - * - * @see the HTTP Live Streaming - * page and Apple's developers website page - * about HTTP Live Streaming for more information about the HLS delivery method - */ - HLS, - - /** - * Used for {@link Stream}s served using the SmoothStreaming adaptive streaming method. - * - * @see Wikipedia's page about adaptive bitrate streaming, - * section Microsoft Smooth Streaming (MSS) for more information about the - * SmoothStreaming delivery method - */ - SS, - - /** - * Used for {@link Stream}s served via a torrent file. - * - * @see Wikipedia's BitTorrent's page, - * Wikipedia's page about torrent files - * and Bitorrent's website for more information - * about the BitTorrent protocol - */ - TORRENT -} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java deleted file mode 100644 index 04d2b3facb..0000000000 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java +++ /dev/null @@ -1,207 +0,0 @@ -package org.schabi.newpipe.extractor.stream; - -import org.schabi.newpipe.extractor.MediaFormat; -import org.schabi.newpipe.extractor.services.youtube.ItagItem; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.Serializable; -import java.util.List; - -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; - -/** - * Abstract class which represents streams in the extractor. - */ -public abstract class Stream implements Serializable { - public static final int FORMAT_ID_UNKNOWN = -1; - public static final String ID_UNKNOWN = " "; - - /** - * An integer to represent that the itag ID returned is not available (only for YouTube; this - * should never happen) or not applicable (for other services than YouTube). - * - *- * An itag should not have a negative value, so {@code -1} is used for this constant. - *
- */ - public static final int ITAG_NOT_AVAILABLE_OR_NOT_APPLICABLE = -1; - - private final String id; - @Nullable private final MediaFormat mediaFormat; - private final String content; - private final boolean isUrl; - private final DeliveryMethod deliveryMethod; - @Nullable private final String manifestUrl; - - /** - * Instantiates a new {@code Stream} object. - * - * @param id the identifier which uniquely identifies the file, e.g. for YouTube - * this would be the itag - * @param content the content or URL, depending on whether isUrl is true - * @param isUrl whether content is the URL or the actual content of e.g. a DASH - * manifest - * @param format the {@link MediaFormat}, which can be null - * @param deliveryMethod the delivery method of the stream - * @param manifestUrl the URL of the manifest this stream comes from (if applicable, - * otherwise null) - */ - public Stream(final String id, - final String content, - final boolean isUrl, - @Nullable final MediaFormat format, - final DeliveryMethod deliveryMethod, - @Nullable final String manifestUrl) { - this.id = id; - this.content = content; - this.isUrl = isUrl; - this.mediaFormat = format; - this.deliveryMethod = deliveryMethod; - this.manifestUrl = manifestUrl; - } - - /** - * Checks if the list already contains a stream with the same statistics. - * - * @param stream the stream to be compared against the streams in the stream list - * @param streamList the list of {@link Stream}s which will be compared - * @return whether the list already contains one stream with equals stats - */ - public static boolean containSimilarStream(final Stream stream, - final List extends Stream> streamList) { - if (isNullOrEmpty(streamList)) { - return false; - } - for (final Stream cmpStream : streamList) { - if (stream.equalStats(cmpStream)) { - return true; - } - } - return false; - } - - /** - * Reveals whether two streams have the same statistics ({@link MediaFormat media format} and - * {@link DeliveryMethod delivery method}). - * - *- * If the {@link MediaFormat media format} of the stream is unknown, the streams are compared - * by using only the {@link DeliveryMethod delivery method} and their ID. - *
- * - *- * Note: This method always returns false if the stream passed is null. - *
- * - * @param other the stream object to be compared to this stream object - * @return whether the stream have the same stats or not, based on the criteria above - */ - public boolean equalStats(@Nullable final Stream other) { - if (other == null || mediaFormat == null || other.mediaFormat == null) { - return false; - } - return mediaFormat.id == other.mediaFormat.id && deliveryMethod == other.deliveryMethod - && isUrl == other.isUrl; - } - - /** - * Gets the identifier of this stream, e.g. the itag for YouTube. - * - *- * It should normally be unique, but {@link #ID_UNKNOWN} may be returned as the identifier if - * the one used by the stream extractor cannot be extracted, which could happen if the - * extractor uses a value from a streaming service. - *
- * - * @return the identifier (which may be {@link #ID_UNKNOWN}) - */ - public String getId() { - return id; - } - - /** - * Gets the URL of this stream if the content is a URL, or {@code null} otherwise. - * - * @return the URL if the content is a URL, {@code null} otherwise - * @deprecated Use {@link #getContent()} instead. - */ - @Deprecated - @Nullable - public String getUrl() { - return isUrl ? content : null; - } - - /** - * Gets the content or URL. - * - * @return the content or URL - */ - public String getContent() { - return content; - } - - /** - * Returns whether the content is a URL or not. - * - * @return {@code true} if the content of this stream is a URL, {@code false} if it's the - * actual content - */ - public boolean isUrl() { - return isUrl; - } - - /** - * Gets the {@link MediaFormat}, which can be null. - * - * @return the format - */ - @Nullable - public MediaFormat getFormat() { - return mediaFormat; - } - - /** - * Gets the format ID, which can be unknown. - * - * @return the format ID or {@link #FORMAT_ID_UNKNOWN} - */ - public int getFormatId() { - if (mediaFormat != null) { - return mediaFormat.id; - } - return FORMAT_ID_UNKNOWN; - } - - /** - * Gets the {@link DeliveryMethod}. - * - * @return the delivery method - */ - @Nonnull - public DeliveryMethod getDeliveryMethod() { - return deliveryMethod; - } - - /** - * Gets the URL of the manifest this stream comes from (if applicable, otherwise null). - * - * @return the URL of the manifest this stream comes from or {@code null} - */ - @Nullable - public String getManifestUrl() { - return manifestUrl; - } - - /** - * Gets the {@link ItagItem} of a stream. - * - *- * If the stream is not from YouTube, this value will always be null. - *
- * - * @return the {@link ItagItem} of the stream or {@code null} - */ - @Nullable - public abstract ItagItem getItagItem(); -} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java index ab922b1c98..12dc554833 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java @@ -20,11 +20,10 @@ * along with NewPipe. If not, see+ * If you don't know how DASH works take a look at + * + * here. + *
+ * + *+ * If you don't know what a dash MPD is you can read about it * here. + *
* - * @return the url as a string or an empty string or an empty string if not available + * @return the url as a string or an empty string if not available * @throws ParsingException if an error occurs while reading */ @Nonnull @@ -266,15 +279,18 @@ public String getDashMpdUrl() throws ParsingException { } /** - * I am not sure if this is in use, and how this is used. However the frontend is missing - * support for HLS streams. Prove me if I am wrong. Please open an - * issue, - * or fix this description if you know whats up with this. + * Get the HLS master playlist url. + * + *+ * If you don't know how HLS works take a look at + * + * here. + *
* * @return The Url to the hls stream or an empty string if not available. */ @Nonnull - public String getHlsUrl() throws ParsingException { + public String getHlsMasterPlaylistUrl() throws ParsingException { return ""; } @@ -286,10 +302,12 @@ public String getHlsUrl() throws ParsingException { * * @return a list of audio only streams in the format of AudioStream */ - public abstract List- * It must not be null, and should be non empty. - *
- * - * @param content the content of the {@link SubtitlesStream}, which must not be null - * @param isUrl whether the content is a URL - * @return this {@link Builder} instance - */ - public Builder setContent(@Nonnull final String content, - final boolean isUrl) { - this.content = content; - this.isUrl = isUrl; - return this; - } - - /** - * Set the {@link MediaFormat} used by the {@link SubtitlesStream}. - * - *- * It should be one of the subtitles {@link MediaFormat}s ({@link MediaFormat#SRT SRT}, - * {@link MediaFormat#TRANSCRIPT1 TRANSCRIPT1}, {@link MediaFormat#TRANSCRIPT2 - * TRANSCRIPT2}, {@link MediaFormat#TRANSCRIPT3 TRANSCRIPT3}, {@link MediaFormat#TTML - * TTML}, or {@link MediaFormat#VTT VTT}) but can be {@code null} if the media format could - * not be determined. - *
- * - *- * The default value is {@code null}. - *
- * - * @param mediaFormat the {@link MediaFormat} of the {@link SubtitlesStream}, which can be - * null - * @return this {@link Builder} instance - */ - public Builder setMediaFormat(@Nullable final MediaFormat mediaFormat) { - this.mediaFormat = mediaFormat; - return this; - } - - /** - * Set the {@link DeliveryMethod} of the {@link SubtitlesStream}. - * - *- * It must not be null. - *
- * - *- * The default delivery method is {@link DeliveryMethod#PROGRESSIVE_HTTP}. - *
- * - * @param deliveryMethod the {@link DeliveryMethod} of the {@link SubtitlesStream}, which - * must not be null - * @return this {@link Builder} instance - */ - public Builder setDeliveryMethod(@Nonnull final DeliveryMethod deliveryMethod) { - this.deliveryMethod = deliveryMethod; - return this; - } - - /** - * Sets the URL of the manifest this stream comes from (if applicable, otherwise null). - * - * @param manifestUrl the URL of the manifest this stream comes from or {@code null} - * @return this {@link Builder} instance - */ - public Builder setManifestUrl(@Nullable final String manifestUrl) { - this.manifestUrl = manifestUrl; - return this; - } - - /** - * Set the language code of the {@link SubtitlesStream}. - * - *- * It must not be null and should not be an empty string. - *
- * - * @param languageCode the language code of the {@link SubtitlesStream} - * @return this {@link Builder} instance - */ - public Builder setLanguageCode(@Nonnull final String languageCode) { - this.languageCode = languageCode; - return this; - } - - /** - * Set whether the subtitles have been auto-generated by the streaming service. - * - * @param autoGenerated whether the subtitles have been generated by the streaming - * service - * @return this {@link Builder} instance - */ - public Builder setAutoGenerated(final boolean autoGenerated) { - this.autoGenerated = autoGenerated; - return this; - } - - /** - * Build a {@link SubtitlesStream} using the builder's current values. - * - *- * The content (and so the {@code isUrl} boolean), the language code and the {@code - * isAutoGenerated} properties must have been set. - *
- * - *- * If no identifier has been set, an identifier will be generated using the language code - * and the media format suffix, if the media format is known. - *
- * - * @return a new {@link SubtitlesStream} using the builder's current values - * @throws IllegalStateException if {@code id}, {@code content} (and so {@code isUrl}), - * {@code deliveryMethod}, {@code languageCode} or the {@code isAutogenerated} have been - * not set, or have been set as {@code null} - */ - @Nonnull - public SubtitlesStream build() { - if (content == null) { - throw new IllegalStateException("No valid content was specified. Please specify a " - + "valid one with setContent."); - } - - if (deliveryMethod == null) { - throw new IllegalStateException( - "The delivery method of the subtitles stream has been set as null, which " - + "is not allowed. Pass a valid one instead with" - + "setDeliveryMethod."); - } - - if (languageCode == null) { - throw new IllegalStateException("The language code of the subtitles stream has " - + "been not set or is null. Make sure you specified an non null language " - + "code with setLanguageCode."); - } - - if (autoGenerated == null) { - throw new IllegalStateException("The subtitles stream has been not set as an " - + "autogenerated subtitles stream or not. Please specify this information " - + "with setIsAutoGenerated."); - } - - if (id == null) { - id = languageCode + (mediaFormat != null ? "." + mediaFormat.suffix - : ""); - } - - return new SubtitlesStream(id, content, isUrl, mediaFormat, deliveryMethod, - languageCode, autoGenerated, manifestUrl); - } - } - - /** - * Create a new subtitles stream. - * - * @param id the identifier which uniquely identifies the stream, e.g. for YouTube - * this would be the itag - * @param content the content or the URL of the stream, depending on whether isUrl is - * true - * @param isUrl whether content is the URL or the actual content of e.g. a DASH - * manifest - * @param mediaFormat the {@link MediaFormat} used by the stream - * @param deliveryMethod the {@link DeliveryMethod} of the stream - * @param languageCode the language code of the stream - * @param autoGenerated whether the subtitles are auto-generated by the streaming service - * @param manifestUrl the URL of the manifest this stream comes from (if applicable, - * otherwise null) - */ - @SuppressWarnings("checkstyle:ParameterNumber") - private SubtitlesStream(@Nonnull final String id, - @Nonnull final String content, - final boolean isUrl, - @Nullable final MediaFormat mediaFormat, - @Nonnull final DeliveryMethod deliveryMethod, - @Nonnull final String languageCode, - final boolean autoGenerated, - @Nullable final String manifestUrl) { - super(id, content, isUrl, mediaFormat, deliveryMethod, manifestUrl); - - /* - * Locale.forLanguageTag only for Android API >= 21 - * Locale.Builder only for Android API >= 21 - * Country codes doesn't work well without - */ - final String[] splits = languageCode.split("-"); - switch (splits.length) { - case 2: - this.locale = new Locale(splits[0], splits[1]); - break; - case 3: - // Complex variants don't work! - this.locale = new Locale(splits[0], splits[1], splits[2]); - break; - default: - this.locale = new Locale(splits[0]); - break; - } - - this.code = languageCode; - this.format = mediaFormat; - this.autoGenerated = autoGenerated; - } - - /** - * Get the extension of the subtitles. - * - * @return the extension of the subtitles - */ - public String getExtension() { - return format.suffix; - } - - /** - * Return whether if the subtitles are auto-generated. - *- * Some streaming services can generate subtitles for their contents, like YouTube. - *
- * - * @return {@code true} if the subtitles are auto-generated, {@code false} otherwise - */ - public boolean isAutoGenerated() { - return autoGenerated; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equalStats(final Stream cmp) { - return super.equalStats(cmp) - && cmp instanceof SubtitlesStream - && code.equals(((SubtitlesStream) cmp).code) - && autoGenerated == ((SubtitlesStream) cmp).autoGenerated; - } - - /** - * Get the display language name of the subtitles. - * - * @return the display language name of the subtitles - */ - public String getDisplayLanguageName() { - return locale.getDisplayName(locale); - } - - /** - * Get the language tag of the subtitles. - * - * @return the language tag of the subtitles - */ - public String getLanguageTag() { - return code; - } - - /** - * Get the {@link Locale locale} of the subtitles. - * - * @return the {@link Locale locale} of the subtitles - */ - public Locale getLocale() { - return locale; - } - - /** - * No subtitles which are currently extracted use an {@link ItagItem}, so {@code null} is - * returned by this method. - * - * @return {@code null} - */ - @Nullable - @Override - public ItagItem getItagItem() { - return null; - } -} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/VideoStream.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/VideoStream.java deleted file mode 100644 index 14952ebd1a..0000000000 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/VideoStream.java +++ /dev/null @@ -1,485 +0,0 @@ -package org.schabi.newpipe.extractor.stream; - -/* - * Created by Christian Schabesberger on 04.03.16. - * - * Copyright (C) Christian Schabesberger 2016- * It must not be null, and should be non empty. - *
- * - *- * If you are not able to get an identifier, use the static constant {@link - * Stream#ID_UNKNOWN ID_UNKNOWN} of the {@link Stream} class. - *
- * - * @param id the identifier of the {@link VideoStream}, which must not be null - * @return this {@link Builder} instance - */ - public Builder setId(@Nonnull final String id) { - this.id = id; - return this; - } - - /** - * Set the content of the {@link VideoStream}. - * - *- * It must not be null, and should be non empty. - *
- * - * @param content the content of the {@link VideoStream} - * @param isUrl whether the content is a URL - * @return this {@link Builder} instance - */ - public Builder setContent(@Nonnull final String content, - final boolean isUrl) { - this.content = content; - this.isUrl = isUrl; - return this; - } - - /** - * Set the {@link MediaFormat} used by the {@link VideoStream}. - * - *- * It should be one of the video {@link MediaFormat}s ({@link MediaFormat#MPEG_4 MPEG_4}, - * {@link MediaFormat#v3GPP v3GPP}, or {@link MediaFormat#WEBM WEBM}) but can be {@code - * null} if the media format could not be determined. - *
- * - *- * The default value is {@code null}. - *
- * - * @param mediaFormat the {@link MediaFormat} of the {@link VideoStream}, which can be null - * @return this {@link Builder} instance - */ - public Builder setMediaFormat(@Nullable final MediaFormat mediaFormat) { - this.mediaFormat = mediaFormat; - return this; - } - - /** - * Set the {@link DeliveryMethod} of the {@link VideoStream}. - * - *- * It must not be null. - *
- * - *- * The default delivery method is {@link DeliveryMethod#PROGRESSIVE_HTTP}. - *
- * - * @param deliveryMethod the {@link DeliveryMethod} of the {@link VideoStream}, which must - * not be null - * @return this {@link Builder} instance - */ - public Builder setDeliveryMethod(@Nonnull final DeliveryMethod deliveryMethod) { - this.deliveryMethod = deliveryMethod; - return this; - } - - /** - * Sets the URL of the manifest this stream comes from (if applicable, otherwise null). - * - * @param manifestUrl the URL of the manifest this stream comes from or {@code null} - * @return this {@link Builder} instance - */ - public Builder setManifestUrl(@Nullable final String manifestUrl) { - this.manifestUrl = manifestUrl; - return this; - } - - /** - * Set whether the {@link VideoStream} is video-only. - * - *- * This property must be set before building the {@link VideoStream}. - *
- * - * @param isVideoOnly whether the {@link VideoStream} is video-only - * @return this {@link Builder} instance - */ - public Builder setIsVideoOnly(final boolean isVideoOnly) { - this.isVideoOnly = isVideoOnly; - return this; - } - - /** - * Set the resolution of the {@link VideoStream}. - * - *- * This resolution can be used by clients to know the quality of the video stream. - *
- * - *- * If you are not able to know the resolution, you should use {@link #RESOLUTION_UNKNOWN} - * as the resolution of the video stream. - *
- * - *- * It must be set before building the builder and not null. - *
- * - * @param resolution the resolution of the {@link VideoStream} - * @return this {@link Builder} instance - */ - public Builder setResolution(@Nonnull final String resolution) { - this.resolution = resolution; - return this; - } - - /** - * Set the {@link ItagItem} corresponding to the {@link VideoStream}. - * - *- * {@link ItagItem}s are YouTube specific objects, so they are only known for this service - * and can be null. - *
- * - *- * The default value is {@code null}. - *
- * - * @param itagItem the {@link ItagItem} of the {@link VideoStream}, which can be null - * @return this {@link Builder} instance - */ - public Builder setItagItem(@Nullable final ItagItem itagItem) { - this.itagItem = itagItem; - return this; - } - - /** - * Build a {@link VideoStream} using the builder's current values. - * - *- * The identifier, the content (and so the {@code isUrl} boolean), the {@code isVideoOnly} - * and the {@code resolution} properties must have been set. - *
- * - * @return a new {@link VideoStream} using the builder's current values - * @throws IllegalStateException if {@code id}, {@code content} (and so {@code isUrl}), - * {@code deliveryMethod}, {@code isVideoOnly} or {@code resolution} have been not set, or - * have been set as {@code null} - */ - @Nonnull - public VideoStream build() { - if (id == null) { - throw new IllegalStateException( - "The identifier of the video stream has been not set or is null. If you " - + "are not able to get an identifier, use the static constant " - + "ID_UNKNOWN of the Stream class."); - } - - if (content == null) { - throw new IllegalStateException("The content of the video stream has been not set " - + "or is null. Please specify a non-null one with setContent."); - } - - if (deliveryMethod == null) { - throw new IllegalStateException( - "The delivery method of the video stream has been set as null, which is " - + "not allowed. Pass a valid one instead with setDeliveryMethod."); - } - - if (isVideoOnly == null) { - throw new IllegalStateException("The video stream has been not set as a " - + "video-only stream or as a video stream with embedded audio. Please " - + "specify this information with setIsVideoOnly."); - } - - if (resolution == null) { - throw new IllegalStateException( - "The resolution of the video stream has been not set. Please specify it " - + "with setResolution (use an empty string if you are not able to " - + "get it)."); - } - - return new VideoStream(id, content, isUrl, mediaFormat, deliveryMethod, resolution, - isVideoOnly, manifestUrl, itagItem); - } - } - - /** - * Create a new video stream. - * - * @param id the identifier which uniquely identifies the stream, e.g. for YouTube - * this would be the itag - * @param content the content or the URL of the stream, depending on whether isUrl is - * true - * @param isUrl whether content is the URL or the actual content of e.g. a DASH - * manifest - * @param format the {@link MediaFormat} used by the stream, which can be null - * @param deliveryMethod the {@link DeliveryMethod} of the stream - * @param resolution the resolution of the stream - * @param isVideoOnly whether the stream is video-only - * @param itagItem the {@link ItagItem} corresponding to the stream, which cannot be null - * @param manifestUrl the URL of the manifest this stream comes from (if applicable, - * otherwise null) - */ - @SuppressWarnings("checkstyle:ParameterNumber") - private VideoStream(@Nonnull final String id, - @Nonnull final String content, - final boolean isUrl, - @Nullable final MediaFormat format, - @Nonnull final DeliveryMethod deliveryMethod, - @Nonnull final String resolution, - final boolean isVideoOnly, - @Nullable final String manifestUrl, - @Nullable final ItagItem itagItem) { - super(id, content, isUrl, format, deliveryMethod, manifestUrl); - if (itagItem != null) { - this.itagItem = itagItem; - this.itag = itagItem.id; - this.bitrate = itagItem.getBitrate(); - this.initStart = itagItem.getInitStart(); - this.initEnd = itagItem.getInitEnd(); - this.indexStart = itagItem.getIndexStart(); - this.indexEnd = itagItem.getIndexEnd(); - this.codec = itagItem.getCodec(); - this.height = itagItem.getHeight(); - this.width = itagItem.getWidth(); - this.quality = itagItem.getQuality(); - this.fps = itagItem.getFps(); - } - this.resolution = resolution; - this.isVideoOnly = isVideoOnly; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equalStats(final Stream cmp) { - return super.equalStats(cmp) - && cmp instanceof VideoStream - && resolution.equals(((VideoStream) cmp).resolution) - && isVideoOnly == ((VideoStream) cmp).isVideoOnly; - } - - /** - * Get the video resolution. - * - *- * It can be unknown for some streams, like for HLS master playlists. In this case, - * {@link #RESOLUTION_UNKNOWN} is returned by this method. - *
- * - * @return the video resolution or {@link #RESOLUTION_UNKNOWN} - */ - @Nonnull - public String getResolution() { - return resolution; - } - - /** - * Return whether the stream is video-only. - * - *- * Video-only streams have no audio. - *
- * - * @return {@code true} if this stream is video-only, {@code false} otherwise - */ - public boolean isVideoOnly() { - return isVideoOnly; - } - - /** - * Get the itag identifier of the stream. - * - *- * Always equals to {@link #ITAG_NOT_AVAILABLE_OR_NOT_APPLICABLE} for other streams than the - * ones of the YouTube service. - *
- * - * @return the number of the {@link ItagItem} passed in the constructor of the video stream. - */ - public int getItag() { - return itag; - } - - /** - * Get the bitrate of the stream. - * - * @return the bitrate set from the {@link ItagItem} passed in the constructor of the stream. - */ - public int getBitrate() { - return bitrate; - } - - /** - * Get the initialization start of the stream. - * - * @return the initialization start value set from the {@link ItagItem} passed in the - * constructor of the - * stream. - */ - public int getInitStart() { - return initStart; - } - - /** - * Get the initialization end of the stream. - * - * @return the initialization end value set from the {@link ItagItem} passed in the constructor - * of the stream. - */ - public int getInitEnd() { - return initEnd; - } - - /** - * Get the index start of the stream. - * - * @return the index start value set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getIndexStart() { - return indexStart; - } - - /** - * Get the index end of the stream. - * - * @return the index end value set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getIndexEnd() { - return indexEnd; - } - - /** - * Get the width of the video stream. - * - * @return the width set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getWidth() { - return width; - } - - /** - * Get the height of the video stream. - * - * @return the height set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getHeight() { - return height; - } - - /** - * Get the frames per second of the video stream. - * - * @return the frames per second set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public int getFps() { - return fps; - } - - /** - * Get the quality of the stream. - * - * @return the quality label set from the {@link ItagItem} passed in the constructor of the - * stream. - */ - public String getQuality() { - return quality; - } - - /** - * Get the codec of the stream. - * - * @return the codec set from the {@link ItagItem} passed in the constructor of the stream. - */ - public String getCodec() { - return codec; - } - - /** - * {@inheritDoc} - */ - @Override - @Nullable - public ItagItem getItagItem() { - return itagItem; - } -} From 8b94206ef78174a24a76508364f20f65edc931eb Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 4 Jun 2022 23:31:16 +0200 Subject: [PATCH 03/43] Added Utility --- .../stream/util/NewPipeStreamCollectors.java | 100 ++++++++++++++++++ .../stream/util/NewPipeStreamUtil.java | 38 +++++++ 2 files changed, 138 insertions(+) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/util/NewPipeStreamCollectors.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/util/NewPipeStreamUtil.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/util/NewPipeStreamCollectors.java b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/util/NewPipeStreamCollectors.java new file mode 100644 index 0000000000..f9d1d03f04 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/streamdata/stream/util/NewPipeStreamCollectors.java @@ -0,0 +1,100 @@ +package org.schabi.newpipe.extractor.streamdata.stream.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Stream; + +public final class NewPipeStreamCollectors { + private NewPipeStreamCollectors() { + // No impl + } + + public static* Use this method to cache requests, because they can get quite big. + *
+ * + *+ * For more information see also: + * https://github.com/voc/streaming-website#json-api. + *
* TODO: implement better caching policy (max-age: 3 min) - * @param downloader The downloader to use for making the request + * + * @param downloader The downloader to use for making the request * @param localization The localization to be used. Will most likely be ignored. * @return {@link JsonArray} containing current conferences and info about their rooms and - * streams. + * streams. * @throws ExtractionException if the data could not be fetched or the retrieved data could not * be parsed to a {@link JsonArray} */ @@ -58,13 +67,14 @@ public static JsonArray getLiveStreams(final Downloader downloader, throws ExtractionException { if (liveStreams == null) { try { - final String site = downloader.get("https://streaming.media.ccc.de/streams/v2.json", - localization).responseBody(); + final String site = downloader + .get("https://streaming.media.ccc.de/streams/v2.json", localization) + .responseBody(); liveStreams = JsonParser.array().from(site); } catch (final IOException | ReCaptchaException e) { - throw new ExtractionException("Could not get live stream JSON.", e); + throw new ExtractionException("Could not get live stream JSON", e); } catch (final JsonParserException e) { - throw new ExtractionException("Could not parse JSON.", e); + throw new ExtractionException("Could not parse JSON", e); } } return liveStreams; diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java index 0a086fcc6b..10772010ad 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCStreamExtractor.java @@ -1,14 +1,9 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors; -import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE; -import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN; - -import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParserException; -import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -18,18 +13,23 @@ import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory; -import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.stream.StreamType; -import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.streamdata.delivery.simpleimpl.SimpleProgressiveHTTPDeliveryDataImpl; +import org.schabi.newpipe.extractor.streamdata.format.registry.AudioFormatRegistry; +import org.schabi.newpipe.extractor.streamdata.format.registry.VideoAudioFormatRegistry; +import org.schabi.newpipe.extractor.streamdata.stream.AudioStream; +import org.schabi.newpipe.extractor.streamdata.stream.VideoAudioStream; +import org.schabi.newpipe.extractor.streamdata.stream.simpleimpl.SimpleAudioStreamImpl; +import org.schabi.newpipe.extractor.streamdata.stream.simpleimpl.SimpleVideoAudioStreamImpl; import org.schabi.newpipe.extractor.utils.JsonUtils; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -96,75 +96,33 @@ public String getUploaderAvatarUrl() { @Override public List+ * This method downloads the provided manifest URL, finds all web occurrences in the manifest, + * gets the last segment URL, changes its segment range to {@code 0/track-length}, and return + * this as a string. + *
+ * + * @param hlsManifestUrl the URL of the manifest to be parsed + * @return a single URL that contains a range equal to the length of the track + */ + @Nonnull + private static String getSingleUrlFromHlsManifest(@Nonnull final String hlsManifestUrl) + throws ParsingException { + final Downloader dl = NewPipe.getDownloader(); + final String hlsManifestResponse; + + try { + hlsManifestResponse = dl.get(hlsManifestUrl).responseBody(); + } catch (final IOException | ReCaptchaException e) { + throw new ParsingException("Could not get SoundCloud HLS manifest"); + } + + final String[] lines = hlsManifestResponse.split("\\r?\\n"); + for (int l = lines.length - 1; l >= 0; l--) { + final String line = lines[l]; + // Get the last URL from manifest, because it contains the range of the stream + if (line.trim().length() != 0 && !line.startsWith("#") && line.startsWith("https")) { + final String[] hlsLastRangeUrlArray = line.split("/"); + return HTTPS + hlsLastRangeUrlArray[2] + "/media/0/" + hlsLastRangeUrlArray[5] + + "/" + hlsLastRangeUrlArray[6]; + } + } + throw new ParsingException("Could not get any URL from HLS manifest"); + } + private static String urlEncode(final String value) { try { return URLEncoder.encode(value, UTF_8); @@ -326,16 +366,6 @@ private static String urlEncode(final String value) { } } - @Override - public List