remove SLOP

This commit is contained in:
Lukas Weger 2025-08-01 21:08:31 +02:00
parent 9c373c2672
commit 50bb9d98fb
12 changed files with 0 additions and 783 deletions

View file

@ -3,17 +3,14 @@ package com.serena.backend.controller;
import com.serena.backend.dto.ApiResponse;
import com.serena.backend.dto.ConnectClientRequest;
import com.serena.backend.model.Client;
import com.serena.backend.model.RadioStation;
import com.serena.backend.service.RadioStationService;
import com.serena.backend.service.JwtService;
import com.serena.backend.util.AuthUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -62,90 +59,4 @@ public class ClientController {
.body(ApiResponse.error("Failed to connect to radio station. Invalid join code or station not found."));
}
}
@DeleteMapping("/{clientId}/disconnect")
public ResponseEntity<ApiResponse<Void>> disconnectClient(@PathVariable String clientId) {
String currentUserId = AuthUtil.getCurrentUserId();
// Check if user is authenticated
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
// Get client to check authorization
Optional<Client> clientOpt = radioStationService.getClient(clientId);
if (clientOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Client not found"));
}
Client client = clientOpt.get();
// Get the station to check if current user is the owner
Optional<RadioStation> stationOpt = radioStationService.getRadioStation(client.getRadioStationId());
if (stationOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Radio station not found"));
}
RadioStation station = stationOpt.get();
// Allow disconnection if:
// 1. Current user is the station owner (can disconnect anyone)
// 2. Current user is the client themselves (self-disconnect)
boolean isOwner = currentUserId.equals(station.getOwnerId());
// Note: For self-disconnect, we'd need to link clients to users, which isn't
// implemented yet
// For now, only station owners can disconnect clients
if (!isOwner) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(ApiResponse.error("Only the station owner can disconnect clients"));
}
boolean disconnected = radioStationService.disconnectClient(clientId);
if (disconnected) {
return ResponseEntity.ok(ApiResponse.success("Client disconnected successfully", null));
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error("Failed to disconnect client"));
}
}
@GetMapping("/{clientId}")
public ResponseEntity<ApiResponse<Client>> getClient(@PathVariable String clientId) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
Optional<Client> client = radioStationService.getClient(clientId);
if (client.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Client not found"));
}
return ResponseEntity.ok(ApiResponse.success(client.get()));
}
@GetMapping("/station/{radioStationId}")
public ResponseEntity<ApiResponse<List<Client>>> getConnectedClients(@PathVariable String radioStationId) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
// Check if station exists
Optional<RadioStation> stationOpt = radioStationService.getRadioStation(radioStationId);
if (stationOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Radio station not found"));
}
List<Client> clients = radioStationService.getConnectedClients(radioStationId);
return ResponseEntity.ok(ApiResponse.success(clients));
}
}

View file

