11package org .schabi .newpipe .extractor .services .soundcloud ;
22
3+ import com .grack .nanojson .JsonArray ;
34import com .grack .nanojson .JsonObject ;
45import com .grack .nanojson .JsonParser ;
56import com .grack .nanojson .JsonParserException ;
@@ -143,7 +144,7 @@ public List<AudioStream> getAudioStreams() throws IOException, ExtractionExcepti
143144 List <AudioStream > audioStreams = new ArrayList <>();
144145 Downloader dl = NewPipe .getDownloader ();
145146
146- String apiUrl = "https://api.soundcloud.com/i1/ tracks/" + urlEncode (getId ()) + "/streams"
147+ String apiUrl = "https://api-v2 .soundcloud.com/tracks/" + urlEncode (getId ())
147148 + "?client_id=" + urlEncode (SoundcloudParsingHelper .clientId ());
148149
149150 String response = dl .get (apiUrl , getExtractorLocalization ()).responseBody ();
@@ -154,11 +155,45 @@ public List<AudioStream> getAudioStreams() throws IOException, ExtractionExcepti
154155 throw new ParsingException ("Could not parse json response" , e );
155156 }
156157
157- String mp3Url = responseObject .getString ("http_mp3_128_url" );
158- if (mp3Url != null && !mp3Url .isEmpty ()) {
159- audioStreams .add (new AudioStream (mp3Url , MediaFormat .MP3 , 128 ));
160- } else {
161- throw new ExtractionException ("Could not get SoundCloud's track audio url" );
158+ // Streams can be streamable and downloadable - or explicitly not.
159+ // For playing the track, it is only necessary to have a streamable track.
160+ // If this is not the case, this track might not be published yet.
161+ if (!responseObject .getBoolean ("streamable" )) return audioStreams ;
162+
163+ try {
164+ JsonArray transcodings = responseObject .getObject ("media" ).getArray ("transcodings" );
165+
166+ // get information about what stream formats are available
167+ for (Object transcoding : transcodings ) {
168+
169+ JsonObject t = (JsonObject ) transcoding ;
170+ String url = t .getString ("url" );
171+
172+ if (url != null && !url .isEmpty ()) {
173+
174+ // We can only play the mp3 format, but not handle m3u playlists / streams.
175+ // what about Opus?
176+ if (t .getString ("preset" ).contains ("mp3" )
177+ && t .getObject ("format" ).getString ("protocol" ).equals ("progressive" )) {
178+ // This url points to the endpoint which generates a unique and short living url to the stream.
179+ // TODO: move this to a separate method to generate valid urls when needed (e.g. resuming a paused stream)
180+ url += "?client_id=" + SoundcloudParsingHelper .clientId ();
181+ String res = dl .get (url ).responseBody ();
182+
183+ try {
184+ JsonObject mp3UrlObject = JsonParser .object ().from (res );
185+ // Links in this file are also only valid for a short period.
186+ audioStreams .add (new AudioStream (mp3UrlObject .getString ("url" ),
187+ MediaFormat .MP3 , 128 ));
188+ } catch (JsonParserException e ) {
189+ throw new ParsingException ("Could not parse streamable url" , e );
190+ }
191+ }
192+ }
193+ }
194+
195+ } catch (NullPointerException e ) {
196+ throw new ExtractionException ("Could not get SoundCloud's track audio url" , e );
162197 }
163198
164199 return audioStreams ;
0 commit comments