package eric.Roullette.service; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.neovisionaries.i18n.CountryCode; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.apache.hc.core5.http.ParseException; import se.michaelthelin.spotify.SpotifyApi; import se.michaelthelin.spotify.SpotifyHttpManager; import se.michaelthelin.spotify.exceptions.SpotifyWebApiException; import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; import se.michaelthelin.spotify.model_objects.specification.*; import se.michaelthelin.spotify.requests.data.player.GetCurrentUsersRecentlyPlayedTracksRequest; import se.michaelthelin.spotify.requests.data.library.GetUsersSavedTracksRequest; import java.io.IOException; import java.net.URI; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static com.neovisionaries.i18n.CountryCode.DE; public class SpotifyAuthService { private final String clientId; private final String clientSecret; private final URI redirectUri; // Speichert für jeden Benutzer eine eigene, authentifizierte SpotifyApi-Instanz private final Map userApis = new ConcurrentHashMap<>(); public SpotifyAuthService(String clientId, String clientSecret, String redirectUri) { this.clientId = clientId; this.clientSecret = clientSecret; this.redirectUri = SpotifyHttpManager.makeUri(redirectUri); } public URI getAuthorizationUri(String user) { // Temporäre API-Instanz nur für die Erstellung der Auth-URL SpotifyApi tempApi = new SpotifyApi.Builder() .setClientId(clientId) .setClientSecret(clientSecret) .setRedirectUri(redirectUri) .build(); return tempApi.authorizationCodeUri() .scope("user-read-recently-played user-library-read user-read-playback-state user-modify-playback-state streaming") .state(user) // Der Benutzername wird im State mitgegeben .build() .execute(); } public void exchangeCode(String code, String user) throws IOException, ParseException, SpotifyWebApiException { // Erstellt eine neue, dedizierte API-Instanz für diesen Benutzer SpotifyApi userApi = new SpotifyApi.Builder() .setClientId(clientId) .setClientSecret(clientSecret) .setRedirectUri(redirectUri) .build(); // Tauscht den Code gegen Tokens und konfiguriert die Instanz AuthorizationCodeCredentials creds = userApi.authorizationCode(code).build().execute(); userApi.setAccessToken(creds.getAccessToken()); userApi.setRefreshToken(creds.getRefreshToken()); // Speichert die fertig konfigurierte API-Instanz für den Benutzer userApis.put(user, userApi); } public List getRecentTracks(String user) { int limit = 50; SpotifyApi userApi = userApis.get(user); if (userApi == null) { System.err.println("Kein SpotifyApi-Client für Benutzer gefunden: " + user); return Collections.emptyList(); } try { GetCurrentUsersRecentlyPlayedTracksRequest request = userApi.getCurrentUsersRecentlyPlayedTracks() .limit(limit) .build(); PagingCursorbased history = request.execute(); if (history == null || history.getItems() == null) { return Collections.emptyList(); } List recentTracks = Arrays.stream(history.getItems()) .map(item -> item.getTrack().getUri()) .distinct() .toList(); if (recentTracks.size() < limit) { int newLimit = limit - recentTracks.size(); // restliche songs mit kürzlich gespeicherten Tracks auffüllen List savedTracks = getSavedTracks(user, newLimit, 0); // Nur Tracks hinzufügen, die noch nicht in recentTracks sind savedTracks.removeAll(recentTracks); recentTracks = new java.util.ArrayList<>(recentTracks); recentTracks.addAll(savedTracks.subList(0, Math.min(newLimit, savedTracks.size()))); if(recentTracks.size() < limit){ newLimit = limit - recentTracks.size(); List savedTracks2 = getSavedTracks(user, newLimit, 50); savedTracks2.removeAll(recentTracks); recentTracks.addAll(savedTracks2.subList(0, Math.min(newLimit, savedTracks2.size()))); } } return recentTracks.subList(0, Math.min(limit, recentTracks.size())); } catch (IOException | SpotifyWebApiException | ParseException e) { e.printStackTrace(); return Collections.emptyList(); } } private List getSavedTracks(String user, int limit, int offset) { SpotifyApi userApi = userApis.get(user); if (userApi == null) { System.err.println("Kein SpotifyApi-Client für Benutzer gefunden: " + user); return Collections.emptyList(); } try { List saved = new ArrayList<>(); while (saved.size() < limit) { GetUsersSavedTracksRequest req = userApi.getUsersSavedTracks() .limit(limit) .offset(offset) .market(CountryCode.DE) .build(); Paging page = req.execute(); if (page == null || page.getItems().length == 0){ System.out.println("Keine weiteren gespeicherten Tracks gefunden."); break; } for (SavedTrack st : page.getItems()) { saved.add(st.getTrack().getUri()); if (saved.size() == limit) break; } offset += limit; } return saved; } catch (IOException | SpotifyWebApiException | ParseException e) { e.printStackTrace(); return Collections.emptyList(); } } public List getTrackInfos(List allTracks) { //für jede URI den titel holen List trackInfos = new ArrayList<>(); for (String uri : allTracks) { SpotifyApi userApi = userApis.values().stream().findFirst().orElse(null); if (userApi == null) { System.err.println("Kein SpotifyApi-Client gefunden."); return Collections.emptyList(); } try { String trackId = uri.startsWith("spotify:track:") ? uri.substring("spotify:track:".length()) : uri; var track = userApi.getTrack(trackId) .build() .execute(); if (track != null) { String info = track.getName() + " - " + Arrays.stream(track.getArtists()) .map(ArtistSimplified::getName) .reduce((a, b) -> a + ", " + b) .orElse("Unbekannt"); trackInfos.add(info); } else { System.err.println("Track nicht gefunden: " + uri); } } catch (IOException | SpotifyWebApiException | ParseException e) { System.err.println("Fehler beim Abrufen des Tracks: " + uri); e.printStackTrace(); } } return trackInfos; } public String getAccessTokenForUser(String username) { SpotifyApi userApi = userApis.get(username); if (userApi == null) { System.err.println("Kein SpotifyApi-Client für Benutzer gefunden: " + username); return null; } return userApi.getAccessToken(); } public List> getDevices(String username) { String accessToken = getAccessTokenForUser(username); OkHttpClient client = new OkHttpClient(); Request req = new Request.Builder() .url("https://api.spotify.com/v1/me/player/devices") .addHeader("Authorization", "Bearer " + accessToken) .build(); try (Response resp = client.newCall(req).execute()) { if (!resp.isSuccessful()) return List.of(); String body = resp.body().string(); // Parsen, z.B. mit Jackson var node = new ObjectMapper().readTree(body); var devices = node.get("devices"); return new ObjectMapper().convertValue(devices, new TypeReference>>(){}); } catch (Exception e) { return List.of(); } } }