@ -2,7 +2,6 @@ package com.serena.backend.controller;
import com.serena.backend.dto.ApiResponse;
import com.serena.backend.dto.CreateRadioStationRequest;
import com.serena.backend.dto.UpdateRadioStationRequest;
import com.serena.backend.model.RadioStation;
import com.serena.backend.service.RadioStationService;
import com.serena.backend.service.JwtService;
@ -86,57 +85,6 @@ public class RadioStationController {
return ResponseEntity.ok(ApiResponse.success(station.get()));
}
@GetMapping("/join/{joinCode}")
public ResponseEntity<ApiResponse<RadioStation>> getRadioStationByJoinCode(@PathVariable String joinCode) {
// This endpoint is public - no authentication required
Optional<RadioStation> station = radioStationService.getRadioStationByJoinCode(joinCode);
if (station.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Radio station not found with join code"));
}
return ResponseEntity.ok(ApiResponse.success(station.get()));
}
@PutMapping("/{stationId}")
public ResponseEntity<ApiResponse<RadioStation>> updateRadioStation(
@PathVariable String stationId,
@RequestBody UpdateRadioStationRequest request) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
// Check if station exists
Optional<RadioStation> stationOpt = radioStationService.getRadioStation(stationId);
if (stationOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Radio station not found"));
}
RadioStation station = stationOpt.get();
// Check if current user is the owner
if (!currentUserId.equals(station.getOwnerId())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(ApiResponse.error("Only the station owner can update the radio station"));
}
Optional<RadioStation> updated = radioStationService.updateRadioStation(
stationId,
request.getName(),
request.getDescription());
if (updated.isPresent()) {
return ResponseEntity.ok(ApiResponse.success("Radio station updated successfully", updated.get()));
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error("Failed to update radio station"));
}
}
@DeleteMapping("/{stationId}")
public ResponseEntity<ApiResponse<Void>> deleteRadioStation(@PathVariable String stationId) {
String currentUserId = AuthUtil.getCurrentUserId();
@ -145,7 +93,6 @@ public class RadioStationController {
.body(ApiResponse.error("User not authenticated"));
}
// Check if station exists
Optional<RadioStation> stationOpt = radioStationService.getRadioStation(stationId);
if (stationOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
@ -154,7 +101,6 @@ public class RadioStationController {
RadioStation station = stationOpt.get();
// Check if current user is the owner
if (!currentUserId.equals(station.getOwnerId())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(ApiResponse.error("Only the station owner can delete the radio station"));

View file

@ -1,168 +0,0 @@
package com.serena.backend.controller;
import com.serena.backend.dto.AddSongRequest;
import com.serena.backend.dto.ApiResponse;
import com.serena.backend.dto.VoteRequest;
import com.serena.backend.model.RadioStation;
import com.serena.backend.model.Song;
import com.serena.backend.service.RadioStationService;
import com.serena.backend.util.AuthUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/radio-stations/{stationId}/songs")
@CrossOrigin(origins = "*")
public class SongController {
@Autowired
private RadioStationService radioStationService;
@PostMapping
public ResponseEntity<ApiResponse<Song>> addSongToQueue(
@PathVariable String stationId,
@RequestBody AddSongRequest request) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
// Check if station exists
Optional<RadioStation> stationOpt = radioStationService.getRadioStation(stationId);
if (stationOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Radio station not found"));
}
// Use the authenticated user as the one adding the song
Optional<Song> song = radioStationService.addSongToQueue(
stationId,
request.getTitle(),
request.getArtist(),
request.getAlbum(),
request.getDuration(),
request.getUrl(),
currentUserId);
if (song.isPresent()) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success("Song added to queue successfully", song.get()));
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error("Failed to add song to queue"));
}
}
@GetMapping("/queue")
public ResponseEntity<ApiResponse<List<Song>>> getSongQueue(@PathVariable String stationId) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
List<Song> queue = radioStationService.getSongQueue(stationId);
return ResponseEntity.ok(ApiResponse.success(queue));
}
@GetMapping("/current")
public ResponseEntity<ApiResponse<Song>> getCurrentlyPlaying(@PathVariable String stationId) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
Optional<Song> current = radioStationService.getCurrentlyPlaying(stationId);
if (current.isPresent()) {
return ResponseEntity.ok(ApiResponse.success(current.get()));
} else {
return ResponseEntity.ok(ApiResponse.success("No song currently playing", null));
}
}
@PostMapping("/next")
public ResponseEntity<ApiResponse<Song>> playNextSong(@PathVariable String stationId) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
// Check if station exists
Optional<RadioStation> stationOpt = radioStationService.getRadioStation(stationId);
if (stationOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error("Radio station not found"));
}
RadioStation station = stationOpt.get();
// Only station owner can control playback
if (!currentUserId.equals(station.getOwnerId())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(ApiResponse.error("Only the station owner can control playback"));
}
Optional<Song> nextSong = radioStationService.playNextSong(stationId);
if (nextSong.isPresent()) {
return ResponseEntity.ok(ApiResponse.success("Playing next song", nextSong.get()));
} else {
return ResponseEntity.ok(ApiResponse.success("No songs in queue", null));
}
}
@PostMapping("/{songId}/vote")
public ResponseEntity<ApiResponse<Song>> voteSong(
@PathVariable String stationId,
@PathVariable String songId,
@RequestBody VoteRequest request) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
// Use the authenticated user as the one voting
Optional<Song> song = radioStationService.voteSong(
stationId,
songId,
currentUserId,
request.getVoteType());
if (song.isPresent()) {
return ResponseEntity.ok(ApiResponse.success("Vote recorded successfully", song.get()));
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error("Failed to record vote"));
}
}
@DeleteMapping("/{songId}/vote")
public ResponseEntity<ApiResponse<Void>> removeSongVote(
@PathVariable String stationId,
@PathVariable String songId) {
String currentUserId = AuthUtil.getCurrentUserId();
if (currentUserId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error("User not authenticated"));
}
boolean removed = radioStationService.removeSongVote(stationId, songId, currentUserId);
if (removed) {
return ResponseEntity.ok(ApiResponse.success("Vote removed successfully", null));
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error("Failed to remove vote"));
}
}
}

