3838import java .util .Set ;
3939import java .util .function .Consumer ;
4040import java .util .function .Function ;
41+ import java .util .regex .Matcher ;
4142import java .util .regex .Pattern ;
4243import java .util .stream .Collectors ;
4344
4445public class SpotifySourceManager extends MirroringAudioSourceManager implements HttpConfigurable , AudioSearchManager , AudioLyricsManager {
4546
4647 public static final Pattern URL_PATTERN = Pattern .compile ("(https?://)(www\\ .)?open\\ .spotify\\ .com/((?<region>[a-zA-Z-]+)/)?(user/(?<user>[a-zA-Z0-9-_]+)/)?(?<type>track|album|playlist|artist)/(?<identifier>[a-zA-Z0-9-_]+)" );
48+ public static final Pattern RADIO_MIX_QUERY_PATTERN = Pattern .compile ("mix:(?<seedType>album|artist|track|isrc):(?<seed>[a-zA-Z0-9-_]+)" );
4749 public static final String SEARCH_PREFIX = "spsearch:" ;
4850 public static final String RECOMMENDATIONS_PREFIX = "sprec:" ;
4951 public static final String PREVIEW_PREFIX = "spprev:" ;
@@ -65,6 +67,7 @@ public class SpotifySourceManager extends MirroringAudioSourceManager implements
6567 private boolean localFiles ;
6668 private boolean resolveArtistsInSearch = true ;
6769 private boolean preferAnonymousToken = false ;
70+
6871
6972 public SpotifySourceManager (String [] providers , String clientId , String clientSecret , String countryCode , AudioPlayerManager audioPlayerManager ) {
7073 this (clientId , clientSecret , null , countryCode , unused -> audioPlayerManager , new DefaultMirroringAudioTrackResolver (providers ));
@@ -83,7 +86,7 @@ public SpotifySourceManager(String clientId, String clientSecret, String country
8386 }
8487
8588 public SpotifySourceManager (String clientId , String clientSecret , String spDc , String countryCode , Function <Void , AudioPlayerManager > audioPlayerManager , MirroringAudioTrackResolver mirroringAudioTrackResolver ) {
86- this (clientId , clientSecret , false , spDc , countryCode , audioPlayerManager , mirroringAudioTrackResolver );
89+ this (clientId , clientSecret , false , spDc , countryCode , audioPlayerManager , mirroringAudioTrackResolver );
8790 }
8891
8992 public SpotifySourceManager (String clientId , String clientSecret , boolean preferAnonymousToken , String spDc , String countryCode , Function <Void , AudioPlayerManager > audioPlayerManager , MirroringAudioTrackResolver mirroringAudioTrackResolver ) {
@@ -376,6 +379,36 @@ public AudioItem getSearch(String query, boolean preview) throws IOException {
376379 }
377380
378381 public AudioItem getRecommendations (String query , boolean preview ) throws IOException {
382+ Matcher matcher = RADIO_MIX_QUERY_PATTERN .matcher (query );
383+ if (matcher .find ()) {
384+ String seedType = matcher .group ("seedType" );
385+ String seed = matcher .group ("seed" );
386+ if (seedType .equals ("isrc" )) {
387+ AudioItem item = this .getSearch ("isrc:" + seed , preview );
388+ if (item == AudioReference .NO_TRACK ) {
389+ return AudioReference .NO_TRACK ;
390+ }
391+ if (item instanceof AudioTrack ) {
392+ seed = ((AudioTrack ) item ).getIdentifier ();
393+ seedType = "track" ;
394+ } else if (item instanceof AudioPlaylist ) {
395+ var playlist = (AudioPlaylist ) item ;
396+ if (!playlist .getTracks ().isEmpty ()) {
397+ seed = playlist .getTracks ().get (0 ).getIdentifier ();
398+ seedType = "track" ;
399+ } else {
400+ return AudioReference .NO_TRACK ;
401+ }
402+ }
403+ }
404+ JsonBrowser rjson = this .getJson (CLIENT_API_BASE + "inspiredby-mix/v2/seed_to_playlist/spotify:" + seedType + ":" + seed + "?response-format=json" , true , this .preferAnonymousToken );
405+ JsonBrowser mediaItems = rjson .get ("mediaItems" );
406+ if (mediaItems .isList () && mediaItems .values ().size () > 0 ) {
407+ String playlistId = mediaItems .index (0 ).get ("uri" ).text ().split (":" )[2 ];
408+ return this .getPlaylist (playlistId , preview );
409+ }
410+
411+ }
379412 var json = this .getJson (API_BASE + "recommendations?" + query , false , false );
380413 if (json == null || json .get ("tracks" ).values ().isEmpty ()) {
381414 return AudioReference .NO_TRACK ;
0 commit comments