Ajout de la gestion audio avec les commandes play et stop, et implémentation des gestionnaires de musique
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled

This commit is contained in:
Melaine Gérard 2024-12-24 14:00:07 +01:00
parent 0fedd154ea
commit f858684132
No known key found for this signature in database
GPG Key ID: 89A9588EB2726A53
9 changed files with 309 additions and 1 deletions

View File

@ -1,21 +1,26 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins { plugins {
application
id("java") id("java")
id("com.github.johnrengelman.shadow") version "8.1.1" id("com.github.johnrengelman.shadow") version "8.1.1"
} }
group = "fr.melaine.gerard.kiss.shot.acerola" group = "fr.melaine.gerard.kiss.shot.acerola"
version = "1.0-SNAPSHOT" version = "1.0-SNAPSHOT"
application {
mainClass = "org.camelia.studio.kiss.shot.acerola.KissShotAcerola"
}
repositories { repositories {
mavenCentral() mavenCentral()
maven { setUrl("https://jitpack.io") }
} }
dependencies { dependencies {
implementation("io.github.cdimascio:dotenv-kotlin:6.4.2") implementation("io.github.cdimascio:dotenv-kotlin:6.4.2")
implementation("net.dv8tion:JDA:5.2.1") implementation("net.dv8tion:JDA:5.2.1")
implementation("ch.qos.logback:logback-classic:1.5.12") implementation("ch.qos.logback:logback-classic:1.5.12")
implementation ("dev.arbjerg:lavaplayer:2.2.2")
} }

View File

@ -0,0 +1,36 @@
package org.camelia.studio.kiss.shot.acerola.audio;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame;
import net.dv8tion.jda.api.audio.AudioSendHandler;
import java.nio.Buffer;
import java.nio.ByteBuffer;
public class AudioPlayerSendHandler implements AudioSendHandler {
private final AudioPlayer audioPlayer;
private final ByteBuffer buffer;
private final MutableAudioFrame frame;
public AudioPlayerSendHandler(AudioPlayer audioPlayer) {
this.audioPlayer = audioPlayer;
this.buffer = ByteBuffer.allocate(1024);
this.frame = new MutableAudioFrame();
this.frame.setBuffer(buffer);
}
@Override
public boolean canProvide() {
return audioPlayer.provide(frame);
}
@Override
public ByteBuffer provide20MsAudio() {
((Buffer) buffer).flip();
return buffer;
}
@Override
public boolean isOpus() {
return true;
}
}

View File

@ -0,0 +1,21 @@
package org.camelia.studio.kiss.shot.acerola.audio;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
public class GuildMusicManager {
public final AudioPlayer audioPlayer;
public final TrackScheduler scheduler;
private final AudioPlayerSendHandler sendHandler;
public GuildMusicManager(AudioPlayerManager manager) {
this.audioPlayer = manager.createPlayer();
this.scheduler = new TrackScheduler(this.audioPlayer);
this.audioPlayer.addListener(this.scheduler);
this.sendHandler = new AudioPlayerSendHandler(this.audioPlayer);
}
public AudioPlayerSendHandler getSendHandler() {
return sendHandler;
}
}

View File

@ -0,0 +1,70 @@
package org.camelia.studio.kiss.shot.acerola.audio;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import java.util.HashMap;
import java.util.Map;
public class PlayerManager {
private static PlayerManager INSTANCE;
private final Map<Long, GuildMusicManager> musicManagers;
private final AudioPlayerManager audioPlayerManager;
public PlayerManager() {
this.musicManagers = new HashMap<>();
this.audioPlayerManager = new DefaultAudioPlayerManager();
AudioSourceManagers.registerRemoteSources(audioPlayerManager);
}
public static PlayerManager getInstance() {
if (INSTANCE == null) {
INSTANCE = new PlayerManager();
}
return INSTANCE;
}
public GuildMusicManager getMusicManager(Guild guild) {
return musicManagers.computeIfAbsent(guild.getIdLong(), (guildId) -> {
final GuildMusicManager guildMusicManager = new GuildMusicManager(audioPlayerManager);
guild.getAudioManager().setSendingHandler(guildMusicManager.getSendHandler());
return guildMusicManager;
});
}
public void loadAndPlay(TextChannel channel, String url) {
final GuildMusicManager musicManager = getMusicManager(channel.getGuild());
audioPlayerManager.loadItemOrdered(musicManager, url, new AudioLoadResultHandler() {
@Override
public void trackLoaded(AudioTrack track) {
musicManager.scheduler.queue(track);
channel.sendMessage("Ajout à la file d'attente: `" + track.getInfo().title + "`").queue();
}
@Override
public void playlistLoaded(AudioPlaylist playlist) {
final AudioTrack track = playlist.getTracks().get(0);
musicManager.scheduler.queue(track);
channel.sendMessage("Ajout à la file d'attente: `" + track.getInfo().title + "`").queue();
}
@Override
public void noMatches() {
channel.sendMessage("Aucun résultat trouvé pour: " + url).queue();
}
@Override
public void loadFailed(FriendlyException e) {
channel.sendMessage("Erreur lors du chargement: " + e.getMessage()).queue();
}
});
}
}

View File

@ -0,0 +1,25 @@
package org.camelia.studio.kiss.shot.acerola.audio;
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;
public class TrackScheduler extends AudioEventAdapter {
private final AudioPlayer player;
public TrackScheduler(AudioPlayer player) {
this.player = player;
}
public void queue(AudioTrack track) {
player.startTrack(track, false);
}
@Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
if (endReason.mayStartNext) {
// Ici vous pouvez gérer la lecture de la prochaine piste si vous implémentez une file d'attente
}
}
}

