mirror of
				https://github.com/JonasunderscoreJones/ZtereoMUSIC.git
				synced 2025-10-26 12:59:19 +01:00 
			
		
		
		
	new: spotify songs support!
This commit is contained in:
		
							parent
							
								
									5ee95f97e3
								
							
						
					
					
						commit
						6a46016fb1
					
				
					 3 changed files with 146 additions and 9 deletions
				
			
		|  | @ -6,6 +6,7 @@ import codes.ztereohype.ztereomusic.command.commands.*; | |||
| import codes.ztereohype.ztereomusic.database.Config; | ||||
| import codes.ztereohype.ztereomusic.listeners.AloneDisconnectListener; | ||||
| import codes.ztereohype.ztereomusic.listeners.CommandListener; | ||||
| import codes.ztereohype.ztereomusic.networking.SpotifyApiHelper; | ||||
| import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; | ||||
| import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; | ||||
| import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers; | ||||
|  | @ -52,7 +53,8 @@ public class ZtereoMUSIC { | |||
| 
 | ||||
|         EnumSet<GatewayIntent> intents = EnumSet.of( | ||||
|                 GatewayIntent.GUILD_MESSAGES, | ||||
|                 GatewayIntent.GUILD_VOICE_STATES | ||||
|                 GatewayIntent.GUILD_VOICE_STATES, | ||||
|                 GatewayIntent.GUILD_EMOJIS | ||||
|         ); | ||||
| 
 | ||||
|         ztereoMUSIC.setConfig(Config.loadFrom("./config.json5")); | ||||
|  | @ -102,6 +104,7 @@ public class ZtereoMUSIC { | |||
|         this.setPlayerManager(new DefaultAudioPlayerManager()); | ||||
|         AudioSourceManagers.registerRemoteSources(this.getPlayerManager()); | ||||
|         AudioSourceManagers.registerLocalSource(this.getPlayerManager()); | ||||
|         SpotifyApiHelper.startTokenTimer(); | ||||
|     } | ||||
| 
 | ||||
|     private void setListeners() { | ||||
|  |  | |||
|  | @ -7,9 +7,9 @@ import codes.ztereohype.ztereomusic.audio.TrackManagers; | |||
| import codes.ztereohype.ztereomusic.command.Command; | ||||
| import codes.ztereohype.ztereomusic.command.CommandMeta; | ||||
| import codes.ztereohype.ztereomusic.command.permissions.VoiceChecks; | ||||
| import codes.ztereohype.ztereomusic.networking.SpotifyApiHelper; | ||||
| import codes.ztereohype.ztereomusic.networking.YoutubeSearch; | ||||
| import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; | ||||
| import lombok.Getter; | ||||
| import net.dv8tion.jda.api.entities.*; | ||||
| import net.dv8tion.jda.api.events.message.MessageReceivedEvent; | ||||
| 
 | ||||
|  | @ -20,8 +20,9 @@ import java.util.regex.Pattern; | |||
| 
 | ||||
| public class Play implements Command { | ||||
|     private static final Pattern URL_PATTERN = Pattern.compile("^(http|https)://([a-z]+\\.[a-z]+)+/\\S+$", Pattern.CASE_INSENSITIVE); | ||||
|     private static final Pattern SPOTIFY_URL_PATTERN = Pattern.compile("^(?:https://open\\.spotify\\.com/(track|playlist)/)(\\S+(?:\\?si=\\S+))$"); | ||||
| 
 | ||||
|     private @Getter final CommandMeta meta; | ||||
|     private final CommandMeta meta; | ||||
| 
 | ||||
|     public Play() { | ||||
|         this.meta = CommandMeta.builder() | ||||
|  | @ -35,10 +36,10 @@ public class Play implements Command { | |||
|                                .build(); | ||||
|     } | ||||
| 
 | ||||
| //    @Override | ||||
| //    public CommandMeta getMeta() { | ||||
| //        return this.meta; | ||||
| //    } | ||||
|     @Override | ||||
|     public CommandMeta getMeta() { | ||||
|         return this.meta; | ||||
|     } | ||||
| 
 | ||||
|     public void execute(MessageReceivedEvent messageEvent, String[] args) { | ||||
|         Member author = Objects.requireNonNull(messageEvent.getMember()); | ||||
|  | @ -64,11 +65,25 @@ public class Play implements Command { | |||
| 
 | ||||
|         String mergedArgs = String.join(" ", args); | ||||
|         Matcher matchedUrls = URL_PATTERN.matcher(mergedArgs); | ||||
|         Matcher matchedSpotifyUrl = SPOTIFY_URL_PATTERN.matcher(mergedArgs); | ||||
|         boolean urlFound = matchedUrls.find(); | ||||
|         boolean spotifyUrlFound = matchedSpotifyUrl.find(); | ||||
| 
 | ||||
|         if (spotifyUrlFound) { | ||||
|             Optional<String> songSearchQuery = SpotifyApiHelper.query(mergedArgs, messageChannel); | ||||
| 
 | ||||
|             if (songSearchQuery.isPresent()) { | ||||
|                 mergedArgs = songSearchQuery.get(); | ||||
|             } else { | ||||
|                 return; // SpotifyApiHelper takes care of answering why it failed | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         String identifier; | ||||
|         if (!urlFound) { | ||||
|         // spotify urls need to be queried through youtube | ||||
|         if (!urlFound | spotifyUrlFound) { | ||||
|             Optional<String> query = YoutubeSearch.query(mergedArgs); | ||||
| 
 | ||||
|             if (query.isPresent()) { | ||||
|                 identifier = query.get(); | ||||
|             } else { | ||||
|  | @ -76,7 +91,6 @@ public class Play implements Command { | |||
|                 return; | ||||
|             } | ||||
|         } else { | ||||
|             // set identifier to the parsed url | ||||
|             identifier = mergedArgs; | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,120 @@ | |||
| package codes.ztereohype.ztereomusic.networking; | ||||
| 
 | ||||
| import codes.ztereohype.ztereomusic.ZtereoMUSIC; | ||||
| import lombok.SneakyThrows; | ||||
| import net.dv8tion.jda.api.entities.MessageChannel; | ||||
| import net.shadew.json.Json; | ||||
| import net.shadew.json.JsonPath; | ||||
| import net.shadew.json.JsonSyntaxException; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.net.URI; | ||||
| import java.net.http.HttpClient; | ||||
| import java.net.http.HttpRequest; | ||||
| import java.net.http.HttpResponse; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.Base64; | ||||
| import java.util.Optional; | ||||
| import java.util.Timer; | ||||
| import java.util.TimerTask; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| public class SpotifyApiHelper { | ||||
|     private static final String CLIENT_ID = ZtereoMUSIC.getInstance().getConfig().getPropreties().get("spotify_client_id"); | ||||
|     private static final String CLIENT_SECRET = ZtereoMUSIC.getInstance().getConfig().getPropreties().get("spotify_client_secret"); | ||||
|     private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("(?:(?<=https://open\\.spotify\\.com/track/)|(?<=https://open\\.spotify\\.com/playlist/))(\\S+(?=\\?si=\\S))"); | ||||
| 
 | ||||
|     private static String spotifyToken; | ||||
| 
 | ||||
|     private static final Json JSON = Json.json(); | ||||
| 
 | ||||
|     public static void startTokenTimer() { | ||||
|         Timer timer = new Timer(); | ||||
|         timer.scheduleAtFixedRate(new TimerTask() { | ||||
|             @Override | ||||
|             public void run() { | ||||
|                 spotifyToken = null; //remove old outdated token | ||||
|                 Optional<String> parsedToken = getToken(); | ||||
| 
 | ||||
|                 if (parsedToken.isEmpty()) { | ||||
|                     System.out.println("ERROR: Couldn't get a Spotify token. Spotify features will not work!"); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 spotifyToken = parsedToken.get(); | ||||
|             } | ||||
|         }, 0, 3599*1000); | ||||
|     } | ||||
| 
 | ||||
|     @SneakyThrows | ||||
|     private static Optional<String> getToken() { | ||||
|         HttpClient client = HttpClient.newHttpClient(); | ||||
| 
 | ||||
|         HttpRequest request = HttpRequest.newBuilder( | ||||
|                 URI.create("https://accounts.spotify.com/api/token?grant_type=client_credentials")) | ||||
|                 .POST(HttpRequest.BodyPublishers.ofString("")) | ||||
|                 .header("Authorization", "Basic " + Base64.getEncoder().encodeToString((CLIENT_ID + ":" + CLIENT_SECRET).getBytes(StandardCharsets.UTF_8.toString()))) | ||||
|                 .header("Content-Type", "application/x-www-form-urlencoded") | ||||
|                 .build(); | ||||
| 
 | ||||
|         JsonPath tokenPath = JsonPath.parse("access_token"); | ||||
|         try { | ||||
|             HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||
|             String results = JSON.parse(response.body()).query(tokenPath).asString(); | ||||
|             return Optional.ofNullable(results); | ||||
|         } catch (IOException | InterruptedException | JsonSyntaxException e) { | ||||
|             e.printStackTrace(); | ||||
|             return Optional.empty(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Optional<String> query(String songUrl, MessageChannel messageChannel) { | ||||
|         if (spotifyToken == null) { | ||||
|             System.out.println("Null Spotify token detected"); | ||||
|             messageChannel.sendMessage("I don't have a spotify token for now. Try again later.").queue(); | ||||
|             return Optional.empty(); | ||||
|         } | ||||
| 
 | ||||
|         if (songUrl.contains("/playlist/")) { | ||||
|             messageChannel.sendMessage("Playlists aren't supported for now, please send the individual song links.").queue(); | ||||
|         } | ||||
| 
 | ||||
|         Matcher matchedSpotifyIdentifier = IDENTIFIER_PATTERN.matcher(songUrl); | ||||
|         String spotifyIdentifier; | ||||
|         if (matchedSpotifyIdentifier.find()) { | ||||
|             spotifyIdentifier = matchedSpotifyIdentifier.group(); | ||||
|         } else { | ||||
|             messageChannel.sendMessage("Could not parse Spotify link. Try entering the song title directly.").queue(); | ||||
|             return Optional.empty(); | ||||
|         } | ||||
| 
 | ||||
|         String query = "https://api.spotify.com/v1/tracks?ids=" | ||||
|                 + spotifyIdentifier + "&market=ES"; //españaaaa | ||||
| 
 | ||||
|         HttpClient client = HttpClient.newHttpClient(); | ||||
| 
 | ||||
|         HttpRequest request = HttpRequest.newBuilder( | ||||
|                 URI.create(query)) | ||||
|                 .GET() | ||||
|                 .header("Authorization", "Bearer " + spotifyToken) | ||||
|                 .header("Content-Type", "application/json") | ||||
|                 .build(); | ||||
| 
 | ||||
|         JsonPath titlePath = JsonPath.parse("tracks[0].name"); | ||||
|         JsonPath authorPath = JsonPath.parse("tracks[0].artists[0].name"); | ||||
|         try { | ||||
|             HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||
| 
 | ||||
|             String title = JSON.parse(response.body()).query(titlePath).asString(); | ||||
|             String author = JSON.parse(response.body()).query(authorPath).asString(); | ||||
|             String songSearchQuery = title + " " + author; | ||||
| 
 | ||||
|             return Optional.of(songSearchQuery); | ||||
|         } catch (IOException | InterruptedException | JsonSyntaxException e) { | ||||
|             e.printStackTrace(); | ||||
|             messageChannel.sendMessage("Something wrong happened with the spotify request.").queue(); | ||||
|             return Optional.empty(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue