SpotifyRoulette/src/main/java/eric/Roullette/controller/GameController.java

196 lines
8.3 KiB
Java

package eric.Roullette.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.javalin.Javalin;
import io.javalin.http.Context;
import eric.Roullette.service.GameService;
import eric.Roullette.service.SpotifyAuthService;
import eric.Roullette.websocket.GameWebSocketHandler;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.MediaType;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class GameController {
private final GameService gameService;
private final SpotifyAuthService authService;
private final GameWebSocketHandler webSocketHandler;
private final OkHttpClient httpClient = new OkHttpClient();
private String accessToken = "";
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GameController.class);
public GameController(Javalin app, GameService gs, SpotifyAuthService sas, GameWebSocketHandler wsHandler) {
this.gameService = gs;
this.authService = sas;
this.webSocketHandler = wsHandler;
app.post("/api/create-game", this::createGame);
app.post("/api/join-game", this::joinGame);
app.get("/api/game/{gameId}/players", this::getPlayers);
app.post("/api/game/{gameId}/start-round", this::startRound);
app.post("/api/game/{gameId}/guess", this::guess);
app.post("/api/spotify/play", this::playTrack);
app.get("/api/spotify/devices", ctx -> {
String username = ctx.queryParam("username");
if (username == null) {
ctx.status(400).result("username fehlt");
return;
}
var accessToken = authService.getAccessTokenForUser(username);
if (accessToken == null) {
ctx.status(401).result("Zugriffstoken fehlt oder ist ungültig");
return;
}
setToken(accessToken);
var devices = authService.getDevices(username, accessToken);
ctx.json(devices);
});
}
private void setToken(String accessToken) {
this.accessToken = accessToken;
}
private void createGame(Context ctx) {
Map<String, Object> body = ctx.bodyAsClass(Map.class);
String user = (String) body.get("username");
if (user == null || user.isBlank()) {
ctx.status(400).result("username fehlt");
return;
}
String gameId;
do {
gameId = String.format("%04d", ThreadLocalRandom.current().nextInt(0, 10000));
} while (gameService.gameExists(gameId));
gameService.createGame(gameId);
gameService.addPlayer(gameId, user);
gameService.broadcastPlayers(gameId);
ctx.json(Map.of("status", "ok", "gameId", gameId));
}
private void joinGame(Context ctx) {
Map<String, String> body = ctx.bodyAsClass(Map.class);
String user = body.get("username");
String gameId = body.get("gameId");
if (user == null || gameId == null) {
ctx.status(400).result("username oder gameId fehlt");
return;
}
gameService.addPlayer(gameId, user);
gameService.broadcastPlayers(gameId);
ctx.json(Map.of("status", "ok"));
}
private void getPlayers(Context ctx) {
String gameId = ctx.pathParam("gameId");
var game = gameService.getOrCreateGame(gameId);
ctx.json(Map.of(
"players", game.players()
));
}
private void startRound(Context ctx) {
String gameId = ctx.pathParam("gameId");
ctx.json(Map.of("status", "ok"));
webSocketHandler.broadcastRoundStart(gameId);
}
private void guess(Context ctx) {
String gameId = ctx.pathParam("gameId");
Map<String, String> body = ctx.bodyAsClass(Map.class);
String guess = body.get("guess");
String user = body.get("username");
GameService.Game game = gameService.getOrCreateGame(gameId);
String owner = game.currentOwner();
if (owner == null || guess == null) {
ctx.status(400).result("ungültig");
return;
}
boolean correct = guess.equals(owner);
if (correct) game.scores().merge(user, 1, Integer::sum);
ctx.json(Map.of(
"correct", correct,
"owner", owner,
"scores", game.scores()
));
}
// Java
private void playTrack(Context ctx) {
Map<String, String> body = ctx.bodyAsClass(Map.class);
String username = body.get("username");
String deviceId = body.get("device_id");
String trackUri = body.get("track_uri");
if (username == null || deviceId == null || trackUri == null) {
ctx.status(400).result("Fehlende Parameter: username, device_id oder track_uri");
return;
}
try {
//String accessToken = authService.getAccessTokenForUser(username);
OkHttpClient client = httpClient;
String trackId = trackUri.split(":")[2];
Request getTrack = new Request.Builder()
.url("https://api.spotify.com/v1/tracks/" + trackId)
.addHeader("Authorization", "Bearer " + accessToken)
.build();
try (Response trackResp = client.newCall(getTrack).execute()) {
if (!trackResp.isSuccessful()) {
ctx.status(trackResp.code()).result("Fehler beim Laden der Track-Details: " + trackResp.body().string());
return;
}
var node = new com.fasterxml.jackson.databind.ObjectMapper().readTree(trackResp.body().string());
long durationMs = node.get("duration_ms").asLong();
long startOffset = durationMs / 2;
ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
jsonNode.putArray("uris").add(trackUri);
jsonNode.put("position_ms", startOffset);
String jsonBody = jsonNode.toString();
Request playReq = new Request.Builder()
.url("https://api.spotify.com/v1/me/player/play?device_id=" + deviceId)
.addHeader("Authorization", "Bearer " + accessToken)
.put(RequestBody.create(jsonBody, MediaType.parse("application/json")))
.build();
try (Response playResp = client.newCall(playReq).execute()) {
ctx.status(playResp.code());
ctx.header("Retry-After", playResp.header("Retry-After") != null ? playResp.header("Retry-After") : "");
ctx.header("x-rate-limit-limit", playResp.header("x-rate-limit-limit"));
ctx.header("x-rate-limit-remaining", playResp.header("x-rate-limit-remaining"));
ctx.header("x-rate-limit-reset", playResp.header("x-rate-limit-reset"));
ctx.result(playResp.body().string());
if (playResp.isSuccessful()) {
ctx.status(204).result("Track erfolgreich abgespielt");
} else {
ctx.status(playResp.code()).result("Fehler beim Abspielen: " + playResp.body().string());
}
}
}
} catch (Exception e) {
log.error("Fehler beim Abspielen des Tracks", e);
ctx.status(500).result("Interner Fehler: " + e.getMessage());
}
}
}