package cc.fascinated.bat.service; import cc.fascinated.bat.common.StringUtils; import cc.fascinated.bat.features.spotify.profile.SpotifyProfile; import cc.fascinated.bat.model.BatUser; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; import net.jodah.expiringmap.ExpiringMap; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import se.michaelthelin.spotify.SpotifyApi; import se.michaelthelin.spotify.enums.AuthorizationScope; import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; import se.michaelthelin.spotify.model_objects.miscellaneous.CurrentlyPlaying; import java.net.URI; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @author Fascinated (fascinated7) */ @Service @Getter public class SpotifyService { private static final String REDIRECT_URI = "http://localhost:8080/spotify/callback"; /** * The access token map. */ private final Map accessToken = ExpiringMap.builder() .expiration(30, TimeUnit.MINUTES) .build(); /** * The client ID. */ private final String clientId; /** * The client secret. */ private final String clientSecret; /** * The Spotify API instance. */ private final SpotifyApi spotifyApi; /** * The user service. */ private final UserService userService; /** * The authorization URL. */ private final String authorizationUrl; public SpotifyService(@Value("${spotify.client-id}") String clientId, @Value("${spotify.client-secret}") String clientSecret, @NonNull UserService userService) { this.clientId = clientId; this.clientSecret = clientSecret; this.userService = userService; this.spotifyApi = new SpotifyApi.Builder() .setClientId(clientId) .setClientSecret(clientSecret) .setRedirectUri(URI.create(REDIRECT_URI)) .build(); this.authorizationUrl = spotifyApi.authorizationCodeUri() .response_type("code") .scope( AuthorizationScope.APP_REMOTE_CONTROL, AuthorizationScope.USER_READ_PLAYBACK_POSITION, AuthorizationScope.USER_READ_PLAYBACK_STATE, AuthorizationScope.USER_MODIFY_PLAYBACK_STATE, AuthorizationScope.USER_READ_CURRENTLY_PLAYING, AuthorizationScope.APP_REMOTE_CONTROL, AuthorizationScope.STREAMING ) .build().execute().toString(); } /** * Gets the currently playing track for the user. * * @param user the user to check * @return the currently playing track */ public CurrentlyPlaying getCurrentlyPlayingTrack(BatUser user) { try { return getSpotifyApi(user).getUsersCurrentlyPlayingTrack().build().execute(); } catch (Exception e) { return null; } } /** * Checks if the user has a track playing. * * @param user the user to check * @return whether a track is playing */ public boolean hasTrackPlaying(BatUser user) { return getCurrentlyPlayingTrack(user) != null; } /** * Pauses playback for the user. * * @param user the user to start playback for * @return if the playback was paused */ @SneakyThrows public boolean pausePlayback(BatUser user) { try { getSpotifyApi(user).pauseUsersPlayback().build().execute(); return true; } catch (Exception e) { return false; } } /** * Pauses playback for the user. * * @param user the user to start playback for * @return if the playback was paused */ @SneakyThrows public boolean resumePlayback(BatUser user) { try { getSpotifyApi(user).startResumeUsersPlayback().build().execute(); return true; } catch (Exception e) { return false; } } /** * Gets the authorization key to link the user's * Spotify account with their Discord account. * * @param code the code to authorize with * @return the authorization details */ @SneakyThrows public String authorize(String code) { AuthorizationCodeCredentials credentials = spotifyApi.authorizationCode(code).build().execute(); String key = StringUtils.randomString(16); accessToken.put(key, credentials); return "Authorization key: " + key; } /** * Links the user's Spotify account with their Discord account. * * @param user the user to link the account with * @param key the key to link the account with */ public void linkAccount(BatUser user, String key) { AuthorizationCodeCredentials credentials = accessToken.get(key); if (credentials == null) { return; } // Link the user's Spotify account SpotifyProfile profile = user.getProfile(SpotifyProfile.class); profile.setAccessToken(credentials.getAccessToken()); profile.setRefreshToken(credentials.getRefreshToken()); userService.saveUser(user); } /** * Gets a new Spotify API instance. * * @return the Spotify API */ public SpotifyApi getSpotifyApi(BatUser user) { SpotifyProfile profile = user.getProfile(SpotifyProfile.class); return new SpotifyApi.Builder() .setAccessToken(profile.getAccessToken()) .setClientSecret(clientSecret) .build(); } }