@@ -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