From b3c620f0d8b19ddc532fe90dec7b813aea2caa29 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Sat, 28 May 2022 00:26:53 +0200
Subject: [PATCH] Apply code review and Streams rework
---
.../MediaCCCLiveStreamExtractor.java | 41 ++-
.../MediaCCCLiveStreamMapperDTO.java | 29 --
.../extractor/services/youtube/ItagItem.java | 6 +-
.../YoutubeDashManifestCreatorsUtils.java | 274 +++++++-----------
.../YoutubeOtfDashManifestCreator.java | 30 +-
...ePostLiveStreamDvrDashManifestCreator.java | 29 +-
...YoutubeProgressiveDashManifestCreator.java | 60 ++--
.../extractors/YoutubeStreamExtractor.java | 106 +++----
.../newpipe/extractor/stream/Stream.java | 19 +-
.../utils/ManifestCreatorCacheTest.java | 5 +
10 files changed, 248 insertions(+), 351 deletions(-)
delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamMapperDTO.java
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java
index da03e20e6..c761b33a1 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java
@@ -159,11 +159,11 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
return getStreams("audio",
dto -> {
final AudioStream.Builder builder = new AudioStream.Builder()
- .setId(dto.getUrlValue().getString("tech", ID_UNKNOWN))
- .setContent(dto.getUrlValue().getString(URL), true)
+ .setId(dto.urlValue.getString("tech", ID_UNKNOWN))
+ .setContent(dto.urlValue.getString(URL), true)
.setAverageBitrate(UNKNOWN_BITRATE);
- if ("hls".equals(dto.getUrlKey())) {
+ if ("hls".equals(dto.urlKey)) {
// We don't know with the type string what media format will
// have HLS streams.
// However, the tech string may contain some information
@@ -172,7 +172,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
.build();
}
- return builder.setMediaFormat(MediaFormat.getFromSuffix(dto.getUrlKey()))
+ return builder.setMediaFormat(MediaFormat.getFromSuffix(dto.urlKey))
.build();
});
}
@@ -181,15 +181,15 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
public List getVideoStreams() throws IOException, ExtractionException {
return getStreams("video",
dto -> {
- final JsonArray videoSize = dto.getStreamJsonObj().getArray("videoSize");
+ final JsonArray videoSize = dto.streamJsonObj.getArray("videoSize");
final VideoStream.Builder builder = new VideoStream.Builder()
- .setId(dto.getUrlValue().getString("tech", ID_UNKNOWN))
- .setContent(dto.getUrlValue().getString(URL), true)
+ .setId(dto.urlValue.getString("tech", ID_UNKNOWN))
+ .setContent(dto.urlValue.getString(URL), true)
.setIsVideoOnly(false)
.setResolution(videoSize.getInt(0) + "x" + videoSize.getInt(1));
- if ("hls".equals(dto.getUrlKey())) {
+ if ("hls".equals(dto.urlKey)) {
// We don't know with the type string what media format will
// have HLS streams.
// However, the tech string may contain some information
@@ -198,11 +198,32 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
.build();
}
- return builder.setMediaFormat(MediaFormat.getFromSuffix(dto.getUrlKey()))
+ return builder.setMediaFormat(MediaFormat.getFromSuffix(dto.urlKey))
.build();
});
}
+
+ /**
+ * This is just an internal class used in {@link #getStreams(String, Function)} to tie together
+ * the stream json object, its URL key and its URL value. An object of this class would be
+ * temporary and the three values it holds would be converted to a proper {@link Stream}
+ * object based on the wanted stream type.
+ */
+ private static final class MediaCCCLiveStreamMapperDTO {
+ final JsonObject streamJsonObj;
+ final String urlKey;
+ final JsonObject urlValue;
+
+ MediaCCCLiveStreamMapperDTO(final JsonObject streamJsonObj,
+ final String urlKey,
+ final JsonObject urlValue) {
+ this.streamJsonObj = streamJsonObj;
+ this.urlKey = urlKey;
+ this.urlValue = urlValue;
+ }
+ }
+
private List getStreams(
@Nonnull final String streamType,
@Nonnull final Function converter) {
@@ -220,7 +241,7 @@ public class MediaCCCLiveStreamExtractor extends StreamExtractor {
e.getKey(),
(JsonObject) e.getValue())))
// The DASH manifest will be extracted with getDashMpdUrl
- .filter(dto -> !"dash".equals(dto.getUrlKey()))
+ .filter(dto -> !"dash".equals(dto.urlKey))
// Convert
.map(converter)
.collect(Collectors.toList());
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamMapperDTO.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamMapperDTO.java
deleted file mode 100644
index c06ef736b..000000000
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamMapperDTO.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.schabi.newpipe.extractor.services.media_ccc.extractors;
-
-import com.grack.nanojson.JsonObject;
-
-final class MediaCCCLiveStreamMapperDTO {
- private final JsonObject streamJsonObj;
- private final String urlKey;
- private final JsonObject urlValue;
-
- MediaCCCLiveStreamMapperDTO(final JsonObject streamJsonObj,
- final String urlKey,
- final JsonObject urlValue) {
- this.streamJsonObj = streamJsonObj;
- this.urlKey = urlKey;
- this.urlValue = urlValue;
- }
-
- JsonObject getStreamJsonObj() {
- return streamJsonObj;
- }
-
- String getUrlKey() {
- return urlKey;
- }
-
- JsonObject getUrlValue() {
- return urlValue;
- }
-}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java
index fa6e97326..e0ff09a6f 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java
@@ -409,11 +409,7 @@ public class ItagItem implements Serializable {
* @param sampleRate the sample rate of an audio itag
*/
public void setSampleRate(final int sampleRate) {
- if (sampleRate > 0) {
- this.sampleRate = sampleRate;
- } else {
- this.sampleRate = SAMPLE_RATE_UNKNOWN;
- }
+ this.sampleRate = sampleRate > 0 ? sampleRate : SAMPLE_RATE_UNKNOWN;
}
/**
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeDashManifestCreatorsUtils.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeDashManifestCreatorsUtils.java
index ec876c671..5c45f65df 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeDashManifestCreatorsUtils.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeDashManifestCreatorsUtils.java
@@ -97,6 +97,25 @@ public final class YoutubeDashManifestCreatorsUtils {
public static final String SEGMENT_BASE = "SegmentBase";
public static final String INITIALIZATION = "Initialization";
+ /**
+ * Create an attribute with {@link Document#createAttribute(String)}, assign to it the provided
+ * name and value, then add it to the provided element using {@link
+ * Element#setAttributeNode(Attr)}.
+ *
+ * @param element element to which to add the created node
+ * @param doc document to use to create the attribute
+ * @param name name of the attribute
+ * @param value value of the attribute, will be set using {@link Attr#setValue(String)}
+ */
+ public static void setAttribute(final Element element,
+ final Document doc,
+ final String name,
+ final String value) {
+ final Attr attr = doc.createAttribute(name);
+ attr.setValue(value);
+ element.setAttributeNode(attr);
+ }
+
/**
* Generate a {@link Document} with common manifest creator elements added to it.
*
@@ -123,17 +142,17 @@ public final class YoutubeDashManifestCreatorsUtils {
public static Document generateDocumentAndDoCommonElementsGeneration(
@Nonnull final ItagItem itagItem,
final long streamDuration) throws CreationException {
- final Document document = generateDocumentAndMpdElement(streamDuration);
+ final Document doc = generateDocumentAndMpdElement(streamDuration);
- generatePeriodElement(document);
- generateAdaptationSetElement(document, itagItem);
- generateRoleElement(document);
- generateRepresentationElement(document, itagItem);
+ generatePeriodElement(doc);
+ generateAdaptationSetElement(doc, itagItem);
+ generateRoleElement(doc);
+ generateRepresentationElement(doc, itagItem);
if (itagItem.itagType == ItagItem.ItagType.AUDIO) {
- generateAudioChannelConfigurationElement(document, itagItem);
+ generateAudioChannelConfigurationElement(doc, itagItem);
}
- return document;
+ return doc;
}
/**
@@ -161,46 +180,25 @@ public final class YoutubeDashManifestCreatorsUtils {
public static Document generateDocumentAndMpdElement(final long duration)
throws CreationException {
try {
- final Document document = newDocument();
+ final Document doc = newDocument();
- final Element mpdElement = document.createElement(MPD);
- document.appendChild(mpdElement);
+ final Element mpdElement = doc.createElement(MPD);
+ doc.appendChild(mpdElement);
- final Attr xmlnsXsiAttribute = document.createAttribute("xmlns:xsi");
- xmlnsXsiAttribute.setValue("http://www.w3.org/2001/XMLSchema-instance");
- mpdElement.setAttributeNode(xmlnsXsiAttribute);
+ setAttribute(mpdElement, doc, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ setAttribute(mpdElement, doc, "xmlns", "urn:mpeg:DASH:schema:MPD:2011");
+ setAttribute(mpdElement, doc, "xsi:schemaLocation",
+ "urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd");
+ setAttribute(mpdElement, doc, "minBufferTime", "PT1.500S");
+ setAttribute(mpdElement, doc, "profiles", "urn:mpeg:dash:profile:full:2011");
+ setAttribute(mpdElement, doc, "type", "static");
+ setAttribute(mpdElement, doc, "mediaPresentationDuration",
+ String.format(Locale.ENGLISH, "PT%.3fS", duration / 1000.0));
- final Attr xmlns = document.createAttribute("xmlns");
- xmlns.setValue("urn:mpeg:DASH:schema:MPD:2011");
- mpdElement.setAttributeNode(xmlns);
-
- final Attr xsiSchemaLocationAttribute = document.createAttribute("xsi:schemaLocation");
- xsiSchemaLocationAttribute.setValue("urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd");
- mpdElement.setAttributeNode(xsiSchemaLocationAttribute);
-
- final Attr minBufferTimeAttribute = document.createAttribute("minBufferTime");
- minBufferTimeAttribute.setValue("PT1.500S");
- mpdElement.setAttributeNode(minBufferTimeAttribute);
-
- final Attr profilesAttribute = document.createAttribute("profiles");
- profilesAttribute.setValue("urn:mpeg:dash:profile:full:2011");
- mpdElement.setAttributeNode(profilesAttribute);
-
- final Attr typeAttribute = document.createAttribute("type");
- typeAttribute.setValue("static");
- mpdElement.setAttributeNode(typeAttribute);
-
- final Attr mediaPresentationDurationAttribute = document.createAttribute(
- "mediaPresentationDuration");
- final String durationSeconds = String.format(Locale.ENGLISH, "%.3f",
- duration / 1000.0);
- mediaPresentationDurationAttribute.setValue("PT" + durationSeconds + "S");
- mpdElement.setAttributeNode(mediaPresentationDurationAttribute);
-
- return document;
+ return doc;
} catch (final Exception e) {
throw new CreationException(
- "Could not generate the DASH manifest or append the MPD document to it", e);
+ "Could not generate the DASH manifest or append the MPD doc to it", e);
}
}
@@ -212,14 +210,13 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generateDocumentAndMpdElement(long)}.
*
*
- * @param document the {@link Document} on which the the {@code } element will be
- * appended
+ * @param doc the {@link Document} on which the the {@code } element will be appended
*/
- public static void generatePeriodElement(@Nonnull final Document document)
+ public static void generatePeriodElement(@Nonnull final Document doc)
throws CreationException {
try {
- final Element mpdElement = (Element) document.getElementsByTagName(MPD).item(0);
- final Element periodElement = document.createElement(PERIOD);
+ final Element mpdElement = (Element) doc.getElementsByTagName(MPD).item(0);
+ final Element periodElement = doc.createElement(PERIOD);
mpdElement.appendChild(periodElement);
} catch (final DOMException e) {
throw CreationException.couldNotAddElement(PERIOD, e);
@@ -235,21 +232,18 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generatePeriodElement(Document)}.
*
*
- * @param document the {@link Document} on which the {@code } element will be
- * appended
+ * @param doc the {@link Document} on which the {@code } element will be appended
* @param itagItem the {@link ItagItem} corresponding to the stream, which must not be null
*/
- public static void generateAdaptationSetElement(@Nonnull final Document document,
+ public static void generateAdaptationSetElement(@Nonnull final Document doc,
@Nonnull final ItagItem itagItem)
throws CreationException {
try {
- final Element periodElement = (Element) document.getElementsByTagName(PERIOD)
+ final Element periodElement = (Element) doc.getElementsByTagName(PERIOD)
.item(0);
- final Element adaptationSetElement = document.createElement(ADAPTATION_SET);
+ final Element adaptationSetElement = doc.createElement(ADAPTATION_SET);
- final Attr idAttribute = document.createAttribute("id");
- idAttribute.setValue("0");
- adaptationSetElement.setAttributeNode(idAttribute);
+ setAttribute(adaptationSetElement, doc, "id", "0");
final MediaFormat mediaFormat = itagItem.getMediaFormat();
if (mediaFormat == null || isNullOrEmpty(mediaFormat.getMimeType())) {
@@ -257,14 +251,8 @@ public final class YoutubeDashManifestCreatorsUtils {
"the MediaFormat or its mime type is null or empty");
}
- final Attr mimeTypeAttribute = document.createAttribute("mimeType");
- mimeTypeAttribute.setValue(mediaFormat.getMimeType());
- adaptationSetElement.setAttributeNode(mimeTypeAttribute);
-
- final Attr subsegmentAlignmentAttribute = document.createAttribute(
- "subsegmentAlignment");
- subsegmentAlignmentAttribute.setValue("true");
- adaptationSetElement.setAttributeNode(subsegmentAlignmentAttribute);
+ setAttribute(adaptationSetElement, doc, "mimeType", mediaFormat.getMimeType());
+ setAttribute(adaptationSetElement, doc, "subsegmentAlignment", "true");
periodElement.appendChild(adaptationSetElement);
} catch (final DOMException e) {
@@ -289,23 +277,17 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generateAdaptationSetElement(Document, ItagItem)}).
*
*
- * @param document the {@link Document} on which the the {@code } element will be
- * appended
+ * @param doc the {@link Document} on which the the {@code } element will be appended
*/
- public static void generateRoleElement(@Nonnull final Document document)
+ public static void generateRoleElement(@Nonnull final Document doc)
throws CreationException {
try {
- final Element adaptationSetElement = (Element) document.getElementsByTagName(
+ final Element adaptationSetElement = (Element) doc.getElementsByTagName(
ADAPTATION_SET).item(0);
- final Element roleElement = document.createElement(ROLE);
+ final Element roleElement = doc.createElement(ROLE);
- final Attr schemeIdUriAttribute = document.createAttribute("schemeIdUri");
- schemeIdUriAttribute.setValue("urn:mpeg:DASH:role:2011");
- roleElement.setAttributeNode(schemeIdUriAttribute);
-
- final Attr valueAttribute = document.createAttribute("value");
- valueAttribute.setValue("main");
- roleElement.setAttributeNode(valueAttribute);
+ setAttribute(roleElement, doc, "schemeIdUri", "urn:mpeg:DASH:role:2011");
+ setAttribute(roleElement, doc, "value", "main");
adaptationSetElement.appendChild(roleElement);
} catch (final DOMException e) {
@@ -322,56 +304,43 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generateAdaptationSetElement(Document, ItagItem)}).
*
*
- * @param document the {@link Document} on which the the {@code } element will
- * be appended
+ * @param doc the {@link Document} on which the the {@code } element will be
+ * appended
* @param itagItem the {@link ItagItem} to use, which must not be null
*/
- public static void generateRepresentationElement(@Nonnull final Document document,
+ public static void generateRepresentationElement(@Nonnull final Document doc,
@Nonnull final ItagItem itagItem)
throws CreationException {
try {
- final Element adaptationSetElement = (Element) document.getElementsByTagName(
+ final Element adaptationSetElement = (Element) doc.getElementsByTagName(
ADAPTATION_SET).item(0);
- final Element representationElement = document.createElement(REPRESENTATION);
+ final Element representationElement = doc.createElement(REPRESENTATION);
final int id = itagItem.id;
if (id <= 0) {
throw CreationException.couldNotAddElement(REPRESENTATION,
"the id of the ItagItem is <= 0");
}
- final Attr idAttribute = document.createAttribute("id");
- idAttribute.setValue(String.valueOf(id));
- representationElement.setAttributeNode(idAttribute);
+ setAttribute(representationElement, doc, "id", String.valueOf(id));
final String codec = itagItem.getCodec();
if (isNullOrEmpty(codec)) {
throw CreationException.couldNotAddElement(ADAPTATION_SET,
"the codec value of the ItagItem is null or empty");
}
- final Attr codecsAttribute = document.createAttribute("codecs");
- codecsAttribute.setValue(codec);
- representationElement.setAttributeNode(codecsAttribute);
-
- final Attr startWithSAPAttribute = document.createAttribute("startWithSAP");
- startWithSAPAttribute.setValue("1");
- representationElement.setAttributeNode(startWithSAPAttribute);
-
- final Attr maxPlayoutRateAttribute = document.createAttribute("maxPlayoutRate");
- maxPlayoutRateAttribute.setValue("1");
- representationElement.setAttributeNode(maxPlayoutRateAttribute);
+ setAttribute(representationElement, doc, "codecs", codec);
+ setAttribute(representationElement, doc, "startWithSAP", "1");
+ setAttribute(representationElement, doc, "maxPlayoutRate", "1");
final int bitrate = itagItem.getBitrate();
if (bitrate <= 0) {
throw CreationException.couldNotAddElement(REPRESENTATION,
"the bitrate of the ItagItem is <= 0");
}
- final Attr bandwidthAttribute = document.createAttribute("bandwidth");
- bandwidthAttribute.setValue(String.valueOf(bitrate));
- representationElement.setAttributeNode(bandwidthAttribute);
+ setAttribute(representationElement, doc, "bandwidth", String.valueOf(bitrate));
- final ItagItem.ItagType itagType = itagItem.itagType;
-
- if (itagType == ItagItem.ItagType.VIDEO || itagType == ItagItem.ItagType.VIDEO_ONLY) {
+ if (itagItem.itagType == ItagItem.ItagType.VIDEO
+ || itagItem.itagType == ItagItem.ItagType.VIDEO_ONLY) {
final int height = itagItem.getHeight();
final int width = itagItem.getWidth();
if (height <= 0 && width <= 0) {
@@ -380,25 +349,19 @@ public final class YoutubeDashManifestCreatorsUtils {
}
if (width > 0) {
- final Attr widthAttribute = document.createAttribute("width");
- widthAttribute.setValue(String.valueOf(width));
- representationElement.setAttributeNode(widthAttribute);
+ setAttribute(representationElement, doc, "width", String.valueOf(width));
}
-
- final Attr heightAttribute = document.createAttribute("height");
- heightAttribute.setValue(String.valueOf(itagItem.getHeight()));
- representationElement.setAttributeNode(heightAttribute);
+ setAttribute(representationElement, doc, "height",
+ String.valueOf(itagItem.getHeight()));
final int fps = itagItem.getFps();
if (fps > 0) {
- final Attr frameRateAttribute = document.createAttribute("frameRate");
- frameRateAttribute.setValue(String.valueOf(fps));
- representationElement.setAttributeNode(frameRateAttribute);
+ setAttribute(representationElement, doc, "frameRate", String.valueOf(fps));
}
}
- if (itagType == ItagItem.ItagType.AUDIO && itagItem.getSampleRate() > 0) {
- final Attr audioSamplingRateAttribute = document.createAttribute(
+ if (itagItem.itagType == ItagItem.ItagType.AUDIO && itagItem.getSampleRate() > 0) {
+ final Attr audioSamplingRateAttribute = doc.createAttribute(
"audioSamplingRate");
audioSamplingRateAttribute.setValue(String.valueOf(itagItem.getSampleRate()));
}
@@ -433,32 +396,28 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generateRepresentationElement(Document, ItagItem)}).
*
*
- * @param document the {@link Document} on which the {@code }
- * element will be appended
+ * @param doc the {@link Document} on which the {@code } element will
+ * be appended
* @param itagItem the {@link ItagItem} to use, which must not be null
*/
public static void generateAudioChannelConfigurationElement(
- @Nonnull final Document document,
+ @Nonnull final Document doc,
@Nonnull final ItagItem itagItem) throws CreationException {
try {
- final Element representationElement = (Element) document.getElementsByTagName(
+ final Element representationElement = (Element) doc.getElementsByTagName(
REPRESENTATION).item(0);
- final Element audioChannelConfigurationElement = document.createElement(
+ final Element audioChannelConfigurationElement = doc.createElement(
AUDIO_CHANNEL_CONFIGURATION);
- final Attr schemeIdUriAttribute = document.createAttribute("schemeIdUri");
- schemeIdUriAttribute.setValue(
+ setAttribute(audioChannelConfigurationElement, doc, "schemeIdUri",
"urn:mpeg:dash:23003:3:audio_channel_configuration:2011");
- audioChannelConfigurationElement.setAttributeNode(schemeIdUriAttribute);
- final Attr valueAttribute = document.createAttribute("value");
- final int audioChannels = itagItem.getAudioChannels();
- if (audioChannels <= 0) {
+ if (itagItem.getAudioChannels() <= 0) {
throw new CreationException("the number of audioChannels in the ItagItem is <= 0: "
- + audioChannels);
+ + itagItem.getAudioChannels());
}
- valueAttribute.setValue(String.valueOf(itagItem.getAudioChannels()));
- audioChannelConfigurationElement.setAttributeNode(valueAttribute);
+ setAttribute(audioChannelConfigurationElement, doc, "value",
+ String.valueOf(itagItem.getAudioChannels()));
representationElement.appendChild(audioChannelConfigurationElement);
} catch (final DOMException e) {
@@ -467,22 +426,22 @@ public final class YoutubeDashManifestCreatorsUtils {
}
/**
- * Convert a DASH manifest {@link Document document} to a string and cache it.
+ * Convert a DASH manifest {@link Document doc} to a string and cache it.
*
* @param originalBaseStreamingUrl the original base URL of the stream
- * @param document the document to be converted
+ * @param doc the doc to be converted
* @param manifestCreatorCache the {@link ManifestCreatorCache} on which store the string
* generated
- * @return the DASH manifest {@link Document document} converted to a string
+ * @return the DASH manifest {@link Document doc} converted to a string
*/
public static String buildAndCacheResult(
@Nonnull final String originalBaseStreamingUrl,
- @Nonnull final Document document,
+ @Nonnull final Document doc,
@Nonnull final ManifestCreatorCache manifestCreatorCache)
throws CreationException {
try {
- final String documentXml = documentToXml(document);
+ final String documentXml = documentToXml(doc);
manifestCreatorCache.put(originalBaseStreamingUrl, documentXml);
return documentXml;
} catch (final Exception e) {
@@ -517,13 +476,13 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generateRepresentationElement(Document, ItagItem)}).
*
*
- * @param document the {@link Document} on which the {@code } element will
+ * @param doc the {@link Document} on which the {@code } element will
* be appended
* @param baseUrl the base URL of the OTF/post-live-DVR stream
* @param deliveryType the stream {@link DeliveryType delivery type}, which must be either
* {@link DeliveryType#OTF OTF} or {@link DeliveryType#LIVE LIVE}
*/
- public static void generateSegmentTemplateElement(@Nonnull final Document document,
+ public static void generateSegmentTemplateElement(@Nonnull final Document doc,
@Nonnull final String baseUrl,
final DeliveryType deliveryType)
throws CreationException {
@@ -533,32 +492,22 @@ public final class YoutubeDashManifestCreatorsUtils {
}
try {
- final Element representationElement = (Element) document.getElementsByTagName(
+ final Element representationElement = (Element) doc.getElementsByTagName(
REPRESENTATION).item(0);
- final Element segmentTemplateElement = document.createElement(SEGMENT_TEMPLATE);
+ final Element segmentTemplateElement = doc.createElement(SEGMENT_TEMPLATE);
- final Attr startNumberAttribute = document.createAttribute("startNumber");
- final boolean isDeliveryTypeLive = deliveryType == DeliveryType.LIVE;
// The first sequence of post DVR streams is the beginning of the video stream and not
// an initialization segment
- final String startNumberValue = isDeliveryTypeLive ? "0" : "1";
- startNumberAttribute.setValue(startNumberValue);
- segmentTemplateElement.setAttributeNode(startNumberAttribute);
-
- final Attr timescaleAttribute = document.createAttribute("timescale");
- timescaleAttribute.setValue("1000");
- segmentTemplateElement.setAttributeNode(timescaleAttribute);
+ setAttribute(segmentTemplateElement, doc, "startNumber",
+ deliveryType == DeliveryType.LIVE ? "0" : "1");
+ setAttribute(segmentTemplateElement, doc, "timescale", "1000");
// Post-live-DVR/ended livestreams streams don't require an initialization sequence
- if (!isDeliveryTypeLive) {
- final Attr initializationAttribute = document.createAttribute("initialization");
- initializationAttribute.setValue(baseUrl + SQ_0);
- segmentTemplateElement.setAttributeNode(initializationAttribute);
+ if (deliveryType != DeliveryType.LIVE) {
+ setAttribute(segmentTemplateElement, doc, "initialization", baseUrl + SQ_0);
}
- final Attr mediaAttribute = document.createAttribute("media");
- mediaAttribute.setValue(baseUrl + "&sq=$Number$");
- segmentTemplateElement.setAttributeNode(mediaAttribute);
+ setAttribute(segmentTemplateElement, doc, "media", baseUrl + "&sq=$Number$");
representationElement.appendChild(segmentTemplateElement);
} catch (final DOMException e) {
@@ -575,15 +524,15 @@ public final class YoutubeDashManifestCreatorsUtils {
* {@link #generateSegmentTemplateElement(Document, String, DeliveryType)}.
*
*
- * @param document the {@link Document} on which the the {@code } element will
- * be appended
+ * @param doc the {@link Document} on which the the {@code } element will be
+ * appended
*/
- public static void generateSegmentTimelineElement(@Nonnull final Document document)
+ public static void generateSegmentTimelineElement(@Nonnull final Document doc)
throws CreationException {
try {
- final Element segmentTemplateElement = (Element) document.getElementsByTagName(
+ final Element segmentTemplateElement = (Element) doc.getElementsByTagName(
SEGMENT_TEMPLATE).item(0);
- final Element segmentTimelineElement = document.createElement(SEGMENT_TIMELINE);
+ final Element segmentTimelineElement = doc.createElement(SEGMENT_TIMELINE);
segmentTemplateElement.appendChild(segmentTimelineElement);
} catch (final DOMException e) {
@@ -672,8 +621,7 @@ public final class YoutubeDashManifestCreatorsUtils {
// supported by all platforms (like the Android implementation)
}
- final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
- return documentBuilder.newDocument();
+ return documentBuilderFactory.newDocumentBuilder().newDocument();
}
/**
@@ -681,13 +629,13 @@ public final class YoutubeDashManifestCreatorsUtils {
* support setting {@link XMLConstants#ACCESS_EXTERNAL_DTD} and
* {@link XMLConstants#ACCESS_EXTERNAL_SCHEMA} in {@link TransformerFactory} instances.
*
- * @param document the document to convert, which must have been created using
- * {@link #newDocument()} to properly prevent XXE attacks
- * @return the document converted to an XML string, making sure there can't be XXE attacks
+ * @param doc the doc to convert, which must have been created using {@link #newDocument()} to
+ * properly prevent XXE attacks
+ * @return the doc converted to an XML string, making sure there can't be XXE attacks
*/
// Sonar warning is suppressed because it is still shown even if we apply its solution
@SuppressWarnings("squid:S2755")
- private static String documentToXml(@Nonnull final Document document)
+ private static String documentToXml(@Nonnull final Document doc)
throws TransformerException {
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
@@ -705,7 +653,7 @@ public final class YoutubeDashManifestCreatorsUtils {
transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
final StringWriter result = new StringWriter();
- transformer.transform(new DOMSource(document), new StreamResult(result));
+ transformer.transform(new DOMSource(doc), new StreamResult(result));
return result.toString();
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeOtfDashManifestCreator.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeOtfDashManifestCreator.java
index f76d22356..8161b5263 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeOtfDashManifestCreator.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeOtfDashManifestCreator.java
@@ -5,7 +5,6 @@ import org.schabi.newpipe.extractor.services.youtube.DeliveryType;
import org.schabi.newpipe.extractor.services.youtube.ItagItem;
import org.schabi.newpipe.extractor.utils.ManifestCreatorCache;
import org.schabi.newpipe.extractor.utils.Utils;
-import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -23,6 +22,7 @@ import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.generateSegmentTemplateElement;
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.generateSegmentTimelineElement;
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.getInitializationResponse;
+import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.setAttribute;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
@@ -148,14 +148,14 @@ public final class YoutubeOtfDashManifestCreator {
streamDuration = durationSecondsFallback * 1000;
}
- final Document document = generateDocumentAndDoCommonElementsGeneration(itagItem,
+ final Document doc = generateDocumentAndDoCommonElementsGeneration(itagItem,
streamDuration);
- generateSegmentTemplateElement(document, realOtfBaseStreamingUrl, DeliveryType.OTF);
- generateSegmentTimelineElement(document);
- generateSegmentElementsForOtfStreams(segmentDuration, document);
+ generateSegmentTemplateElement(doc, realOtfBaseStreamingUrl, DeliveryType.OTF);
+ generateSegmentTimelineElement(doc);
+ generateSegmentElementsForOtfStreams(segmentDuration, doc);
- return buildAndCacheResult(otfBaseStreamingUrl, document, OTF_STREAMS_CACHE);
+ return buildAndCacheResult(otfBaseStreamingUrl, doc, OTF_STREAMS_CACHE);
}
/**
@@ -192,17 +192,18 @@ public final class YoutubeOtfDashManifestCreator {
*
* @param segmentDurations the sequences "length" or "length(r=repeat_count" extracted with the
* regular expressions
- * @param document the {@link Document} on which the {@code } elements will be appended
+ * @param doc the {@link Document} on which the {@code } elements will be
+ * appended
*/
private static void generateSegmentElementsForOtfStreams(
@Nonnull final String[] segmentDurations,
- @Nonnull final Document document) throws CreationException {
+ @Nonnull final Document doc) throws CreationException {
try {
- final Element segmentTimelineElement = (Element) document.getElementsByTagName(
+ final Element segmentTimelineElement = (Element) doc.getElementsByTagName(
SEGMENT_TIMELINE).item(0);
for (final String segmentDuration : segmentDurations) {
- final Element sElement = document.createElement("S");
+ final Element sElement = doc.createElement("S");
final String[] segmentLengthRepeat = segmentDuration.split("\\(r=");
// make sure segmentLengthRepeat[0], which is the length, is convertible to int
@@ -212,14 +213,9 @@ public final class YoutubeOtfDashManifestCreator {
if (segmentLengthRepeat.length > 1) {
final int segmentRepeatCount = Integer.parseInt(
Utils.removeNonDigitCharacters(segmentLengthRepeat[1]));
- final Attr rAttribute = document.createAttribute("r");
- rAttribute.setValue(String.valueOf(segmentRepeatCount));
- sElement.setAttributeNode(rAttribute);
+ setAttribute(sElement, doc, "r", String.valueOf(segmentRepeatCount));
}
-
- final Attr dAttribute = document.createAttribute("d");
- dAttribute.setValue(segmentLengthRepeat[0]);
- sElement.setAttributeNode(dAttribute);
+ setAttribute(sElement, doc, "d", segmentLengthRepeat[0]);
segmentTimelineElement.appendChild(sElement);
}
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubePostLiveStreamDvrDashManifestCreator.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubePostLiveStreamDvrDashManifestCreator.java
index 07ee3b887..43d7e41e5 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubePostLiveStreamDvrDashManifestCreator.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubePostLiveStreamDvrDashManifestCreator.java
@@ -4,7 +4,6 @@ import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.services.youtube.DeliveryType;
import org.schabi.newpipe.extractor.services.youtube.ItagItem;
import org.schabi.newpipe.extractor.utils.ManifestCreatorCache;
-import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -23,6 +22,7 @@ import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.generateSegmentTemplateElement;
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.generateSegmentTimelineElement;
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.getInitializationResponse;
+import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.setAttribute;
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
@@ -159,15 +159,15 @@ public final class YoutubePostLiveStreamDvrDashManifestCreator {
streamDuration = durationSecondsFallback;
}
- final Document document = generateDocumentAndDoCommonElementsGeneration(itagItem,
+ final Document doc = generateDocumentAndDoCommonElementsGeneration(itagItem,
streamDuration);
- generateSegmentTemplateElement(document, realPostLiveStreamDvrStreamingUrl,
+ generateSegmentTemplateElement(doc, realPostLiveStreamDvrStreamingUrl,
DeliveryType.LIVE);
- generateSegmentTimelineElement(document);
- generateSegmentElementForPostLiveDvrStreams(document, targetDurationSec, segmentCount);
+ generateSegmentTimelineElement(doc);
+ generateSegmentElementForPostLiveDvrStreams(doc, targetDurationSec, segmentCount);
- return buildAndCacheResult(postLiveStreamDvrStreamingUrl, document,
+ return buildAndCacheResult(postLiveStreamDvrStreamingUrl, doc,
POST_LIVE_DVR_STREAMS_CACHE);
}
@@ -190,7 +190,7 @@ public final class YoutubePostLiveStreamDvrDashManifestCreator {
* {@code }
*
*
- * @param document the {@link Document} on which the {@code } element will
+ * @param doc the {@link Document} on which the {@code } element will
* be appended
* @param targetDurationSeconds the {@code targetDurationSec} value from YouTube player
* response's stream
@@ -198,21 +198,16 @@ public final class YoutubePostLiveStreamDvrDashManifestCreator {
* #fromPostLiveStreamDvrStreamingUrl(String, ItagItem, int, long)}
*/
private static void generateSegmentElementForPostLiveDvrStreams(
- @Nonnull final Document document,
+ @Nonnull final Document doc,
final int targetDurationSeconds,
@Nonnull final String segmentCount) throws CreationException {
try {
- final Element segmentTimelineElement = (Element) document.getElementsByTagName(
+ final Element segmentTimelineElement = (Element) doc.getElementsByTagName(
SEGMENT_TIMELINE).item(0);
- final Element sElement = document.createElement("S");
+ final Element sElement = doc.createElement("S");
- final Attr dAttribute = document.createAttribute("d");
- dAttribute.setValue(String.valueOf(targetDurationSeconds * 1000));
- sElement.setAttributeNode(dAttribute);
-
- final Attr rAttribute = document.createAttribute("r");
- rAttribute.setValue(segmentCount);
- sElement.setAttributeNode(rAttribute);
+ setAttribute(sElement, doc, "d", String.valueOf(targetDurationSeconds * 1000));
+ setAttribute(sElement, doc, "r", segmentCount);
segmentTimelineElement.appendChild(sElement);
} catch (final DOMException e) {
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeProgressiveDashManifestCreator.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeProgressiveDashManifestCreator.java
index 2b4ff86ae..0f69895bb 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeProgressiveDashManifestCreator.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/dashmanifestcreators/YoutubeProgressiveDashManifestCreator.java
@@ -3,7 +3,6 @@ package org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators;
import org.schabi.newpipe.extractor.services.youtube.DeliveryType;
import org.schabi.newpipe.extractor.services.youtube.ItagItem;
import org.schabi.newpipe.extractor.utils.ManifestCreatorCache;
-import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -18,6 +17,7 @@ import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.SEGMENT_BASE;
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.buildAndCacheResult;
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.generateDocumentAndDoCommonElementsGeneration;
+import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.setAttribute;
/**
* Class which generates DASH manifests of {@link DeliveryType#PROGRESSIVE YouTube progressive}
@@ -100,14 +100,14 @@ public final class YoutubeProgressiveDashManifestCreator {
}
}
- final Document document = generateDocumentAndDoCommonElementsGeneration(itagItem,
+ final Document doc = generateDocumentAndDoCommonElementsGeneration(itagItem,
streamDuration);
- generateBaseUrlElement(document, progressiveStreamingBaseUrl);
- generateSegmentBaseElement(document, itagItem);
- generateInitializationElement(document, itagItem);
+ generateBaseUrlElement(doc, progressiveStreamingBaseUrl);
+ generateSegmentBaseElement(doc, itagItem);
+ generateInitializationElement(doc, itagItem);
- return buildAndCacheResult(progressiveStreamingBaseUrl, document,
+ return buildAndCacheResult(progressiveStreamingBaseUrl, doc,
PROGRESSIVE_STREAMS_CACHE);
}
@@ -128,18 +128,17 @@ public final class YoutubeProgressiveDashManifestCreator {
* {@link YoutubeDashManifestCreatorsUtils#generateRepresentationElement(Document, ItagItem)}).
*
*
- * @param document the {@link Document} on which the {@code } element will
- * be appended
+ * @param doc the {@link Document} on which the {@code } element will be appended
* @param baseUrl the base URL of the stream, which must not be null and will be set as the
* content of the {@code } element
*/
- private static void generateBaseUrlElement(@Nonnull final Document document,
+ private static void generateBaseUrlElement(@Nonnull final Document doc,
@Nonnull final String baseUrl)
throws CreationException {
try {
- final Element representationElement = (Element) document.getElementsByTagName(
+ final Element representationElement = (Element) doc.getElementsByTagName(
REPRESENTATION).item(0);
- final Element baseURLElement = document.createElement(BASE_URL);
+ final Element baseURLElement = doc.createElement(BASE_URL);
baseURLElement.setTextContent(baseUrl);
representationElement.appendChild(baseURLElement);
} catch (final DOMException e) {
@@ -167,28 +166,23 @@ public final class YoutubeProgressiveDashManifestCreator {
* should be generated too.
*
*
- * @param document the {@link Document} on which the {@code } element will be
- * appended
+ * @param doc the {@link Document} on which the {@code } element will be appended
* @param itagItem the {@link ItagItem} to use, which must not be null
*/
- private static void generateSegmentBaseElement(@Nonnull final Document document,
+ private static void generateSegmentBaseElement(@Nonnull final Document doc,
@Nonnull final ItagItem itagItem)
throws CreationException {
try {
- final Element representationElement = (Element) document.getElementsByTagName(
+ final Element representationElement = (Element) doc.getElementsByTagName(
REPRESENTATION).item(0);
+ final Element segmentBaseElement = doc.createElement(SEGMENT_BASE);
- final Element segmentBaseElement = document.createElement(SEGMENT_BASE);
- final Attr indexRangeAttribute = document.createAttribute("indexRange");
-
+ final String range = itagItem.getIndexStart() + "-" + itagItem.getIndexEnd();
if (itagItem.getIndexStart() < 0 || itagItem.getIndexEnd() < 0) {
throw CreationException.couldNotAddElement(SEGMENT_BASE,
- "ItagItem's indexStart or " + "indexEnd are < 0: "
- + itagItem.getIndexStart() + "-" + itagItem.getIndexEnd());
+ "ItagItem's indexStart or " + "indexEnd are < 0: " + range);
}
-
- indexRangeAttribute.setValue(itagItem.getIndexStart() + "-" + itagItem.getIndexEnd());
- segmentBaseElement.setAttributeNode(indexRangeAttribute);
+ setAttribute(segmentBaseElement, doc, "indexRange", range);
representationElement.appendChild(segmentBaseElement);
} catch (final DOMException e) {
@@ -214,28 +208,24 @@ public final class YoutubeProgressiveDashManifestCreator {
* {@link #generateSegmentBaseElement(Document, ItagItem)}).
*
*
- * @param document the {@link Document} on which the {@code } element will
- * be appended
+ * @param doc the {@link Document} on which the {@code } element will be
+ * appended
* @param itagItem the {@link ItagItem} to use, which must not be null
*/
- private static void generateInitializationElement(@Nonnull final Document document,
+ private static void generateInitializationElement(@Nonnull final Document doc,
@Nonnull final ItagItem itagItem)
throws CreationException {
try {
- final Element segmentBaseElement = (Element) document.getElementsByTagName(
+ final Element segmentBaseElement = (Element) doc.getElementsByTagName(
SEGMENT_BASE).item(0);
+ final Element initializationElement = doc.createElement(INITIALIZATION);
- final Element initializationElement = document.createElement(INITIALIZATION);
- final Attr rangeAttribute = document.createAttribute("range");
-
+ final String range = itagItem.getInitStart() + "-" + itagItem.getInitEnd();
if (itagItem.getInitStart() < 0 || itagItem.getInitEnd() < 0) {
throw CreationException.couldNotAddElement(INITIALIZATION,
- "ItagItem's initStart and/or " + "initEnd are/is < 0: "
- + itagItem.getInitStart() + "-" + itagItem.getInitEnd());
+ "ItagItem's initStart and/or " + "initEnd are/is < 0: " + range);
}
-
- rangeAttribute.setValue(itagItem.getInitStart() + "-" + itagItem.getInitEnd());
- initializationElement.setAttributeNode(rangeAttribute);
+ setAttribute(initializationElement, doc, "range", range);
segmentBaseElement.appendChild(initializationElement);
} catch (final DOMException e) {
diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
index 5e57b2a13..a41325f3d 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java
@@ -1147,34 +1147,27 @@ public class YoutubeStreamExtractor extends StreamExtractor {
final java.util.function.Function streamBuilderHelper,
final String streamTypeExceptionMessage) throws ParsingException {
try {
- final List itagInfos = new ArrayList<>();
- if (html5StreamingData == null && androidStreamingData == null
- && iosStreamingData == null) {
- return Collections.emptyList();
- }
-
- final List> streamingDataAndCpnLoopList = new ArrayList<>();
- // Use the androidStreamingData object first because there is no n param and no
- // signatureCiphers in streaming URLs of the Android client
- streamingDataAndCpnLoopList.add(new Pair<>(androidStreamingData, androidCpn));
- streamingDataAndCpnLoopList.add(new Pair<>(html5StreamingData, html5Cpn));
- // Use the iosStreamingData object in the last position because most of the available
- // streams can be extracted with the Android and web clients and also because the iOS
- // client is only enabled by default on livestreams
- streamingDataAndCpnLoopList.add(new Pair<>(iosStreamingData, iosCpn));
-
- for (final Pair pair : streamingDataAndCpnLoopList) {
- itagInfos.addAll(getStreamsFromStreamingDataKey(pair.getFirst(), streamingDataKey,
- itagTypeWanted, pair.getSecond()));
- }
-
+ final String videoId = getId();
final List streamList = new ArrayList<>();
- for (final ItagInfo itagInfo : itagInfos) {
- final T stream = streamBuilderHelper.apply(itagInfo);
- if (!Stream.containSimilarStream(stream, streamList)) {
- streamList.add(stream);
- }
- }
+
+ java.util.stream.Stream.of(
+ // Use the androidStreamingData object first because there is no n param and no
+ // signatureCiphers in streaming URLs of the Android client
+ new Pair<>(androidStreamingData, androidCpn),
+ new Pair<>(html5StreamingData, html5Cpn),
+ // Use the iosStreamingData object in the last position because most of the
+ // available streams can be extracted with the Android and web clients and also
+ // because the iOS client is only enabled by default on livestreams
+ new Pair<>(iosStreamingData, iosCpn)
+ )
+ .flatMap(pair -> getStreamsFromStreamingDataKey(videoId, pair.getFirst(),
+ streamingDataKey, itagTypeWanted, pair.getSecond()))
+ .map(streamBuilderHelper)
+ .forEachOrdered(stream -> {
+ if (!Stream.containSimilarStream(stream, streamList)) {
+ streamList.add(stream);
+ }
+ });
return streamList;
} catch (final Exception e) {
@@ -1293,43 +1286,36 @@ public class YoutubeStreamExtractor extends StreamExtractor {
}
@Nonnull
- private List getStreamsFromStreamingDataKey(
+ private java.util.stream.Stream getStreamsFromStreamingDataKey(
+ final String videoId,
final JsonObject streamingData,
final String streamingDataKey,
@Nonnull final ItagItem.ItagType itagTypeWanted,
- @Nonnull final String contentPlaybackNonce) throws ParsingException {
+ @Nonnull final String contentPlaybackNonce) {
if (streamingData == null || !streamingData.has(streamingDataKey)) {
- return Collections.emptyList();
+ return java.util.stream.Stream.empty();
}
- final String videoId = getId();
- final List itagInfos = new ArrayList<>();
- final JsonArray formats = streamingData.getArray(streamingDataKey);
- for (int i = 0; i != formats.size(); ++i) {
- final JsonObject formatData = formats.getObject(i);
- final int itag = formatData.getInt("itag");
-
- if (!ItagItem.isSupported(itag)) {
- continue;
- }
-
- try {
- final ItagItem itagItem = ItagItem.getItag(itag);
- final ItagItem.ItagType itagType = itagItem.itagType;
- if (itagType == itagTypeWanted) {
- buildAndAddItagInfoToList(videoId, itagInfos, formatData, itagItem,
- itagType, contentPlaybackNonce);
- }
- } catch (final IOException | ExtractionException ignored) {
- }
- }
-
- return itagInfos;
+ return streamingData.getArray(streamingDataKey).stream()
+ .filter(JsonObject.class::isInstance)
+ .map(JsonObject.class::cast)
+ .map(formatData -> {
+ try {
+ final ItagItem itagItem = ItagItem.getItag(formatData.getInt("itag"));
+ if (itagItem.itagType == itagTypeWanted) {
+ return buildAndAddItagInfoToList(videoId, formatData, itagItem,
+ itagItem.itagType, contentPlaybackNonce);
+ }
+ } catch (final IOException | ExtractionException ignored) {
+ // if the itag is not supported and getItag fails, we end up here
+ }
+ return null;
+ })
+ .filter(Objects::nonNull);
}
- private void buildAndAddItagInfoToList(
+ private ItagInfo buildAndAddItagInfoToList(
@Nonnull final String videoId,
- @Nonnull final List itagInfos,
@Nonnull final JsonObject formatData,
@Nonnull final ItagItem itagItem,
@Nonnull final ItagItem.ItagType itagType,
@@ -1372,12 +1358,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
if (streamType == StreamType.LIVE_STREAM || streamType == StreamType.POST_LIVE_STREAM) {
itagItem.setTargetDurationSec(formatData.getInt("targetDurationSec"));
- }
-
- if (itagType == ItagItem.ItagType.VIDEO || itagType == ItagItem.ItagType.VIDEO_ONLY) {
+ } else if (itagType == ItagItem.ItagType.VIDEO
+ || itagType == ItagItem.ItagType.VIDEO_ONLY) {
itagItem.setFps(formatData.getInt("fps"));
- }
- if (itagType == ItagItem.ItagType.AUDIO) {
+ } else if (itagType == ItagItem.ItagType.AUDIO) {
// YouTube return the audio sample rate as a string
itagItem.setSampleRate(Integer.parseInt(formatData.getString("audioSampleRate")));
itagItem.setAudioChannels(formatData.getInt("audioChannels"));
@@ -1403,7 +1387,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
itagInfo.setIsUrl(streamType != StreamType.POST_LIVE_STREAM);
}
- itagInfos.add(itagInfo);
+ return itagInfo;
}
@Nonnull
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
index e9232c8cf..04d2b3fac 100644
--- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java
+++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java
@@ -94,24 +94,15 @@ public abstract class Stream implements Serializable {
* Note: This method always returns false if the stream passed is null.
*
*
- * @param cmp the stream object to be compared to this stream object
+ * @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 cmp) {
- if (cmp == null) {
+ public boolean equalStats(@Nullable final Stream other) {
+ if (other == null || mediaFormat == null || other.mediaFormat == null) {
return false;
}
-
- Boolean haveSameMediaFormatId = null;
- if (mediaFormat != null && cmp.mediaFormat != null) {
- haveSameMediaFormatId = mediaFormat.id == cmp.mediaFormat.id;
- }
- final boolean areUsingSameDeliveryMethodAndAreUrlStreams =
- deliveryMethod == cmp.deliveryMethod && isUrl == cmp.isUrl;
-
- return haveSameMediaFormatId != null
- ? haveSameMediaFormatId && areUsingSameDeliveryMethodAndAreUrlStreams
- : areUsingSameDeliveryMethodAndAreUrlStreams;
+ return mediaFormat.id == other.mediaFormat.id && deliveryMethod == other.deliveryMethod
+ && isUrl == other.isUrl;
}
/**
diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/utils/ManifestCreatorCacheTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/utils/ManifestCreatorCacheTest.java
index 7d3fa5b65..83c5c1dfb 100644
--- a/extractor/src/test/java/org/schabi/newpipe/extractor/utils/ManifestCreatorCacheTest.java
+++ b/extractor/src/test/java/org/schabi/newpipe/extractor/utils/ManifestCreatorCacheTest.java
@@ -53,6 +53,11 @@ class ManifestCreatorCacheTest {
+ "call");
}
+ /**
+ * Adds sample strings to the provided manifest creator cache, in order to test clear factor and
+ * maximum size.
+ * @param cache the cache to fill with some data
+ */
private static void setCacheContent(final ManifestCreatorCache cache) {
int i = 0;
while (i < 26) {