Skip to content

Commit d56dbe5

Browse files
committed
Added Music...
1 parent 43a0fe3 commit d56dbe5

File tree

13 files changed

+902
-1
lines changed

13 files changed

+902
-1
lines changed

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,12 @@ dependencies {
7272
testImplementation 'org.junit.jupiter:junit-jupiter'
7373

7474
installer('org.mangorage:installer:4.0.3')
75-
bot('org.mangorage:mangobot:12.0.19')
75+
bot('org.mangorage:mangobot:12.0.20')
7676

7777
library('org.luaj:luaj-jse:3.0.1')
78+
79+
library('dev.arbjerg:lavaplayer:2.2.3')
80+
library('dev.lavalink.youtube:common:1.12.0')
7881
}
7982

8083
configurations.all {

src/main/java/module-info.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
requires net.minecraftforge.eventbus;
88
requires java.desktop;
99
requires annotations;
10+
requires lavaplayer;
11+
requires common;
1012

1113
exports org.mangorage.mangobotplugin.entrypoint;
1214
exports org.mangorage.mangobotplugin.commands.trick;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2023. MangoRage
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20+
* OR OTHER DEALINGS IN THE SOFTWARE.
21+
*/
22+
23+
package org.mangorage.mangobotplugin.commands.music;
24+
25+
public enum AudioStatus {
26+
PLAYING,
27+
PAUSED,
28+
STOPPED
29+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2023. MangoRage
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20+
* OR OTHER DEALINGS IN THE SOFTWARE.
21+
*/
22+
23+
package org.mangorage.mangobotplugin.commands.music;
24+
25+
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
26+
27+
public class AudioTrackEvent {
28+
private final AudioTrack track;
29+
private final Info reason;
30+
31+
public AudioTrackEvent(AudioTrack track, Info info) {
32+
this.track = track;
33+
this.reason = info;
34+
}
35+
36+
public AudioTrack getTrack() {
37+
return track;
38+
}
39+
40+
public Info getReason() {
41+
return reason;
42+
}
43+
44+
public enum Info {
45+
SUCCESS,
46+
FAILED,
47+
NO_MATCHES
48+
}
49+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (c) 2023. MangoRage
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20+
* OR OTHER DEALINGS IN THE SOFTWARE.
21+
*/
22+
23+
package org.mangorage.mangobotplugin.commands.music;
24+
25+
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
26+
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
27+
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer;
28+
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
29+
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
30+
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
31+
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
32+
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
33+
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
34+
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
35+
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
36+
import net.dv8tion.jda.api.audio.AudioSendHandler;
37+
import org.mangorage.commonutils.log.LogHelper;
38+
39+
import java.nio.ByteBuffer;
40+
import java.util.ArrayDeque;
41+
import java.util.Deque;
42+
import java.util.HashMap;
43+
import java.util.function.Consumer;
44+
45+
public class MusicPlayer extends AudioEventAdapter implements AudioSendHandler {
46+
private static final HashMap<String, MusicPlayer> MUSIC_PLAYERS = new HashMap<>();
47+
48+
public static MusicPlayer getInstance(String guildID) {
49+
if (!MUSIC_PLAYERS.containsKey(guildID)) {
50+
MusicPlayer player = new MusicPlayer(guildID);
51+
MUSIC_PLAYERS.put(guildID, player);
52+
return player;
53+
}
54+
return MUSIC_PLAYERS.get(guildID);
55+
}
56+
57+
private final DefaultAudioPlayerManager manager;
58+
private final DefaultAudioPlayer audioPlayer;
59+
private final Deque<AudioTrack> TRACKS_QUEUE = new ArrayDeque<>();
60+
private final String guildID;
61+
private AudioStatus status = AudioStatus.STOPPED;
62+
private AudioFrame lastFrame;
63+
64+
65+
private MusicPlayer(String guildID) {
66+
this.guildID = guildID;
67+
this.manager = new DefaultAudioPlayerManager();
68+
this.audioPlayer = new DefaultAudioPlayer(manager);
69+
70+
MusicUtil.registerRemoteSources(manager);
71+
72+
audioPlayer.addListener(this);
73+
}
74+
75+
public AudioTrack getPlaying() {
76+
return audioPlayer.getPlayingTrack();
77+
}
78+
79+
public void setVolume(int volume) {
80+
audioPlayer.setVolume(volume);
81+
}
82+
83+
public boolean isPlaying() {
84+
if (audioPlayer.getPlayingTrack() == null)
85+
return false;
86+
return audioPlayer.getPlayingTrack() != null;
87+
}
88+
89+
public boolean isQueueEmpty() {
90+
return TRACKS_QUEUE.isEmpty();
91+
}
92+
93+
public void load(String URL, Consumer<AudioTrackEvent> eventConsumer) {
94+
95+
manager.loadItem(new AudioReference(URL.trim(), null), new AudioLoadResultHandler() {
96+
@Override
97+
public void trackLoaded(AudioTrack track) {
98+
eventConsumer.accept(new AudioTrackEvent(track, AudioTrackEvent.Info.SUCCESS));
99+
}
100+
101+
@Override
102+
public void playlistLoaded(AudioPlaylist playlist) {
103+
// Allow playlists maybe?
104+
}
105+
106+
@Override
107+
public void noMatches() {
108+
eventConsumer.accept(new AudioTrackEvent(null, AudioTrackEvent.Info.NO_MATCHES));
109+
}
110+
111+
@Override
112+
public void loadFailed(FriendlyException exception) {
113+
eventConsumer.accept(new AudioTrackEvent(null, AudioTrackEvent.Info.FAILED));
114+
LogHelper.info(exception.getMessage());
115+
}
116+
});
117+
}
118+
119+
public AudioStatus getStatus() {
120+
return this.status;
121+
}
122+
123+
public void play() {
124+
AudioTrack track = TRACKS_QUEUE.poll();
125+
if (track != null)
126+
audioPlayer.playTrack(track);
127+
}
128+
129+
public void playNext() {
130+
131+
}
132+
133+
public void add(AudioTrack track) {
134+
TRACKS_QUEUE.add(track);
135+
}
136+
137+
public void pause() {
138+
audioPlayer.setPaused(true);
139+
}
140+
141+
public void resume() {
142+
audioPlayer.setPaused(false);
143+
}
144+
145+
public void stop() {
146+
audioPlayer.stopTrack();
147+
}
148+
149+
150+
public void onPlayerPause(AudioPlayer player) {
151+
this.status = AudioStatus.PAUSED;
152+
}
153+
154+
public void onPlayerResume(AudioPlayer player) {
155+
this.status = AudioStatus.PLAYING;
156+
}
157+
158+
public void onTrackStart(AudioPlayer player, AudioTrack track) {
159+
this.status = AudioStatus.PLAYING;
160+
}
161+
162+
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
163+
this.status = AudioStatus.STOPPED;
164+
if (endReason.mayStartNext)
165+
playNext();
166+
}
167+
168+
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
169+
LogHelper.info(exception.getMessage());
170+
}
171+
172+
173+
@Override
174+
public boolean canProvide() {
175+
lastFrame = audioPlayer.provide();
176+
return lastFrame != null;
177+
}
178+
179+
@Override
180+
public ByteBuffer provide20MsAudio() {
181+
return ByteBuffer.wrap(lastFrame.getData());
182+
}
183+
184+
@Override
185+
public boolean isOpus() {
186+
return true;
187+
}
188+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2023. MangoRage
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20+
* OR OTHER DEALINGS IN THE SOFTWARE.
21+
*/
22+
23+
package org.mangorage.mangobotplugin.commands.music;
24+
25+
import com.sedmelluq.discord.lavaplayer.container.MediaContainerRegistry;
26+
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
27+
import com.sedmelluq.discord.lavaplayer.source.bandcamp.BandcampAudioSourceManager;
28+
import com.sedmelluq.discord.lavaplayer.source.beam.BeamAudioSourceManager;
29+
import com.sedmelluq.discord.lavaplayer.source.getyarn.GetyarnAudioSourceManager;
30+
import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
31+
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
32+
import com.sedmelluq.discord.lavaplayer.source.vimeo.VimeoAudioSourceManager;
33+
import dev.lavalink.youtube.YoutubeAudioSourceManager;
34+
import net.dv8tion.jda.api.audio.SpeakingMode;
35+
import net.dv8tion.jda.api.entities.Guild;
36+
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
37+
import net.dv8tion.jda.api.managers.AudioManager;
38+
import org.mangorage.commonutils.log.LogHelper;
39+
40+
public class MusicUtil {
41+
public static void connectToAudioChannel(VoiceChannel channel) {
42+
try {
43+
Guild guild = channel.getGuild();
44+
AudioManager audioManager = guild.getAudioManager();
45+
46+
audioManager.setSendingHandler(MusicPlayer.getInstance(guild.getId()));
47+
48+
audioManager.setSelfDeafened(true);
49+
audioManager.setSelfMuted(false);
50+
audioManager.setAutoReconnect(true);
51+
audioManager.setSpeakingMode(SpeakingMode.SOUNDSHARE);
52+
audioManager.setConnectTimeout(30_000);
53+
54+
MusicPlayer.getInstance(guild.getId()).setVolume(5); // Default volume so nobody gets there ears torn out by sound.
55+
audioManager.openAudioConnection(channel);
56+
} catch (Exception e) {
57+
LogHelper.error("Failed to connect to voice channel: " + e.getMessage());
58+
}
59+
}
60+
61+
public static void connectToAudioChannelNoMusic(VoiceChannel channel) {
62+
try {
63+
Guild guild = channel.getGuild();
64+
AudioManager audioManager = guild.getAudioManager();
65+
66+
audioManager.setSelfDeafened(true);
67+
audioManager.setSelfMuted(false);
68+
audioManager.setAutoReconnect(true);
69+
audioManager.setSpeakingMode(SpeakingMode.SOUNDSHARE);
70+
audioManager.setConnectTimeout(30_000);
71+
72+
audioManager.openAudioConnection(channel);
73+
} catch (Exception e) {
74+
LogHelper.error("Failed to connect to voice channel: " + e.getMessage());
75+
}
76+
}
77+
78+
public static void leaveVoiceChannel(Guild guild) {
79+
guild.getAudioManager().closeAudioConnection();
80+
}
81+
82+
public static void registerRemoteSources(AudioPlayerManager playerManager) {
83+
var containerRegistry = MediaContainerRegistry.DEFAULT_REGISTRY;
84+
playerManager.registerSourceManager(new YoutubeAudioSourceManager());
85+
playerManager.registerSourceManager(SoundCloudAudioSourceManager.createDefault());
86+
playerManager.registerSourceManager(new BandcampAudioSourceManager());
87+
playerManager.registerSourceManager(new VimeoAudioSourceManager());
88+
// playerManager.registerSourceManager(new TwitchStreamAudioSourceManager());
89+
playerManager.registerSourceManager(new BeamAudioSourceManager());
90+
playerManager.registerSourceManager(new GetyarnAudioSourceManager());
91+
playerManager.registerSourceManager(new HttpAudioSourceManager(containerRegistry));
92+
}
93+
}

0 commit comments

Comments
 (0)