Skip to content

Commit d61d9d1

Browse files
committed
Refactor getAudioStreams method of SoundcloudStreamExtractor
Split the method into private methods, in order to have a better reading.
1 parent a7b15b5 commit d61d9d1

1 file changed

Lines changed: 86 additions & 83 deletions

File tree

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

Lines changed: 86 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,8 @@ public String getHlsUrl() {
184184
}
185185

186186
@Override
187-
public List<AudioStream> getAudioStreams() throws IOException, ExtractionException {
187+
public List<AudioStream> getAudioStreams() throws ExtractionException {
188188
final List<AudioStream> audioStreams = new ArrayList<>();
189-
final Downloader dl = NewPipe.getDownloader();
190189

191190
// Streams can be streamable and downloadable - or explicitly not.
192191
// For playing the track, it is only necessary to have a streamable track.
@@ -195,99 +194,101 @@ public List<AudioStream> getAudioStreams() throws IOException, ExtractionExcepti
195194

196195
try {
197196
final JsonArray transcodings = track.getObject("media").getArray("transcodings");
198-
// Iterate a first time to see if there is a progressive MP3 stream available.
199-
// If yes, the MP3 HLS stream will be not added to audioStreams.
200-
boolean mp3ProgressiveStreamInTranscodings = false;
201-
202-
for (final Object transcoding : transcodings) {
203-
final JsonObject transcodingJsonObject = (JsonObject) transcoding;
204-
if (transcodingJsonObject.getString("preset").contains("mp3") &&
205-
transcodingJsonObject.getObject("format").getString("protocol").equals("progressive")) {
206-
mp3ProgressiveStreamInTranscodings = true;
207-
break;
208-
}
197+
if (transcodings != null) {
198+
// Get information about what stream formats are available
199+
setUpAudioStreams(transcodings, checkMp3ProgressivePresence(transcodings),
200+
audioStreams);
209201
}
202+
} catch (final NullPointerException e) {
203+
throw new ExtractionException("Could not get SoundCloud's tracks audio URL", e);
204+
}
210205

211-
// Get information about what stream formats are available
212-
for (final Object transcoding : transcodings) {
213-
final JsonObject transcodingJsonObject = (JsonObject) transcoding;
214-
final String mediaUrl;
215-
final String preset = transcodingJsonObject.getString("preset");
216-
final String protocol = transcodingJsonObject.getObject("format").getString("protocol");
217-
String url = transcodingJsonObject.getString("url");
218-
final MediaFormat mediaFormat;
219-
final int bitrate;
220-
221-
if (!isNullOrEmpty(url)) {
222-
if (preset.contains("mp3")) {
223-
// Don't add the MP3 HLS stream if there is a progressive stream present
224-
// because the two have the same bitrate
225-
if (mp3ProgressiveStreamInTranscodings && protocol.equals("hls")) {
226-
continue;
227-
}
228-
mediaFormat = MediaFormat.MP3;
229-
bitrate = 128;
230-
} else if (preset.contains("opus")) {
231-
mediaFormat = MediaFormat.OPUS;
232-
bitrate = 64;
233-
} else {
234-
// Unknown format
235-
continue;
236-
}
206+
return audioStreams;
207+
}
208+
209+
private static boolean checkMp3ProgressivePresence(final JsonArray transcodings) {
210+
boolean presence = false;
211+
for (final Object transcoding : transcodings) {
212+
final JsonObject transcodingJsonObject = (JsonObject) transcoding;
213+
if (transcodingJsonObject.getString("preset").contains("mp3") &&
214+
transcodingJsonObject.getObject("format").getString("protocol")
215+
.equals("progressive")) {
216+
presence = true;
217+
break;
218+
}
219+
}
220+
return presence;
221+
}
237222

238-
// TODO: move this to a separate method to generate valid urls when needed (e.g. resuming a paused stream)
239-
240-
if (protocol.equals("progressive")) {
241-
// This url points to the endpoint which generates a unique and short living url to the stream.
242-
url += "?client_id=" + SoundcloudParsingHelper.clientId();
243-
final String res = dl.get(url).responseBody();
244-
245-
try {
246-
final JsonObject mp3UrlObject = JsonParser.object().from(res);
247-
// Links in this file are also only valid for a short period.
248-
mediaUrl = mp3UrlObject.getString("url");
249-
} catch (final JsonParserException e) {
250-
throw new ParsingException("Could not parse streamable url", e);
251-
}
252-
} else if (protocol.equals("hls")) {
253-
// This url points to the endpoint which generates a unique and short living url to the stream.
254-
url += "?client_id=" + SoundcloudParsingHelper.clientId();
255-
final String res = dl.get(url).responseBody();
256-
257-
try {
258-
final JsonObject mp3HlsUrlObject = JsonParser.object().from(res);
259-
// Links in this file are also only valid for a short period.
260-
try {
261-
mediaUrl = getSingleUrlFromHlsManifest(mp3HlsUrlObject.getString("url"));
262-
} catch (final ParsingException e) {
263-
// Something went during HLS manifest parsing, don't add this stream to audioStreams
264-
continue;
265-
}
266-
} catch (final JsonParserException e) {
267-
throw new ParsingException("Could not parse streamable url", e);
268-
}
269-
} else {
270-
// Unknown protocol
223+
@Nonnull
224+
private static String getTranscodingUrl(final String endpointUrl, final String protocol) throws IOException, ExtractionException {
225+
final Downloader downloader = NewPipe.getDownloader();
226+
final String apiStreamUrl = endpointUrl + "?client_id=" + SoundcloudParsingHelper.clientId();
227+
final String response = downloader.get(apiStreamUrl).responseBody();
228+
final JsonObject urlObject;
229+
try {
230+
urlObject = JsonParser.object().from(response);
231+
} catch (final JsonParserException e) {
232+
throw new ParsingException("Could not parse streamable url", e);
233+
}
234+
final String urlString = urlObject.getString("url");
235+
236+
if (protocol.equals("progressive")) {
237+
return urlString;
238+
} else if (protocol.equals("hls")) {
239+
return getSingleUrlFromHlsManifest(urlString);
240+
}
241+
// else, unknown protocol
242+
return "";
243+
}
244+
245+
private static void setUpAudioStreams(final JsonArray transcodings,
246+
final boolean mp3ProgressiveInStreams,
247+
final List<AudioStream> audioStreams) {
248+
for (final Object transcoding : transcodings) {
249+
final JsonObject transcodingJsonObject = (JsonObject) transcoding;
250+
final String mediaUrl;
251+
final String preset = transcodingJsonObject.getString("preset");
252+
final String protocol = transcodingJsonObject.getObject("format").getString("protocol");
253+
final String url = transcodingJsonObject.getString("url");
254+
final MediaFormat mediaFormat;
255+
final int bitrate;
256+
257+
if (!isNullOrEmpty(url)) {
258+
if (preset.contains("mp3")) {
259+
// Don't add the MP3 HLS stream if there is a progressive stream present
260+
// because the two have the same bitrate
261+
if (mp3ProgressiveInStreams && protocol.equals("hls")) {
271262
continue;
272263
}
264+
mediaFormat = MediaFormat.MP3;
265+
bitrate = 128;
266+
} else if (preset.contains("opus")) {
267+
mediaFormat = MediaFormat.OPUS;
268+
bitrate = 64;
269+
} else {
270+
// Unknown format
271+
continue;
272+
}
273273

274-
audioStreams.add(new AudioStream(mediaUrl, mediaFormat, bitrate));
274+
try {
275+
mediaUrl = getTranscodingUrl(url, protocol);
276+
} catch (final Exception e) {
277+
// something went wrong when parsing this transcoding
278+
continue;
275279
}
280+
audioStreams.add(new AudioStream(mediaUrl, mediaFormat, bitrate));
276281
}
277-
278-
} catch (final NullPointerException e) {
279-
throw new ExtractionException("Could not get SoundCloud's track audio url", e);
280282
}
281-
282-
return audioStreams;
283283
}
284284

285-
private final static Pattern PATTERN_WEB_URLS_IN_HLS_MANIFESTS = Pattern.compile("((http?|https?):((//)|(\\\\))+[\\w\\d:#@%/;$()~_?+-=\\\\.&]*)");
285+
private final static Pattern PATTERN_HTTPS_URLS_IN_HLS_MANIFESTS = Pattern.compile("((https?):((//)|(\\\\))+[\\w\\d:#@%/;$()~_?+-=\\\\.&]*)");
286286

287287
/** Parses a SoundCloud HLS manifest to get a single URL of HLS streams.
288288
* <p>
289289
* This method downloads the provided manifest URL, find all web occurrences using a regex, get
290-
* the last segment URL, changes its segment range to {@code 0/track-length} and return this string.
290+
* the last segment URL, changes its segment range to {@code 0/track-length} and return this
291+
* string.
291292
* @param hlsManifestUrl the URL of the manifest to be parsed
292293
* @return a single URL that contains a range equal to the length of the track
293294
*/
@@ -298,11 +299,12 @@ private static String getSingleUrlFromHlsManifest(final String hlsManifestUrl) t
298299
try {
299300
hlsManifestResponse = dl.get(hlsManifestUrl).responseBody();
300301
} catch (final IOException | ReCaptchaException e) {
301-
throw new ParsingException("Could not get SoundCloud HLS Manifest");
302+
throw new ParsingException("Could not get SoundCloud HLS manifest");
302303
}
303304

304305
final List<String> hlsRangesList = new ArrayList<>();
305-
final Matcher pattern_matches = PATTERN_WEB_URLS_IN_HLS_MANIFESTS.matcher(hlsManifestResponse);
306+
final Matcher pattern_matches = PATTERN_HTTPS_URLS_IN_HLS_MANIFESTS
307+
.matcher(hlsManifestResponse);
306308

307309
while (pattern_matches.find()) {
308310
hlsRangesList.add(hlsManifestResponse.substring(pattern_matches.start(0),
@@ -312,7 +314,8 @@ private static String getSingleUrlFromHlsManifest(final String hlsManifestUrl) t
312314
final String hlsLastRangeUrl = hlsRangesList.get(hlsRangesList.size() - 1);
313315
final String[] hlsLastRangeUrlArray = hlsLastRangeUrl.split("/");
314316

315-
return HTTPS + hlsLastRangeUrlArray[2] + "/media/0/" + hlsLastRangeUrlArray[5] + "/" + hlsLastRangeUrlArray[6];
317+
return HTTPS + hlsLastRangeUrlArray[2] + "/media/0/" + hlsLastRangeUrlArray[5] + "/"
318+
+ hlsLastRangeUrlArray[6];
316319
}
317320

318321
private static String urlEncode(final String value) {

0 commit comments

Comments
 (0)