diff --git a/src/main/java/me/bobert/netneutrality/http/server/MusicRoutes.java b/src/main/java/me/bobert/netneutrality/http/server/MusicRoutes.java index 1dd550f..a904b5a 100644 --- a/src/main/java/me/bobert/netneutrality/http/server/MusicRoutes.java +++ b/src/main/java/me/bobert/netneutrality/http/server/MusicRoutes.java @@ -34,6 +34,7 @@ import me.bobert.netneutrality.http.server.dto.SearchResponse; import me.bobert.netneutrality.http.server.dto.SeekRequest; import me.bobert.netneutrality.http.server.dto.SeekResponse; import me.bobert.netneutrality.http.server.dto.ShuffleResponse; +import me.bobert.netneutrality.http.server.dto.VoteSkipStatusResponse; import me.bobert.netneutrality.http.server.dto.VolumeRequest; import me.bobert.netneutrality.http.server.dto.VolumeResponse; import me.bobert.netneutrality.music.MusicManager; @@ -79,6 +80,7 @@ public final class MusicRoutes { routes.get("/music/{guildId}/volume", ctx -> getVolume(jdaSupplier, musicService, ctx)); routes.put("/music/{guildId}/volume", ctx -> volume(jdaSupplier, musicService, ctx)); routes.put("/music/{guildId}/seek", ctx -> seek(jdaSupplier, musicService, ctx)); + routes.get("/music/{guildId}/vote-skip", ctx -> voteSkipStatus(jdaSupplier, musicService, ctx)); routes.post("/music/{guildId}/vote-skip/{userId}", ctx -> voteSkip(jdaSupplier, musicService, ctx)); routes.get("/music/{guildId}/dj/{userId}", ctx -> dj(jdaSupplier, musicService, ctx)); routes.post("/music/{guildId}/queue", ctx -> play(jdaSupplier, musicService, ctx)); @@ -328,6 +330,31 @@ public final class MusicRoutes { return positionMs; } + @OpenApi( + path = "/music/{guildId}/vote-skip", + methods = HttpMethod.GET, + summary = "Get the current vote-skip status of the playing track", + tags = {"Music"}, + pathParams = @OpenApiParam(name = "guildId", description = "The Discord guild id"), + responses = { + @OpenApiResponse(status = "200", content = @OpenApiContent(from = VoteSkipStatusResponse.class)), + @OpenApiResponse(status = "404", content = @OpenApiContent(from = ErrorResponse.class)) + } + ) + private static void voteSkipStatus(Supplier jdaSupplier, MusicService musicService, Context ctx) { + MusicManager manager = manager(jdaSupplier, musicService, ctx); + AudioChannel channel = manager.getConnectedChannel(); + TrackInfo head = manager.getQueueManager().peekHeadOfQueue(); + int threshold = manager.getVoteSkipThreshold(); + + boolean active = channel != null && head != null && manager.isCurrentlyPlaying(); + int listeners = channel == null ? 0 : Math.max(0, channel.getMembers().size() - 1); + int votes = head == null ? 0 : head.getSkipCount(); + int needed = (int) Math.ceil(listeners * (threshold / 100.0)); + + ctx.json(new VoteSkipStatusResponse(active, votes, needed, listeners, threshold)); + } + @OpenApi( path = "/music/{guildId}/vote-skip/{userId}", methods = HttpMethod.POST, diff --git a/src/main/java/me/bobert/netneutrality/http/server/dto/VoteSkipStatusResponse.java b/src/main/java/me/bobert/netneutrality/http/server/dto/VoteSkipStatusResponse.java new file mode 100644 index 0000000..f901304 --- /dev/null +++ b/src/main/java/me/bobert/netneutrality/http/server/dto/VoteSkipStatusResponse.java @@ -0,0 +1,19 @@ +package me.bobert.netneutrality.http.server.dto; + +/* + * Copyright (c) anthorob + * + * https://discord.gg/qUa6zNFZsd + */ + +/** + * OpenAPI schema for the {@code GET /music/{guildId}/vote-skip} response. + */ +public record VoteSkipStatusResponse( + boolean active, + int votes, + int votesNeeded, + int listeners, + int thresholdPercent +) { +} diff --git a/src/main/java/me/bobert/netneutrality/music/MusicManager.java b/src/main/java/me/bobert/netneutrality/music/MusicManager.java index 7c631e3..aff422f 100644 --- a/src/main/java/me/bobert/netneutrality/music/MusicManager.java +++ b/src/main/java/me/bobert/netneutrality/music/MusicManager.java @@ -356,6 +356,13 @@ public class MusicManager implements MusicManagerCallback { return voteSkipService.voteSkip(member, guild.getId(), getQueueManager(), getConnectedChannel(), isCurrentlyPlaying(), threshold); } + /** + * Returns the configured vote-skip threshold as a percentage of connected listeners. + */ + public int getVoteSkipThreshold() { + return parseVoteSkipThreshold(guildService); + } + private static int parseVoteSkipThreshold(GuildService guildService) { try { return Integer.parseInt(guildService.getGuildSettingService().getOrDefault(GSettings.MUSIC_VOTE_SKIP_THRESHOLD));