GUI #41
|
|
@ -23,6 +23,7 @@ package eric.Roullette.controller;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
// import java.util.Objects;
|
// import java.util.Objects;
|
||||||
// import java.util.UUID;
|
// import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
// import java.util.concurrent.TimeUnit;
|
// import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
@ -32,7 +33,9 @@ public class GameController {
|
||||||
private final GameWebSocketHandler webSocketHandler;
|
private final GameWebSocketHandler webSocketHandler;
|
||||||
private final OkHttpClient httpClient = new OkHttpClient();
|
private final OkHttpClient httpClient = new OkHttpClient();
|
||||||
private String accessToken = "";
|
private String accessToken = "";
|
||||||
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GameController.class);
|
private Map<String, String> userAccessTokens = new ConcurrentHashMap<>();
|
||||||
|
// Map<username, accessToken>
|
||||||
|
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GameController.class);
|
||||||
|
|
||||||
public GameController(Javalin app, GameService gs, SpotifyAuthService sas, GameWebSocketHandler wsHandler) {
|
public GameController(Javalin app, GameService gs, SpotifyAuthService sas, GameWebSocketHandler wsHandler) {
|
||||||
this.gameService = gs;
|
this.gameService = gs;
|
||||||
|
|
@ -50,21 +53,28 @@ public class GameController {
|
||||||
ctx.status(400).result("username fehlt");
|
ctx.status(400).result("username fehlt");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var accessToken = authService.getAccessTokenForUser(username);
|
// spieler in map hinzufügen
|
||||||
|
|
||||||
|
String playerAccessToken = null;
|
||||||
|
if(!userAccessTokens.containsKey(username)) {
|
||||||
|
playerAccessToken = authService.getAccessTokenForUser(username);
|
||||||
|
if (playerAccessToken != null) {
|
||||||
|
userAccessTokens.put(username, playerAccessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (accessToken == null) {
|
if (accessToken == null) {
|
||||||
ctx.status(401).result("Zugriffstoken fehlt oder ist ungültig");
|
ctx.status(401).result("Zugriffstoken fehlt oder ist ungültig");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setToken(accessToken);
|
|
||||||
var devices = authService.getDevices(accessToken);
|
var devices = authService.getDevices(accessToken);
|
||||||
ctx.json(devices);
|
ctx.json(devices);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setToken(String accessToken) {
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
}
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void createGame(Context ctx) throws InterruptedException {
|
private void createGame(Context ctx) throws InterruptedException {
|
||||||
Map<String, Object> body = (Map<String, Object>) ctx.bodyAsClass(Map.class);
|
Map<String, Object> body = (Map<String, Object>) ctx.bodyAsClass(Map.class);
|
||||||
|
|
@ -144,6 +154,7 @@ public class GameController {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//String accessToken = authService.getAccessTokenForUser(username);
|
//String accessToken = authService.getAccessTokenForUser(username);
|
||||||
|
String accessToken = userAccessTokens.get(username);
|
||||||
OkHttpClient client = httpClient;
|
OkHttpClient client = httpClient;
|
||||||
String trackId = trackUri.split(":")[2];
|
String trackId = trackUri.split(":")[2];
|
||||||
Request getTrack = new Request.Builder()
|
Request getTrack = new Request.Builder()
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import java.net.URI;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static com.neovisionaries.i18n.CountryCode.DE;
|
//import static com.neovisionaries.i18n.CountryCode.DE;
|
||||||
|
|
||||||
public class SpotifyAuthService {
|
public class SpotifyAuthService {
|
||||||
private final String clientId;
|
private final String clientId;
|
||||||
|
|
@ -38,18 +38,27 @@ public class SpotifyAuthService {
|
||||||
public URI getAuthorizationUri(String user) {
|
public URI getAuthorizationUri(String user) {
|
||||||
System.out.println("Erstelle Auth-URL für Benutzer: " + user);
|
System.out.println("Erstelle Auth-URL für Benutzer: " + user);
|
||||||
// Temporäre API-Instanz nur für die Erstellung der Auth-URL
|
// Temporäre API-Instanz nur für die Erstellung der Auth-URL
|
||||||
|
String scope = "user-read-recently-played user-library-read user-modify-playback-state user-read-playback-state streaming user-read-private";
|
||||||
|
//
|
||||||
|
|
||||||
SpotifyApi tempApi = new SpotifyApi.Builder()
|
SpotifyApi tempApi = new SpotifyApi.Builder()
|
||||||
.setClientId(clientId)
|
.setClientId(clientId)
|
||||||
.setClientSecret(clientSecret)
|
.setClientSecret(clientSecret)
|
||||||
.setRedirectUri(redirectUri)
|
.setRedirectUri(redirectUri)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
//überprüfe, ob der nutzer ein Premium-User ist
|
||||||
return tempApi.authorizationCodeUri()
|
return tempApi.authorizationCodeUri()
|
||||||
.scope("user-read-recently-played user-library-read user-read-playback-state user-modify-playback-state streaming")
|
.scope(scope)
|
||||||
.state(user) // Der Benutzername wird im State mitgegeben
|
.state(user) // Der Benutzername wird im State mitgegeben
|
||||||
|
.show_dialog(true)
|
||||||
.build()
|
.build()
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
// public boolean isPremiumUser(SpotifyApi api) throws IOException, SpotifyWebApiException, ParseException {
|
||||||
|
// var user = api.getCurrentUsersProfile().build().execute();
|
||||||
|
// return "premium".equalsIgnoreCase(String.valueOf(user.getProduct()));
|
||||||
|
// }
|
||||||
|
|
||||||
public void exchangeCode(String code, String user) throws IOException, ParseException, SpotifyWebApiException {
|
public void exchangeCode(String code, String user) throws IOException, ParseException, SpotifyWebApiException {
|
||||||
// Erstellt eine neue, dedizierte API-Instanz für diesen Benutzer
|
// Erstellt eine neue, dedizierte API-Instanz für diesen Benutzer
|
||||||
|
|
@ -63,6 +72,8 @@ public class SpotifyAuthService {
|
||||||
AuthorizationCodeCredentials creds = userApi.authorizationCode(code).build().execute();
|
AuthorizationCodeCredentials creds = userApi.authorizationCode(code).build().execute();
|
||||||
userApi.setAccessToken(creds.getAccessToken());
|
userApi.setAccessToken(creds.getAccessToken());
|
||||||
userApi.setRefreshToken(creds.getRefreshToken());
|
userApi.setRefreshToken(creds.getRefreshToken());
|
||||||
|
System.out.println("Granted scopes: " + creds.getScope()); // wichtig fürs Debugging
|
||||||
|
|
||||||
|
|
||||||
// Speichert die fertig konfigurierte API-Instanz für den Benutzer
|
// Speichert die fertig konfigurierte API-Instanz für den Benutzer
|
||||||
userApis.put(user, userApi);
|
userApis.put(user, userApi);
|
||||||
|
|
@ -70,26 +81,57 @@ public class SpotifyAuthService {
|
||||||
|
|
||||||
public List<String> getRecentTracks(String user) {
|
public List<String> getRecentTracks(String user) {
|
||||||
System.out.println("Hole kürzlich gespielte Tracks für Benutzer: " + user);
|
System.out.println("Hole kürzlich gespielte Tracks für Benutzer: " + user);
|
||||||
int limit = 2;
|
int limit = 50;
|
||||||
SpotifyApi userApi = userApis.get(user);
|
SpotifyApi userApi = userApis.get(user);
|
||||||
|
|
||||||
if (userApi == null) {
|
if (userApi == null) {
|
||||||
System.err.println("Kein SpotifyApi-Client für Benutzer gefunden: " + user);
|
System.out.println("Kein SpotifyApi-Client für Benutzer gefunden: " + user);
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
System.out.println("SpotifyApi-Client für Benutzer gefunden: " + user);
|
||||||
|
System.out.println("AccessToken: " + userApi.getAccessToken());
|
||||||
|
System.out.println("RefreshToken: " + userApi.getRefreshToken());
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
System.out.println("Hole Profil für Benutzer: " + user);
|
||||||
|
// Hole das Profil des Benutzers, um den Account-Typ zu überprüfen
|
||||||
|
var profile = userApi.getCurrentUsersProfile().build().execute();
|
||||||
|
System.out.println("Account-Typ: " + profile.getProduct());
|
||||||
|
|
||||||
|
System.out.println("Erstelle Anfrage für kürzlich gespielte Tracks...");
|
||||||
|
if (userApi.getRefreshToken() == null) {
|
||||||
|
System.out.println("Refresh Token für Benutzer " + user + " ist nicht gesetzt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Refresh Token für Benutzer " + user + " ist gesetzt.");
|
||||||
|
if( userApi.getAccessToken() == null) {
|
||||||
|
System.out.println("Access Token für Benutzer " + user + " ist nicht gesetzt.");
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Access Token für Benutzer " + user + " ist gesetzt.");
|
||||||
|
|
||||||
|
|
||||||
GetCurrentUsersRecentlyPlayedTracksRequest request = userApi.getCurrentUsersRecentlyPlayedTracks()
|
GetCurrentUsersRecentlyPlayedTracksRequest request = userApi.getCurrentUsersRecentlyPlayedTracks()
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.build();
|
.build();
|
||||||
|
// Führe die Anfrage aus und erhalte die Ergebnisse
|
||||||
|
System.out.println("Führe paging Anfrage aus...");
|
||||||
PagingCursorbased<PlayHistory> history = request.execute();
|
PagingCursorbased<PlayHistory> history = request.execute();
|
||||||
|
System.out.println("Paging Anfrage erfolgreich ausgeführt.");
|
||||||
|
// Überprüfe, ob die Ergebnisse leer sind
|
||||||
|
System.out.println("Überprüfe, ob Ergebnisse vorhanden sind...");
|
||||||
if (history == null || history.getItems() == null) {
|
if (history == null || history.getItems() == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
System.out.println("Verarbeite kürzlich gespielte Tracks...");
|
||||||
|
// Extrahiere die URIs der kürzlich gespielten Tracks
|
||||||
List<String> recentTracks = Arrays.stream(history.getItems())
|
List<String> recentTracks = Arrays.stream(history.getItems())
|
||||||
.map(item -> item.getTrack().getUri())
|
.map(item -> item.getTrack().getUri())
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
|
System.out.println("Gefundene kürzlich gespielte Tracks: " + recentTracks.size());
|
||||||
|
|
||||||
if (recentTracks.size() < limit) {
|
if (recentTracks.size() < limit) {
|
||||||
int newLimit = limit - recentTracks.size();
|
int newLimit = limit - recentTracks.size();
|
||||||
|
|
@ -106,7 +148,14 @@ public class SpotifyAuthService {
|
||||||
recentTracks.addAll(savedTracks2.subList(0, Math.min(newLimit, savedTracks2.size())));
|
recentTracks.addAll(savedTracks2.subList(0, Math.min(newLimit, savedTracks2.size())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.out.println("Endgültige Anzahl kürzlich gespielter Tracks: " + recentTracks.size());
|
||||||
return recentTracks.subList(0, Math.min(limit, recentTracks.size()));
|
return recentTracks.subList(0, Math.min(limit, recentTracks.size()));
|
||||||
|
// } catch (se.michaelthelin.spotify.exceptions.detailed.ForbiddenException e) {
|
||||||
|
// System.out.println("ForbiddenException: " + e.getMessage());
|
||||||
|
// System.out.println("Möglicherweise hat der Benutzer keine Berechtigung, kürzlich gespielte Tracks abzurufen.");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// return Collections.emptyList();
|
||||||
} catch (IOException | SpotifyWebApiException | ParseException e) {
|
} catch (IOException | SpotifyWebApiException | ParseException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
@ -197,6 +246,7 @@ public class SpotifyAuthService {
|
||||||
.build();
|
.build();
|
||||||
try (Response resp = client.newCall(req).execute()) {
|
try (Response resp = client.newCall(req).execute()) {
|
||||||
if (!resp.isSuccessful()) return List.of();
|
if (!resp.isSuccessful()) return List.of();
|
||||||
|
assert resp.body() != null;
|
||||||
String body = resp.body().string();
|
String body = resp.body().string();
|
||||||
// Parsen, z.B. mit Jackson
|
// Parsen, z.B. mit Jackson
|
||||||
var node = new ObjectMapper().readTree(body);
|
var node = new ObjectMapper().readTree(body);
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ public class GameWebSocketHandler {
|
||||||
// Spiel-ID → (Username → deren Guess)
|
// Spiel-ID → (Username → deren Guess)
|
||||||
private final Map<String, Map<String, String>> currentGuesses = new ConcurrentHashMap<>();
|
private final Map<String, Map<String, String>> currentGuesses = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Map<String, List<String>> trackInfoCache = new ConcurrentHashMap<>();
|
// private final Map<String, List<String>> trackInfoCache = new ConcurrentHashMap<>();
|
||||||
private final Map<String, List<String>> allTracksCache = new ConcurrentHashMap<>();
|
// private final Map<String, List<String>> allTracksCache = new ConcurrentHashMap<>();
|
||||||
//Map<gameId, Map<player, List<String>>
|
//Map<gameId, Map<player, List<String>>
|
||||||
private Map<String, Map<String, List<String>>> playerTracksCache = new ConcurrentHashMap<>();
|
//private Map<String, Map<String, List<String>>> playerTracksCache = new ConcurrentHashMap<>();
|
||||||
private Map<String, Map<String, List<String>>> playerTrackInfoCache = new ConcurrentHashMap<>();
|
private Map<String, Map<String, List<String>>> playerTrackInfoCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public GameWebSocketHandler(GameService gameService) {
|
public GameWebSocketHandler(GameService gameService) {
|
||||||
this.service = gameService;
|
this.service = gameService;
|
||||||
|
|
@ -77,9 +77,7 @@ public class GameWebSocketHandler {
|
||||||
}
|
}
|
||||||
case "requestPlayers" -> service.broadcastPlayers(gameId);
|
case "requestPlayers" -> service.broadcastPlayers(gameId);
|
||||||
|
|
||||||
case "next-round" -> {
|
case "next-round" -> nextround(gameId);
|
||||||
nextround(gameId);
|
|
||||||
}
|
|
||||||
case "start-round" -> {
|
case "start-round" -> {
|
||||||
var currentGame = service.getOrCreateGame(gameId);
|
var currentGame = service.getOrCreateGame(gameId);
|
||||||
if (currentGame.players().isEmpty()) return;
|
if (currentGame.players().isEmpty()) return;
|
||||||
|
|
@ -94,10 +92,10 @@ public class GameWebSocketHandler {
|
||||||
service.startRound(gameId, allTracks);
|
service.startRound(gameId, allTracks);
|
||||||
}
|
}
|
||||||
// Trackinfos für alle Spieler sammeln
|
// Trackinfos für alle Spieler sammeln
|
||||||
Map<String, List<String>> allTrackInfos = service.getTrackInfos(allPlayerTracks);
|
//Map<String, List<String>> allTrackInfos = service.getTrackInfos(allPlayerTracks);
|
||||||
// Cache für Trackinfos pro Spiel-ID
|
// Cache für Trackinfos pro Spiel-ID
|
||||||
playerTrackInfoCache.put(gameId, allTrackInfos);
|
//playerTrackInfoCache.put(gameId, allTrackInfos);
|
||||||
System.out.println("TrackInfosCache für Spiel " + gameId + " hat " + allTrackInfos.size() + " Spieler (rundenstart)");
|
//System.out.println("TrackInfosCache für Spiel " + gameId + " hat " + allTrackInfos.size() + " Spieler (rundenstart)");
|
||||||
broadcastRoundStart(gameId);
|
broadcastRoundStart(gameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +166,7 @@ private void broadcastRoundResult(String gameId) {
|
||||||
|
|
||||||
// Prüfe auf Gewinner
|
// Prüfe auf Gewinner
|
||||||
String winner = scores.entrySet().stream()
|
String winner = scores.entrySet().stream()
|
||||||
.filter(e -> e.getValue() >= 30)
|
.filter(e -> e.getValue() >= 6)
|
||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|
@ -182,14 +180,15 @@ private void broadcastRoundResult(String gameId) {
|
||||||
broadcastToAll(gameId, winMsg);
|
broadcastToAll(gameId, winMsg);
|
||||||
game.scores().replaceAll((user , pts) -> 0); // Reset Scores für alle Spieler
|
game.scores().replaceAll((user , pts) -> 0); // Reset Scores für alle Spieler
|
||||||
|
|
||||||
}else{
|
|
||||||
// nächste Runde starten
|
|
||||||
// ...
|
|
||||||
// new Thread(() -> {
|
|
||||||
// try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
|
|
||||||
// nextround(gameId);
|
|
||||||
// }).start();
|
|
||||||
}
|
}
|
||||||
|
// else{
|
||||||
|
// // nächste Runde starten
|
||||||
|
// // ...
|
||||||
|
//// new Thread(() -> {
|
||||||
|
//// try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
|
||||||
|
//// nextround(gameId);
|
||||||
|
//// }).start();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hilfsmethode: Sendet eine Nachricht an alle WebSocket-Sessions eines Spiels. */
|
/** Hilfsmethode: Sendet eine Nachricht an alle WebSocket-Sessions eines Spiels. */
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,6 @@
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<div class="card-hd">
|
<div class="card-hd">
|
||||||
<h2 style="margin:0">Teilnehmer</h2>
|
<h2 style="margin:0">Teilnehmer</h2>
|
||||||
<button id="refreshPlayers" class="btn btn-ghost">Aktualisieren</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-bd">
|
<div class="card-bd">
|
||||||
<ul id="playersList"></ul>
|
<ul id="playersList"></ul>
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,10 @@ function handleRoundResult({ scores, guesses, owner }) {
|
||||||
|
|
||||||
// 8) Spielende -> Start-Button zurück
|
// 8) Spielende -> Start-Button zurück
|
||||||
function handleGameEnd({ winner }) {
|
function handleGameEnd({ winner }) {
|
||||||
|
const nextBtn = document.getElementById("nextRound");
|
||||||
resultP.textContent = `🎉 ${winner} hat gewonnen!`;
|
resultP.textContent = `🎉 ${winner} hat gewonnen!`;
|
||||||
|
nextBtn.hidden = true;
|
||||||
|
nextBtn.disabled = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
startBtn.hidden = false;
|
startBtn.hidden = false;
|
||||||
startBtn.disabled = false;
|
startBtn.disabled = false;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue