Refactor REST API music playback flow
Replaced `/play` and `/play/select` endpoints with more streamlined `/search` and `/queue` routes. Updated `MusicManager` and related components to separate query resolution from playback queuing, requiring explicit confirmation for each candidate. Introduced `SearchRequest`, `SearchResultHandler`, and revamped `PlayOutcome` to improve clarity and flexibility.
This commit is contained in:
1 parent
0e68886eb2
commit
a605a834b4
8 files changed
+173
-115
No files matched your search
@@ -0,0 +1,111 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import dev.arbjerg.lavalink.client.AbstractAudioLoadResultHandler;
|
||||
import dev.arbjerg.lavalink.client.player.LoadFailed;
|
||||
import dev.arbjerg.lavalink.client.player.PlaylistLoaded;
|
||||
import dev.arbjerg.lavalink.client.player.SearchResult;
|
||||
import dev.arbjerg.lavalink.client.player.Track;
|
||||
import dev.arbjerg.lavalink.client.player.TrackLoaded;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.db.GSettings;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Translates a Lavalink load result into a {@link PlayOutcome} without ever queuing anything, so
|
||||
* that every REST load request, not only ambiguous text searches, requires a separate
|
||||
* {@code /play} call with {@code searchId}/{@code index} to actually queue a track.
|
||||
*
|
||||
* <p>A single resolved track, the tracks of a playlist, and ambiguous search results are each
|
||||
* stashed as candidates in {@link PendingSearchResults} and reported back with their own
|
||||
* {@link PlayOutcome.PendingTrack}, {@link PlayOutcome.PendingPlaylist}, or
|
||||
* {@link PlayOutcome.PendingSearch} outcome, respectively.
|
||||
*/
|
||||
public class SearchResultHandler extends AbstractAudioLoadResultHandler {
|
||||
private static final Logger logger = LogManager.getLogger(SearchResultHandler.class);
|
||||
private static final int SEARCH_RESULTS_LIMIT = 5;
|
||||
|
||||
private final String identifier;
|
||||
private final Guild guild;
|
||||
private final MusicManager manager;
|
||||
private final CompletableFuture<PlayOutcome> future;
|
||||
|
||||
public SearchResultHandler(MusicManager manager, String identifier, CompletableFuture<PlayOutcome> future) {
|
||||
this.identifier = identifier;
|
||||
this.guild = manager.getGuild();
|
||||
this.manager = manager;
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ontrackLoaded(TrackLoaded result) {
|
||||
Track track = result.getTrack();
|
||||
logger.debug("Guild {}: track resolved: {}", guild.getId(), track.getInfo().getIdentifier());
|
||||
|
||||
String searchId = PendingSearchResults.put(guild.getId(), List.of(track));
|
||||
future.complete(new PlayOutcome.PendingTrack(searchId, track));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaylistLoaded(PlaylistLoaded result) {
|
||||
logger.debug("Guild {}: playlist resolved: {}", guild.getId(), identifier);
|
||||
|
||||
List<Track> tracks = result.getTracks();
|
||||
int maxPlaylistSize = parseMaxPlaylistSize();
|
||||
boolean truncated = tracks.size() > maxPlaylistSize;
|
||||
List<Track> candidates = truncated ? tracks.subList(0, maxPlaylistSize) : tracks;
|
||||
|
||||
String searchId = PendingSearchResults.put(guild.getId(), candidates);
|
||||
future.complete(new PlayOutcome.PendingPlaylist(searchId, candidates, truncated));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchResultLoaded(SearchResult result) {
|
||||
List<Track> tracks = result.getTracks();
|
||||
logger.debug("Guild {}: search returned {} results for {}", guild.getId(), tracks.size(), identifier);
|
||||
|
||||
if (tracks.isEmpty()) {
|
||||
noMatches();
|
||||
return;
|
||||
}
|
||||
|
||||
List<Track> candidates = tracks.stream().limit(SEARCH_RESULTS_LIMIT).toList();
|
||||
String searchId = PendingSearchResults.put(guild.getId(), candidates);
|
||||
future.complete(new PlayOutcome.PendingSearch(searchId, candidates));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
logger.debug("Guild {}: no matches for identifier {}", guild.getId(), identifier);
|
||||
|
||||
future.complete(new PlayOutcome.NoMatches());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(LoadFailed result) {
|
||||
logger.debug("Guild {}: load failed for identifier {}", guild.getId(), identifier);
|
||||
|
||||
RuntimeException exception = new RuntimeException("Failed to load track: " + result.getException().getMessage());
|
||||
LogHandler.log(exception);
|
||||
|
||||
future.complete(new PlayOutcome.Failed(exception.getMessage()));
|
||||
}
|
||||
|
||||
private int parseMaxPlaylistSize() {
|
||||
try {
|
||||
return Integer.parseInt(manager.getGuildService().getGuildSettingService().getOrDefault(GSettings.MUSIC_MAXPLAYLISTSIZE));
|
||||
} catch (NumberFormatException e) {
|
||||
return Integer.parseInt(GSettings.MUSIC_MAXPLAYLISTSIZE.getDefaultValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user