View file

@ -1,70 +0,0 @@
package com.serena.backend.dto;
public class AddSongRequest {
private String title;
private String artist;
private String album;
private int duration;
private String url;
private String addedBy;
public AddSongRequest() {
}
public AddSongRequest(String title, String artist, String album, int duration, String url, String addedBy) {
this.title = title;
this.artist = artist;
this.album = album;
this.duration = duration;
this.url = url;
this.addedBy = addedBy;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getAlbum() {
return album;
}
public void setAlbum(String album) {
this.album = album;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAddedBy() {
return addedBy;
}
public void setAddedBy(String addedBy) {
this.addedBy = addedBy;
}
}

View file

@ -1,30 +0,0 @@
package com.serena.backend.dto;
public class LoginRequest {
private String username;
private String password;
public LoginRequest() {
}
public LoginRequest(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View file

@ -1,40 +0,0 @@
package com.serena.backend.dto;
public class RegisterRequest {
private String username;
private String password;
private String email;
public RegisterRequest() {
}
public RegisterRequest(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View file

@ -1,30 +0,0 @@
package com.serena.backend.dto;
public class UpdateRadioStationRequest {
private String name;
private String description;
public UpdateRadioStationRequest() {
}
public UpdateRadioStationRequest(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View file

@ -1,32 +0,0 @@
package com.serena.backend.dto;
import com.serena.backend.model.VoteType;
public class VoteRequest {
private String clientId;
private VoteType voteType;
public VoteRequest() {
}
public VoteRequest(String clientId, VoteType voteType) {
this.clientId = clientId;
this.voteType = voteType;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public VoteType getVoteType() {
return voteType;
}
public void setVoteType(VoteType voteType) {
this.voteType = voteType;
}
}

View file

@ -15,8 +15,6 @@ public class RadioStation {
private LocalDateTime createdAt;
private boolean isActive;
private List<String> connectedClients;
private List<Song> songQueue;
private Song currentlyPlaying;
public RadioStation() {
this.id = UUID.randomUUID().toString();
@ -24,7 +22,6 @@ public class RadioStation {
this.createdAt = LocalDateTime.now();
this.isActive = true;
this.connectedClients = new ArrayList<>();
this.songQueue = new ArrayList<>();
}
public RadioStation(String name, String description, String ownerId) {
@ -110,19 +107,4 @@ public class RadioStation {
this.connectedClients = connectedClients;
}
public List<Song> getSongQueue() {
return songQueue;
}
public void setSongQueue(List<Song> songQueue) {
this.songQueue = songQueue;
}
public Song getCurrentlyPlaying() {
return currentlyPlaying;
}
public void setCurrentlyPlaying(Song currentlyPlaying) {
this.currentlyPlaying = currentlyPlaying;
}
}

View file

@ -1,163 +0,0 @@
package com.serena.backend.model;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Song {
private String id;
private String title;
private String artist;
private String album;
private int duration; // in seconds
private String url;
private String addedBy;
private LocalDateTime addedAt;
private Map<String, VoteType> votes; // clientId -> vote
private int upvotes;
private int downvotes;
public Song() {
this.id = UUID.randomUUID().toString();
this.addedAt = LocalDateTime.now();
this.votes = new HashMap<>();
this.upvotes = 0;
this.downvotes = 0;
}
public Song(String title, String artist, String album, int duration, String url, String addedBy) {
this();
this.title = title;
this.artist = artist;
this.album = album;
this.duration = duration;
this.url = url;
this.addedBy = addedBy;
}
public void addVote(String clientId, VoteType voteType) {
VoteType previousVote = votes.get(clientId);
// Remove previous vote count
if (previousVote != null) {
if (previousVote == VoteType.UPVOTE) {
upvotes--;
} else if (previousVote == VoteType.DOWNVOTE) {
downvotes--;
}
}
// Add new vote
votes.put(clientId, voteType);
if (voteType == VoteType.UPVOTE) {
upvotes++;
} else if (voteType == VoteType.DOWNVOTE) {
downvotes++;
}
}
public void removeVote(String clientId) {
VoteType previousVote = votes.remove(clientId);
if (previousVote != null) {
if (previousVote == VoteType.UPVOTE) {
upvotes--;
} else if (previousVote == VoteType.DOWNVOTE) {
downvotes--;
}
}
}
public int getScore() {
return upvotes - downvotes;
}
// Getters and Setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getAlbum() {
return album;
}
public void setAlbum(String album) {
this.album = album;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAddedBy() {
return addedBy;
}
public void setAddedBy(String addedBy) {
this.addedBy = addedBy;
}
public LocalDateTime getAddedAt() {
return addedAt;
}
public void setAddedAt(LocalDateTime addedAt) {
this.addedAt = addedAt;
}
public Map<String, VoteType> getVotes() {
return votes;
}
public void setVotes(Map<String, VoteType> votes) {
this.votes = votes;
}
public int getUpvotes() {
return upvotes;
}
public void setUpvotes(int upvotes) {
this.upvotes = upvotes;
}
public int getDownvotes() {
return downvotes;
}
public void setDownvotes(int downvotes) {
this.downvotes = downvotes;
}
}

View file

@ -1,6 +0,0 @@
package com.serena.backend.model;
public enum VoteType {
UPVOTE,
DOWNVOTE
}

View file

@ -1,9 +1,7 @@
package com.serena.backend.service;
import com.serena.backend.model.RadioStation;
import com.serena.backend.model.Song;
import com.serena.backend.model.Client;
import com.serena.backend.model.VoteType;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@ -101,8 +99,6 @@ public class RadioStationService {
RadioStation station = radioStations.get(client.getRadioStationId());
if (station != null) {
station.getConnectedClients().remove(clientId);
// Remove votes from all songs
station.getSongQueue().forEach(song -> song.removeVote(clientId));
}
clients.remove(clientId);
return true;
@ -125,83 +121,4 @@ public class RadioStationService {
return new ArrayList<>();
}
// Song Management
public Optional<Song> addSongToQueue(String radioStationId, String title, String artist,
String album, int duration, String url, String addedBy) {
RadioStation station = radioStations.get(radioStationId);
if (station != null && station.isActive()) {
Song song = new Song(title, artist, album, duration, url, addedBy);
station.getSongQueue().add(song);
return Optional.of(song);
}
return Optional.empty();
}
public Optional<Song> voteSong(String radioStationId, String songId, String userId, VoteType voteType) {
RadioStation station = radioStations.get(radioStationId);
if (station != null) {
Optional<Song> songOpt = station.getSongQueue().stream()
.filter(song -> song.getId().equals(songId))
.findFirst();
if (songOpt.isPresent()) {
Song song = songOpt.get();
song.addVote(userId, voteType);
return Optional.of(song);
}
}
return Optional.empty();
}
public boolean removeSongVote(String radioStationId, String songId, String userId) {
RadioStation station = radioStations.get(radioStationId);
if (station != null) {
Optional<Song> songOpt = station.getSongQueue().stream()
.filter(song -> song.getId().equals(songId))
.findFirst();
if (songOpt.isPresent()) {
Song song = songOpt.get();
song.removeVote(userId);
return true;
}
}
return false;
}
public List<Song> getSongQueue(String radioStationId) {
RadioStation station = radioStations.get(radioStationId);
if (station != null) {
// Sort by score (upvotes - downvotes) descending
return station.getSongQueue().stream()
.sorted((s1, s2) -> Integer.compare(s2.getScore(), s1.getScore()))
.toList();
}
return new ArrayList<>();
}
public Optional<Song> getCurrentlyPlaying(String radioStationId) {
RadioStation station = radioStations.get(radioStationId);
if (station != null) {
return Optional.ofNullable(station.getCurrentlyPlaying());
}
return Optional.empty();
}
public Optional<Song> playNextSong(String radioStationId) {
RadioStation station = radioStations.get(radioStationId);
if (station != null && !station.getSongQueue().isEmpty()) {
// Get the song with highest score
Song nextSong = station.getSongQueue().stream()
.max((s1, s2) -> Integer.compare(s1.getScore(), s2.getScore()))
.orElse(null);
if (nextSong != null) {
station.getSongQueue().remove(nextSong);
station.setCurrentlyPlaying(nextSong);
return Optional.of(nextSong);
}
}
return Optional.empty();
}
}