Skip to content

Commit 1b51eab

Browse files
authored
Merge pull request #859 from AudricV/delivery-methods-fixes-and-improvements
Fix extraction of some properties in ItagItems for YouTube livestreams and post-live streams and remove completely SoundCloud HLS workaround
2 parents c8a77da + 301a795 commit 1b51eab

File tree

2 files changed

+23
-90
lines changed

2 files changed

+23
-90
lines changed

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/extractors/SoundcloudStreamExtractor.java

Lines changed: 13 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE;
66
import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN;
77
import static org.schabi.newpipe.extractor.utils.Utils.EMPTY_STRING;
8-
import static org.schabi.newpipe.extractor.utils.Utils.HTTPS;
98
import static org.schabi.newpipe.extractor.utils.Utils.UTF_8;
109
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
1110

@@ -22,7 +21,6 @@
2221
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
2322
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
2423
import org.schabi.newpipe.extractor.exceptions.ParsingException;
25-
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
2624
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
2725
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
2826
import org.schabi.newpipe.extractor.localization.DateWrapper;
@@ -245,61 +243,30 @@ private void extractAudioStreams(@Nonnull final JsonArray transcodings,
245243
return;
246244
}
247245

248-
final String preset = transcoding.getString("preset", ID_UNKNOWN);
249-
final String protocol = transcoding.getObject("format").getString("protocol");
250-
final AudioStream.Builder builder = new AudioStream.Builder()
251-
.setId(preset);
252-
253246
try {
254-
// streamUrl can be either the MP3 progressive stream URL or the
255-
// manifest URL of the HLS MP3 stream (if there is no MP3 progressive
256-
// stream, see above)
257-
final String streamUrl = getTranscodingUrl(url);
247+
final String preset = transcoding.getString("preset", ID_UNKNOWN);
248+
final String protocol = transcoding.getObject("format")
249+
.getString("protocol");
250+
final AudioStream.Builder builder = new AudioStream.Builder()
251+
.setId(preset);
252+
253+
final boolean isHls = protocol.equals("hls");
254+
if (isHls) {
255+
builder.setDeliveryMethod(DeliveryMethod.HLS);
256+
}
257+
258+
builder.setContent(getTranscodingUrl(url), true);
258259

259260
if (preset.contains("mp3")) {
260261
// Don't add the MP3 HLS stream if there is a progressive stream
261-
// present because the two have the same bitrate
262-
final boolean isHls = protocol.equals("hls");
262+
// present because both have the same bitrate
263263
if (mp3ProgressiveInStreams && isHls) {
264264
return;
265265
}
266266

267267
builder.setMediaFormat(MediaFormat.MP3);
268268
builder.setAverageBitrate(128);
269-
270-
if (isHls) {
271-
builder.setDeliveryMethod(DeliveryMethod.HLS);
272-
builder.setContent(streamUrl, true);
273-
274-
final AudioStream hlsStream = builder.build();
275-
if (!Stream.containSimilarStream(hlsStream, audioStreams)) {
276-
audioStreams.add(hlsStream);
277-
}
278-
279-
final String progressiveHlsUrl =
280-
getSingleUrlFromHlsManifest(streamUrl);
281-
builder.setDeliveryMethod(DeliveryMethod.PROGRESSIVE_HTTP);
282-
builder.setContent(progressiveHlsUrl, true);
283-
284-
final AudioStream progressiveHlsStream = builder.build();
285-
if (!Stream.containSimilarStream(
286-
progressiveHlsStream, audioStreams)) {
287-
audioStreams.add(progressiveHlsStream);
288-
}
289-
290-
// The MP3 HLS stream has been added in both versions (HLS and
291-
// progressive with the manifest parsing trick), so we need to
292-
// continue (otherwise the code would try to add again the stream,
293-
// which would be not added because the containsSimilarStream
294-
// method would return false and an audio stream object would be
295-
// created for nothing)
296-
return;
297-
} else {
298-
builder.setContent(streamUrl, true);
299-
}
300269
} else if (preset.contains("opus")) {
301-
// The HLS manifest trick doesn't work for opus streams
302-
builder.setContent(streamUrl, true);
303270
builder.setMediaFormat(MediaFormat.OPUS);
304271
builder.setAverageBitrate(64);
305272
builder.setDeliveryMethod(DeliveryMethod.HLS);
@@ -352,47 +319,6 @@ public void extractDownloadableFileIfAvailable(final List<AudioStream> audioStre
352319
}
353320
}
354321

355-
/**
356-
* Parses a SoundCloud HLS MP3 manifest to get a single URL of HLS streams.
357-
*
358-
* <p>
359-
* This method downloads the provided manifest URL, finds all web occurrences in the manifest,
360-
* gets the last segment URL, changes its segment range to {@code 0/track-length}, and return
361-
* this as a string.
362-
* </p>
363-
*
364-
* <p>
365-
* This was working before for Opus streams, but has been broken by SoundCloud.
366-
* </p>
367-
*
368-
* @param hlsManifestUrl the URL of the manifest to be parsed
369-
* @return a single URL that contains a range equal to the length of the track
370-
*/
371-
@Nonnull
372-
private static String getSingleUrlFromHlsManifest(@Nonnull final String hlsManifestUrl)
373-
throws ParsingException {
374-
final String hlsManifestResponse;
375-
376-
try {
377-
hlsManifestResponse = NewPipe.getDownloader().get(hlsManifestUrl).responseBody();
378-
} catch (final IOException | ReCaptchaException e) {
379-
throw new ParsingException("Could not get SoundCloud HLS manifest");
380-
}
381-
382-
final String[] lines = hlsManifestResponse.split("\\r?\\n");
383-
for (int l = lines.length - 1; l >= 0; l--) {
384-
final String line = lines[l];
385-
// Get the last URL from manifest, because it contains the range of the stream
386-
if (line.trim().length() != 0 && !line.startsWith("#") && line.startsWith(HTTPS)) {
387-
final String[] hlsLastRangeUrlArray = line.split("/");
388-
return HTTPS + hlsLastRangeUrlArray[2] + "/media/0/" + hlsLastRangeUrlArray[5]
389-
+ "/" + hlsLastRangeUrlArray[6];
390-
}
391-
}
392-
393-
throw new ParsingException("Could not get any URL from HLS manifest");
394-
}
395-
396322
private static String urlEncode(final String value) {
397323
try {
398324
return URLEncoder.encode(value, UTF_8);

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,13 +1358,20 @@ private ItagInfo buildAndAddItagInfoToList(
13581358

13591359
if (streamType == StreamType.LIVE_STREAM || streamType == StreamType.POST_LIVE_STREAM) {
13601360
itagItem.setTargetDurationSec(formatData.getInt("targetDurationSec"));
1361-
} else if (itagType == ItagItem.ItagType.VIDEO
1362-
|| itagType == ItagItem.ItagType.VIDEO_ONLY) {
1361+
}
1362+
1363+
if (itagType == ItagItem.ItagType.VIDEO || itagType == ItagItem.ItagType.VIDEO_ONLY) {
13631364
itagItem.setFps(formatData.getInt("fps"));
13641365
} else if (itagType == ItagItem.ItagType.AUDIO) {
13651366
// YouTube return the audio sample rate as a string
13661367
itagItem.setSampleRate(Integer.parseInt(formatData.getString("audioSampleRate")));
1367-
itagItem.setAudioChannels(formatData.getInt("audioChannels"));
1368+
itagItem.setAudioChannels(formatData.getInt("audioChannels",
1369+
// Most audio streams have two audio channels, so use this value if the real
1370+
// count cannot be extracted
1371+
// Doing this prevents an exception when generating the
1372+
// AudioChannelConfiguration element of DASH manifests of audio streams in
1373+
// YoutubeDashManifestCreatorUtils
1374+
2));
13681375
}
13691376

13701377
// YouTube return the content length and the approximate duration as strings

0 commit comments

Comments
 (0)