refactor: remove convoluted TrackLoader and LoadableSongs abstraction

BREAKING CHANGE: removed TrackLoader amd LoadableSongs
This commit is contained in:
anthorob committed 2024-07-14 00:28:44 -04:00
1 parent 0db751af44
commit 2aa75ce0fc
10 files changed
+9 -385

No files matched your search

@@ -19,7 +19,6 @@ import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.exceptions.CommandExecutionException;
import me.bobert.netneutrality.music.loadable.LoadableSong;
import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
import me.bobert.netneutrality.thread.ThreadManager;
@@ -30,8 +29,6 @@ import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
@@ -48,7 +45,6 @@ public class MusicManager {
private final Guild guild;
private final AudioPlayer player;
private final TrackScheduler scheduler;
private TrackLoader lastTrackLoader;
private ScheduledFuture<?> autoDisconnectThread;
private final int baseVolume;
@@ -126,10 +122,6 @@ public class MusicManager {
return guild;
}
public void stopLoading() {
lastTrackLoader.stopLoading();
}
public synchronized void leave() {
if (guild != null && guild.getAudioManager().getConnectedChannel() != null) {
logger.debug("Guild {}: leaving voice channel {}", guild.getId(), guild.getAudioManager().getConnectedChannel().getId());
@@ -152,16 +144,9 @@ public class MusicManager {
}
}
public synchronized void play(String url, CommandContext context, boolean needToBeConnected, boolean silent) {
logger.debug("Guild {}: request to play '{}' by user {}", guild.getId(), url, context.getGuildUser().getId());
public synchronized void play(String query, CommandContext context, boolean needToBeConnected, boolean silent) {
logger.debug("Guild {}: request to play '{}' by user {}", guild.getId(), query, context.getGuildUser().getId());
List<LoadableSong> toLoad = new ArrayList<>();
toLoad.add(new LoadableSong(null, url, null));
play(toLoad, context, needToBeConnected, silent);
}
public void play(List<LoadableSong> songs, CommandContext context, boolean needToBeConnected, boolean silent) {
Member member = context.getGuildUser();
MessageChannel channel = context.getChannel();
@@ -189,10 +174,14 @@ public class MusicManager {
scheduler.attachTextChannel(channel);
}
TrackLoader loader = new TrackLoader(this, context, playerManager, silent);
lastTrackLoader = loader;
SourceFinder.AudioSourceType type = SourceFinder.getSource(query);
String identifier = SourceFinder.getIdentifier(query);
loader.loadAllSongs(songs);
if (identifier != null) {
TrackLoadingHandler loadingHandler = new TrackLoadingHandler(context, this, identifier, type, silent);
playerManager.loadItemOrdered(this, identifier, loadingHandler);
}
}
public synchronized void stopTrack() {
@@ -138,7 +138,6 @@ public class QueueManager {
}
public CommandExecutionResult clearQueue() {
scheduler.getMusicManager().stopLoading();
scheduler.getMusicManager().leave();
if (queue.isEmpty()) {
@@ -1,126 +0,0 @@
package me.bobert.netneutrality.music;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.loadable.CachedSource;
import me.bobert.netneutrality.music.loadable.LoadableMusicSource;
import me.bobert.netneutrality.music.loadable.LoadableSong;
import me.bobert.netneutrality.thread.ThreadManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
public class TrackLoader implements Runnable {
private final Logger logger = LogManager.getLogger(TrackLoader.class);
private final CommandContext context;
private final MusicManager musicManager;
private final AudioPlayerManager audioPlayerManager;
private TrackLoadingHandler lastLoadingHandler;
private List<LoadableSong> songs;
private LoadableMusicSource source;
private int numberOfTracksLoaded;
private int totalNumberOfTracks;
private boolean loadingDisabled = false;
private boolean silent;
private final Object sync = new Object();
public TrackLoader(MusicManager musicManager, CommandContext context, AudioPlayerManager playerManager, boolean silent) {
this.musicManager = musicManager;
this.context = context;
this.audioPlayerManager = playerManager;
this.silent = silent;
}
public void loadAllSongs(List<LoadableSong> songs) {
this.songs = songs;
if (songs.size() > 1) {
this.silent = true;
}
ThreadManager.getMusicExecutor().submit(this);
}
public void loadAllSongs(CachedSource cachedSource) {
this.source = cachedSource.getSource();
loadAllSongs(cachedSource.getSongs());
}
public void stopLoading() {
loadingDisabled = true;
if (lastLoadingHandler != null) {
lastLoadingHandler.stopLoading();
}
}
@Override
public void run() {
List<LoadableSong> copy = new ArrayList<>(songs);
for (LoadableSong song : songs) {
if (loadingDisabled) {
copy.remove(song);
break;
}
SourceFinder.AudioSourceType type = SourceFinder.getSource(song.name());
String identifier = SourceFinder.getIdentifier(song.name());
if (identifier != null) {
TrackLoadingHandler loadingHandler = new TrackLoadingHandler(context, musicManager, identifier, type, silent);
loadingHandler.useCallbackOnLoad(success -> {
synchronized (sync) {
if (success) {
numberOfTracksLoaded++;
}
totalNumberOfTracks++;
logger.debug("callback notify; total={}, loaded={}", totalNumberOfTracks, numberOfTracksLoaded);
sync.notify();
}
});
lastLoadingHandler = loadingHandler;
audioPlayerManager.loadItemOrdered(this, identifier, loadingHandler);
} else {
copy.remove(song);
if (songs.size() == 1) {
context.replyToMessage("No source found to play the given url");
}
}
}
try {
synchronized (sync) {
while (!(copy.size() == totalNumberOfTracks)) {
logger.debug("waiting for load; size={}, total={}, loaded={}", songs.size(), totalNumberOfTracks, numberOfTracksLoaded);
sync.wait();
}
//TODO: logic
if (copy.size() > 1 && source != null && !loadingDisabled) {
context.replyToMessage("Loaded %d of %d tracks from online source", numberOfTracksLoaded, totalNumberOfTracks);
}
}
} catch (InterruptedException exception) {
stopLoading();
context.replyToMessage("Loading of tracks was interrupted due to an exception");
LogHandler.log(exception);
}
}
}
@@ -17,16 +17,12 @@ import net.dv8tion.jda.api.entities.Guild;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.function.Consumer;
public class TrackLoadingHandler implements AudioLoadResultHandler {
private final Logger logger = LogManager.getLogger(TrackLoadingHandler.class);
private final String identifier;
private final SourceFinder.AudioSourceType type;
private final boolean silent;
private Consumer<Boolean> loadConsumer;
private boolean loadingDisabled = false;
private final Guild guild;
private final TrackScheduler scheduler;
private final CommandContext context;
@@ -40,24 +36,8 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
this.context = context;
}
public synchronized void stopLoading() {
logger.debug("Guild {}: stopped loading of new tracks", guild.getId());
loadingDisabled = true;
}
public void useCallbackOnLoad(Consumer<Boolean> consumer) {
this.loadConsumer = consumer;
}
@Override
public void trackLoaded(AudioTrack audioTrack) {
if (loadConsumer != null) {
loadConsumer.accept(true);
}
if (loadingDisabled)
return;
context.deleteMessage();
logger.debug("Guild {}: track loaded: {}", guild.getId(), audioTrack.getIdentifier());
@@ -70,13 +50,6 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
@Override
public void playlistLoaded(AudioPlaylist playlist) {
if (loadConsumer != null) {
loadConsumer.accept(true);
}
if (loadingDisabled)
return;
logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier);
if (!playlist.getTracks().isEmpty()) {
@@ -96,10 +69,6 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
}
for (AudioTrack track : playlist.getTracks()) {
if (loadingDisabled) {
break;
}
scheduler.getQueueManager().queue(track, context.getGuildUser());
}
}
@@ -108,18 +77,12 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
@Override
public void noMatches() {
if (loadConsumer != null)
loadConsumer.accept(false);
if (!silent)
context.replyToMessage("No matches found");
}
@Override
public void loadFailed(FriendlyException e) {
if (loadConsumer != null)
loadConsumer.accept(false);
logger.debug("Guild {}: load failed for identifier {}", guild.getId(), identifier, e);
LogHandler.log(e);
@@ -1,41 +0,0 @@
package me.bobert.netneutrality.music.loadable;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class AppleMostPlayed implements LoadableMusicSource {
@Override
public String url() {
return "https://rss.applemarketingtools.com/api/v2/ca/music/most-played/25/songs.json";
}
@Override
public List<LoadableSong> parse(JSONObject obj) {
List<LoadableSong> songs = new ArrayList<>();
JSONArray results = obj.getJSONObject("feed").getJSONArray("results");
results.forEach(o -> {
JSONObject song = (JSONObject) o;
String artist = song.getString("artistName");
String songName = song.getString("name");
String artwork = song.getString("artworkUrl100");
songs.add(new LoadableSong(artist, songName, artwork));
});
return songs;
}
@Override
public String getIdentifier() {
return "apple";
}
}
@@ -1,39 +0,0 @@
package me.bobert.netneutrality.music.loadable;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import java.time.OffsetDateTime;
import java.util.List;
public class CachedSource {
private final LoadableMusicSource source;
private OffsetDateTime lastAccess;
private final List<LoadableSong> songs;
protected CachedSource(LoadableMusicSource source, List<LoadableSong> songs) {
this.source = source;
this.lastAccess = OffsetDateTime.now();
this.songs = songs;
}
public List<LoadableSong> getSongs() {
return songs;
}
public OffsetDateTime getLastAccess() {
return lastAccess;
}
public LoadableMusicSource getSource() {
return source;
}
public void updateAccessTime() {
this.lastAccess = OffsetDateTime.now();
}
}
@@ -1,19 +0,0 @@
package me.bobert.netneutrality.music.loadable;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import org.json.JSONObject;
import java.util.List;
public interface LoadableMusicSource {
String url();
List<LoadableSong> parse(JSONObject obj);
String getIdentifier();
}
@@ -1,18 +0,0 @@
package me.bobert.netneutrality.music.loadable;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
public record LoadableSong(String artist, String name, String artworkUrl) {
@Override
public String toString() {
return "LoadableSong{" +
"artist='" + artist + '\'' +
", name='" + name + '\'' +
", artworkUrl='" + artworkUrl + '\'' +
'}';
}
}
@@ -1,57 +0,0 @@
package me.bobert.netneutrality.music.loadable;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.http.WebModule;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import java.time.OffsetDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LoadableSourceManager {
private final Logger logger = LogManager.getLogger(LoadableSourceManager.class);
private static final Map<String, CachedSource> cache = new HashMap<>();
public CachedSource get(LoadableMusicSource source) {
if (cache.containsKey(source.getIdentifier())) {
CachedSource cachedSource = cache.get(source.getIdentifier());
if (OffsetDateTime.now().isAfter(cachedSource.getLastAccess().plusHours(4))) {
cachedSource.getSongs().clear();
cachedSource.getSongs().addAll(loadFromUrl(source));
cachedSource.updateAccessTime();
}
return cachedSource;
}
List<LoadableSong> songs = loadFromUrl(source);
CachedSource newCachedSource = new CachedSource(source, songs);
cache.put(source.getIdentifier(), newCachedSource);
return newCachedSource;
}
private List<LoadableSong> loadFromUrl(LoadableMusicSource source) {
WebModule web = new WebModule(source.url());
JSONObject obj = web.request().getAsObject();
if (obj == null)
return Collections.emptyList();
List<LoadableSong> songs = source.parse(obj);
Collections.shuffle(songs);
return songs;
}
}
@@ -1,27 +0,0 @@
package netneutrality.music;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.music.loadable.AppleMostPlayed;
import me.bobert.netneutrality.music.loadable.CachedSource;
import me.bobert.netneutrality.music.loadable.LoadableSourceManager;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
public class LoadableSourceTest {
@Test
public void cachedSourceAccess() {
LoadableSourceManager loadableSourceManager = new LoadableSourceManager();
CachedSource cachedSource = loadableSourceManager.get(new AppleMostPlayed());
CachedSource sameCachedSource = loadableSourceManager.get(new AppleMostPlayed());
assertTrue(cachedSource.getLastAccess().isEqual(sameCachedSource.getLastAccess()));
}
}