View File

@ -0,0 +1,56 @@
package org.camelia.studio.kiss.shot.acerola.commands.utils;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildVoiceState;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.managers.AudioManager;
import java.util.List;
import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager;
import org.camelia.studio.kiss.shot.acerola.interfaces.ISlashCommand;
public class PlayAudioCommand implements ISlashCommand {
@Override
public String getName() {
return "playaudio";
}
@Override
public String getDescription() {
return "Permet de lancer une musique en .mp3 dans un salon vocal";
}
@Override
public List<OptionData> getOptions() {
return List.of(
new OptionData(OptionType.STRING, "url", "URL de la musique à jouer", true));
}
@Override
public void execute(SlashCommandInteractionEvent event) {
event.deferReply().queue();
String url = event.getOption("url").getAsString();
Member member = event.getMember();
GuildVoiceState voiceState = member.getVoiceState();
if (!member.getVoiceState().inAudioChannel()) {
event.getHook().editOriginal("Vous devez être connecté à un salon vocal pour utiliser cette commande !")
.queue();
return;
}
VoiceChannel channel = voiceState.getChannel().asVoiceChannel();
AudioManager audioManager = event.getGuild().getAudioManager();
audioManager.openAudioConnection(channel);
PlayerManager.getInstance().getMusicManager(event.getGuild()).audioPlayer.setVolume(25);
PlayerManager.getInstance().loadAndPlay(event.getChannel().asTextChannel(), url);
event.getHook().editOriginal("Chargement du fichier audio en cours...").queue();
}
}

View File

@ -0,0 +1,54 @@
package org.camelia.studio.kiss.shot.acerola.commands.utils;
import org.camelia.studio.kiss.shot.acerola.audio.GuildMusicManager;
import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager;
import org.camelia.studio.kiss.shot.acerola.interfaces.ISlashCommand;
import net.dv8tion.jda.api.entities.GuildVoiceState;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.managers.AudioManager;
public class StopCommand implements ISlashCommand{
@Override
public String getName() {
return "stopaudio"
}
@Override
public String getDescription() {
return "Permet de stopper la musique en cours";
}
@Override
public void execute(SlashCommandInteractionEvent event) {
// Vérifier si l'utilisateur est dans un canal vocal
GuildVoiceState voiceState = event.getMember().getVoiceState();
if (!voiceState.inAudioChannel()) {
event.reply("Vous devez être dans un canal vocal pour utiliser cette commande !").queue();
return;
}
// Vérifier si le bot est dans le même canal vocal
AudioManager audioManager = event.getGuild().getAudioManager();
if (!audioManager.isConnected()) {
event.reply("Je ne suis pas connecté à un canal vocal !").queue();
return;
}
if (voiceState.getChannel() != audioManager.getConnectedChannel()) {
event.reply("Vous devez être dans le même canal vocal que moi !").queue();
return;
}
// Arrêter la musique
GuildMusicManager musicManager = PlayerManager.getInstance().getMusicManager(event.getGuild());
musicManager.audioPlayer.stopTrack();
// Déconnecter le bot
audioManager.closeAudioConnection();
event.reply("Musique arrêtée et déconnexion du canal vocal.").queue();
}
}

View File

@ -0,0 +1,38 @@
package org.camelia.studio.kiss.shot.acerola.listeners;
import org.camelia.studio.kiss.shot.acerola.audio.PlayerManager;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.managers.AudioManager;
public class VoiceLeaveListener extends ListenerAdapter {
@Override
public void onGuildVoiceUpdate(GuildVoiceUpdateEvent event) {
Guild guild = event.getGuild();
AudioManager audioManager = guild.getAudioManager();
// Vérifie si le bot est connecté à un canal vocal
if (!audioManager.isConnected()) {
return;
}
VoiceChannel botChannel = audioManager.getConnectedChannel().asVoiceChannel();
// Compte le nombre de membres dans le canal (excluant les bots)
long realMembersCount = botChannel.getMembers().stream()
.filter(member -> !member.getUser().isBot())
.count();
// Si plus personne dans le salon
if (realMembersCount == 0) {
// Arrête la musique
PlayerManager.getInstance().getMusicManager(guild).audioPlayer.stopTrack();
// Déconnecte le bot
audioManager.closeAudioConnection();
}
}
}

View File

@ -4,6 +4,7 @@ import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.camelia.studio.kiss.shot.acerola.listeners.GuildMemberJoinListener; import org.camelia.studio.kiss.shot.acerola.listeners.GuildMemberJoinListener;
import org.camelia.studio.kiss.shot.acerola.listeners.SlashCommandListener; import org.camelia.studio.kiss.shot.acerola.listeners.SlashCommandListener;
import org.camelia.studio.kiss.shot.acerola.listeners.VoiceLeaveListener;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -13,11 +14,13 @@ import java.util.List;
public class ListenerManager { public class ListenerManager {
private final List<ListenerAdapter> listener; private final List<ListenerAdapter> listener;
private final Logger logger = LoggerFactory.getLogger(ListenerManager.class.getName()); private final Logger logger = LoggerFactory.getLogger(ListenerManager.class.getName());
public ListenerManager() { public ListenerManager() {
listener = new ArrayList<>(); listener = new ArrayList<>();
addListener(new SlashCommandListener()); addListener(new SlashCommandListener());
addListener(new GuildMemberJoinListener()); addListener(new GuildMemberJoinListener());
addListener(new VoiceLeaveListener());
} }
public void registerListeners(JDA jda) { public void registerListeners(JDA jda) {