1818import org .apache .http .client .utils .URIBuilder ;
1919import org .apache .http .impl .client .HttpClientBuilder ;
2020import org .apache .http .util .EntityUtils ;
21+ import org .jetbrains .annotations .Nullable ;
2122
2223import java .io .DataInput ;
2324import java .io .DataOutput ;
2728import java .nio .charset .StandardCharsets ;
2829import java .util .ArrayList ;
2930import java .util .List ;
31+ import java .util .Objects ;
3032import java .util .function .Consumer ;
3133import java .util .function .Function ;
3234import java .util .regex .Matcher ;
@@ -51,6 +53,7 @@ public class SoundCloudAudioSourceManager implements AudioSourceManager, HttpCon
5153 private static final String SEARCH_PREFIX = "scsearch" ;
5254 private static final String SEARCH_PREFIX_DEFAULT = "scsearch:" ;
5355 private static final String SEARCH_REGEX = SEARCH_PREFIX + "\\ [([0-9]{1,9}),([0-9]{1,9})\\ ]:\\ s*(.*)\\ s*" ;
56+ private static final String FULL_TRACK_UNAVAILABLE_MARKER = "SUB_HIGH_TIER" ;
5457
5558 private static final Pattern mobileUrlPattern = Pattern .compile (MOBILE_URL_REGEX );
5659 private static final Pattern trackUrlPattern = Pattern .compile (TRACK_URL_REGEX );
@@ -67,14 +70,20 @@ public class SoundCloudAudioSourceManager implements AudioSourceManager, HttpCon
6770 private final HttpInterfaceManager httpInterfaceManager ;
6871 private final SoundCloudClientIdTracker clientIdTracker ;
6972 private final boolean allowSearch ;
73+ private final boolean filterOutPreviewTracks ;
7074
7175 public static SoundCloudAudioSourceManager createDefault () {
7276 SoundCloudDataReader dataReader = new DefaultSoundCloudDataReader ();
7377 SoundCloudDataLoader dataLoader = new DefaultSoundCloudDataLoader ();
7478 SoundCloudFormatHandler formatHandler = new DefaultSoundCloudFormatHandler ();
7579
76- return new SoundCloudAudioSourceManager (true , dataReader , dataLoader , formatHandler ,
77- new DefaultSoundCloudPlaylistLoader (dataLoader , dataReader , formatHandler ));
80+ return new SoundCloudAudioSourceManager (true ,
81+ dataReader ,
82+ dataLoader ,
83+ formatHandler ,
84+ new DefaultSoundCloudPlaylistLoader (dataLoader , dataReader , formatHandler ),
85+ false
86+ );
7887 }
7988
8089 public static Builder builder () {
@@ -91,13 +100,15 @@ public SoundCloudAudioSourceManager(
91100 SoundCloudDataReader dataReader ,
92101 SoundCloudDataLoader dataLoader ,
93102 SoundCloudFormatHandler formatHandler ,
94- SoundCloudPlaylistLoader playlistLoader
103+ SoundCloudPlaylistLoader playlistLoader ,
104+ boolean filterOutPreviewTracks
95105 ) {
96106 this .allowSearch = allowSearch ;
97107 this .dataReader = dataReader ;
98108 this .dataLoader = dataLoader ;
99109 this .formatHandler = formatHandler ;
100110 this .playlistLoader = playlistLoader ;
111+ this .filterOutPreviewTracks = filterOutPreviewTracks ;
101112
102113 httpInterfaceManager = HttpClientTools .createDefaultThreadLocalManager ();
103114 clientIdTracker = new SoundCloudClientIdTracker (httpInterfaceManager );
@@ -209,7 +220,13 @@ private AudioItem processAsLikedTracks(AudioReference reference) {
209220 }
210221 }
211222
223+ @ Nullable
212224 public AudioTrack loadTrack (String trackWebUrl ) {
225+ return loadTrack (trackWebUrl , true );
226+ }
227+
228+ @ Nullable
229+ public AudioTrack loadTrack (String trackWebUrl , boolean honorPreviewFilter ) {
213230 try (HttpInterface httpInterface = getHttpInterface ()) {
214231 JsonBrowser rootData = dataLoader .load (httpInterface , trackWebUrl );
215232 JsonBrowser trackData = dataReader .findTrackData (rootData );
@@ -218,6 +235,10 @@ public AudioTrack loadTrack(String trackWebUrl) {
218235 throw new FriendlyException ("This track is not available" , COMMON , null );
219236 }
220237
238+ if (honorPreviewFilter && filterOutPreviewTracks && Objects .equals (trackData .get ("monetization_model" ).text (), FULL_TRACK_UNAVAILABLE_MARKER )) {
239+ return null ;
240+ }
241+
221242 return loadFromTrackData (trackData );
222243 } catch (IOException e ) {
223244 throw new FriendlyException ("Loading track from SoundCloud failed." , SUSPICIOUS , e );
@@ -363,6 +384,7 @@ public static class Builder {
363384 private SoundCloudFormatHandler formatHandler ;
364385 private SoundCloudPlaylistLoader playlistLoader ;
365386 private PlaylistLoaderFactory playlistLoaderFactory ;
387+ private boolean filterOutPreviewTracks = false ;
366388
367389 public Builder withAllowSearch (boolean allowSearch ) {
368390 this .allowSearch = allowSearch ;
@@ -394,6 +416,16 @@ public Builder withPlaylistLoaderFactory(PlaylistLoaderFactory playlistLoaderFac
394416 return this ;
395417 }
396418
419+ /**
420+ * Whether to filter out short preview tracks that are only fully available with a Soundcloud subscription.
421+ * Lavaplayer does not support getting the full track. Only affects new AudioTracks being loaded by this source,
422+ * with no effect on deserialized tracks.
423+ */
424+ public Builder withFilterOutPreviewTracks (boolean filterOutPreviewTracks ) {
425+ this .filterOutPreviewTracks = filterOutPreviewTracks ;
426+ return this ;
427+ }
428+
397429 public SoundCloudAudioSourceManager build () {
398430 SoundCloudDataReader usedDataReader = dataReader ;
399431
@@ -432,7 +464,8 @@ public SoundCloudAudioSourceManager build() {
432464 usedDataReader ,
433465 usedDataLoader ,
434466 usedFormatHandler ,
435- usedPlaylistLoader
467+ usedPlaylistLoader ,
468+ filterOutPreviewTracks
436469 );
437470 }
438471
0 commit comments