Page MenuHomedesp's stash

TrackScheduler.java
No OneTemporary

TrackScheduler.java

package me.despawningbone.discordbot.command.music;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import me.despawningbone.discordbot.DiscordBot;
import me.despawningbone.discordbot.command.music.AudioTrackHandler.TrackData;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import org.json.JSONObject;
import org.json.JSONTokener;
/**
* This class schedules tracks for the audio player. It contains the queue of
* tracks.
*/
public class TrackScheduler extends AudioEventAdapter {
private final AudioPlayer player;
private final BlockingQueue<AudioTrack> queue;
private AudioTrackHandler ap;
private Guild guild;
public String loop = null; //way too lazy to use getter setters lmao
/**
* @param player
* The audio player this scheduler uses
*/
public TrackScheduler(Guild parent, AudioTrackHandler handler, AudioPlayer player) {
this.player = player;
this.queue = new LinkedBlockingQueue<>();
this.ap = handler;
this.guild = parent;
}
/**
* Add the next track to queue or play right away if nothing is in the
* queue.
*
* @param track
* The track to play or add to queue.
*/
public void queue(AudioTrack track) {
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If
// something is playing, it returns false and does nothing. In that case the player was already playing so this
// track goes to the queue instead.
if (!player.startTrack(track, true)) {
queue.offer(track);
} else if (loop != null && loop.equals("autoplay")) { //i dont think this is needed as people need to play something before autoplay can be toggled anyways
queueAutoplay(track);
}
}
/**
* Start the next track, stopping the current one if it is playing.
*/
public void nextTrack() { //DONE rewrite to not include q.remove here so that stuff like interrupted wont break the queue?
// Start the next track, regardless of if something is already playing or not. In case queue was empty, we are
// giving null to startTrack, which is a valid argument and will simply stop the player.
AudioTrack track = queue.poll();
player.startTrack(track, false);
if(track == null) {
//System.out.println("finished"); //debug
loop = null;
delayCloseConnection(player); //required because if not it will throw InterruptedException
}
} //seems to be called internally somehow; even when mayStartNext is false (REPLACED, STOPPED etc) this still fires
//NVM ITS CALLED FROM AudioTrackHandler.skipTrack() LOL
public void queueAutoplay(AudioTrack track) { //check duplicate please, some can get into a dead loop like cosMo@暴走P - WalpurgisNacht and Ice - 絶 //well randoming it works
ap.ex.submit(() -> { //async so it can free up the event
try {
//System.out.println("autoplay");
InputStream input = new URL("https://www.googleapis.com/youtube/v3/search?part=snippet&relatedToVideoId=" + track.getIdentifier() + "&type=video&key=" + ap.GAPI).openStream();
JSONTokener result = new JSONTokener(new InputStreamReader(input, "UTF-8"));
Member temp;
try {
temp = guild.getMemberByTag(track.getUserData(TrackData.class).getUserWithDiscriminator());
} catch (IllegalArgumentException e) { //track was autoplay queued before this
temp = guild.getMemberById(DiscordBot.BotID); //fallback
}
final Member user = temp;
int t = ThreadLocalRandom.current().nextInt(2);
String url = "https://youtube.com/watch?v=" + new JSONObject(result).getJSONArray("items").getJSONObject(t).getJSONObject("id").getString("videoId");
ap.load(user, url, (n, l) -> {
AudioTrack auto = l.get(0);
auto.setUserData(new TrackData(auto.getInfo().uri, "Autoplay", auto.getDuration()));
ap.queueTracks(l.subList(0, 1), user);
}, ex -> {
ex.printStackTrace();
DiscordBot.lastMusicCmd.get(guild.getId()).sendMessage("Something went wrong when loading next autoplay track: " + ex.getMessage()).queue();
loop = null;
});
} catch (Exception e) {
e.printStackTrace();
DiscordBot.lastMusicCmd.get(guild.getId()).sendMessage("Something went wrong when loading next autoplay track. Is the last track a youtube video?").queue();
loop = null;
}
});
}
public List<AudioTrack> findTracks(int startIndex, int range) {
AudioTrack[] a = queue.toArray(new AudioTrack[queue.size()]);
int i = startIndex - 1 + range < 0 ? Integer.MAX_VALUE : startIndex - 1 + range; //prevent overflow
AudioTrack[] t = Arrays.copyOfRange(a, startIndex - 1, Math.min(queue.size() + 1, i)); //accounts for first track player thats not in queue
return Arrays.asList(t);
}
public AudioTrack removeTrack(int num) {
Iterator<AudioTrack> i = queue.iterator();
num = num - 1;
for (int times = 0; i.hasNext(); times++) {
AudioTrack removed = i.next();
if (num == times) {
i.remove();
return removed;
}
}
return null;
}
public AudioTrack moveTrack(int from, int to) {
List<AudioTrack> q = new ArrayList<>(Arrays.asList(queue.toArray(new AudioTrack[queue.size()])));
AudioTrack track = q.remove(from);
q.add(to, track);
synchronized (queue) { //obtain lock and operate before releasing or else queue might not be complete
queue.clear();
for(AudioTrack t : q) queue.offer(t);
}
return track;
}
public void shuffleQueue() {
List<AudioTrack> q = Arrays.asList(queue.toArray(new AudioTrack[queue.size()]));
Collections.shuffle(q);
synchronized (queue) { //obtain lock and operate before releasing or else queue might not be complete
queue.clear();
for(AudioTrack t : q) queue.offer(t);
}
}
public void clearSchedulerQueue() {
queue.clear();
}
public int getQueueSize() {
return queue.size();
}
@Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
// Only start the next track if the end reason is suitable for it (FINISHED or LOAD_FAILED or REPLACED)
//System.out.println(endReason); //debug
boolean mayStartNext = endReason.mayStartNext;
if (mayStartNext) {
//doesnt queue clone if skipped
if (loop != null && loop.equals("loop")) { //so what the hecc if loop is null and i do loop.equals("Loop") it freezes the thread
AudioTrack clone = track.makeClone();
TrackData origData = clone.getUserData(TrackData.class);
clone.setUserData(new TrackData(origData.getUrl(), origData.getUserWithDiscriminator(), clone.getDuration())); //wipe votes
queue.offer(clone);
}
nextTrack();
}
if(mayStartNext || endReason == AudioTrackEndReason.REPLACED) { //queues new if skipped too
if (loop != null && loop.equals("autoplay") && queue.size() < 1) {
queueAutoplay(player.getPlayingTrack());
}
}
}
private void delayCloseConnection(AudioPlayer player) { //TODO configurable delay; dont leave if new things play within the delay
ap.ex.schedule(() -> guild.getAudioManager().closeAudioConnection(), 1, TimeUnit.MILLISECONDS);
}
}

File Metadata

Mime Type
text/x-Algol68
Expires
Sun, Jul 6, 4:46 PM (1 d, 3 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ba/d0/d3774f8e252fa991c5d9eaa413d7

Event Timeline