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 ;
7+
8+ import org .schabi .newpipe .extractor .NewPipe ;
69import org .schabi .newpipe .extractor .StreamingService ;
710import org .schabi .newpipe .extractor .downloader .Downloader ;
811import org .schabi .newpipe .extractor .exceptions .ExtractionException ;
1316import org .schabi .newpipe .extractor .stream .StreamInfoItemsCollector ;
1417
1518import javax .annotation .Nonnull ;
19+ import javax .annotation .Nullable ;
20+
1621import java .io .IOException ;
22+ import java .util .ArrayList ;
23+ import java .util .List ;
1724
1825@ SuppressWarnings ("WeakerAccess" )
1926public class SoundcloudPlaylistExtractor extends PlaylistExtractor {
27+ private static final int streamsPerRequestedPage = 15 ;
28+
2029 private String playlistId ;
2130 private JsonObject playlist ;
2231
23- private StreamInfoItemsCollector streamInfoItemsCollector = null ;
24- private String nextPageUrl = null ;
32+ private StreamInfoItemsCollector streamInfoItemsCollector ;
33+ private List <Integer > nextTrackIds ;
34+ private int nextTrackIdsIndex ;
35+ private String nextPageUrl ;
2536
2637 public SoundcloudPlaylistExtractor (StreamingService service , ListLinkHandler linkHandler ) {
2738 super (service , linkHandler );
@@ -31,7 +42,7 @@ public SoundcloudPlaylistExtractor(StreamingService service, ListLinkHandler lin
3142 public void onFetchPage (@ Nonnull Downloader downloader ) throws IOException , ExtractionException {
3243
3344 playlistId = getLinkHandler ().getId ();
34- String apiUrl = "https://api.soundcloud.com/playlists/" + playlistId +
45+ String apiUrl = "https://api-v2 .soundcloud.com/playlists/" + playlistId +
3546 "?client_id=" + SoundcloudParsingHelper .clientId () +
3647 "&representation=compact" ;
3748
@@ -110,27 +121,55 @@ public long getStreamCount() {
110121 @ Override
111122 public InfoItemsPage <StreamInfoItem > getInitialPage () throws IOException , ExtractionException {
112123 if (streamInfoItemsCollector == null ) {
113- computeStreamsAndNextPageUrl ();
124+ computeInitialTracksAndNextIds ();
114125 }
115126 return new InfoItemsPage <>(streamInfoItemsCollector , getNextPageUrl ());
116127 }
117128
118- private void computeStreamsAndNextPageUrl () throws ExtractionException , IOException {
129+ private void computeInitialTracksAndNextIds () {
119130 streamInfoItemsCollector = new StreamInfoItemsCollector (getServiceId ());
131+ nextTrackIds = new ArrayList <>();
132+ nextTrackIdsIndex = 0 ;
133+
134+ JsonArray tracks = playlist .getArray ("tracks" );
135+ for (Object o : tracks ) {
136+ if (o instanceof JsonObject ) {
137+ JsonObject track = (JsonObject ) o ;
138+ if (track .has ("title" )) { // i.e. if full info is available
139+ streamInfoItemsCollector .commit (new SoundcloudStreamInfoItemExtractor (track ));
140+ } else {
141+ nextTrackIds .add (track .getInt ("id" ));
142+ }
143+ }
144+ }
145+ }
120146
121- // Note the "api", NOT "api-v2"
122- String apiUrl = "https://api.soundcloud.com/playlists/" + getId () + "/tracks"
123- + "?client_id=" + SoundcloudParsingHelper . clientId ()
124- + "&limit=20"
125- + "&linked_partitioning=1" ;
147+ private void computeAnotherNextPageUrl () throws IOException , ExtractionException {
148+ if ( nextTrackIdsIndex >= nextTrackIds . size ()) {
149+ nextPageUrl = "" ; // there are no more tracks
150+ return ;
151+ }
126152
127- nextPageUrl = SoundcloudParsingHelper .getStreamsFromApiMinItems (15 , streamInfoItemsCollector , apiUrl );
153+ StringBuilder urlBuilder = new StringBuilder ("https://api-v2.soundcloud.com/tracks?client_id=" );
154+ urlBuilder .append (SoundcloudParsingHelper .clientId ());
155+ urlBuilder .append ("&ids=" );
156+
157+ int upperIndex = Math .min (nextTrackIdsIndex + streamsPerRequestedPage , nextTrackIds .size ());
158+ for (int i = nextTrackIdsIndex ; i < upperIndex ; ++i ) {
159+ urlBuilder .append (nextTrackIds .get (i ));
160+ urlBuilder .append ("," ); // a , at the end is ok
161+ }
162+
163+ nextPageUrl = urlBuilder .toString ();
128164 }
129165
130166 @ Override
131167 public String getNextPageUrl () throws IOException , ExtractionException {
132168 if (nextPageUrl == null ) {
133- computeStreamsAndNextPageUrl ();
169+ if (nextTrackIds == null ) {
170+ computeInitialTracksAndNextIds ();
171+ }
172+ computeAnotherNextPageUrl ();
134173 }
135174 return nextPageUrl ;
136175 }
@@ -142,8 +181,20 @@ public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException,
142181 }
143182
144183 StreamInfoItemsCollector collector = new StreamInfoItemsCollector (getServiceId ());
145- String nextPageUrl = SoundcloudParsingHelper .getStreamsFromApiMinItems (15 , collector , pageUrl );
184+ String response = NewPipe .getDownloader ().get (pageUrl , getExtractorLocalization ()).responseBody ();
185+
186+ try {
187+ JsonArray tracks = JsonParser .array ().from (response );
188+ for (Object track : tracks ) {
189+ if (track instanceof JsonObject ) {
190+ collector .commit (new SoundcloudStreamInfoItemExtractor ((JsonObject ) track ));
191+ }
192+ }
193+ } catch (JsonParserException e ) {
194+ throw new ParsingException ("Could not parse json response" , e );
195+ }
146196
197+ computeAnotherNextPageUrl ();
147198 return new InfoItemsPage <>(collector , nextPageUrl );
148199 }
149200}
0 commit comments