Version 1.0.0-rc1 - Version initiale (Ajout du serveur, des playlists)

This commit is contained in:
2025-04-20 23:09:46 +02:00
parent 12c4e2740a
commit 812d5c72fa
37 changed files with 1860 additions and 157 deletions

View File

@@ -1,6 +1,9 @@
const { joinVoiceChannel, getVoiceConnection, entersState, VoiceConnectionStatus, createAudioPlayer, AudioPlayerStatus } = require('@discordjs/voice');
const { joinVoiceChannel, getVoiceConnection, VoiceConnectionStatus, createAudioPlayer, AudioPlayerStatus, StreamType, createAudioResource } = require('@discordjs/voice');
const {List} = require('./List')
const {LogType} = require("loguix");
const ffmpeg = require('fluent-ffmpeg')
const fs = require('fs')
const { PassThrough } = require('stream');
const plog = new LogType("Player")
const clog = new LogType("Signal")
@@ -13,11 +16,13 @@ const AllPlayers = new Map()
class Player {
connection;
connected = false;
player;
guildId;
channelId;
queue;
currentResource;
loop = false;
constructor(guildId) {
if(this.guildId === null) {
clog.error("Impossible de créer un Player, car guildId est null")
@@ -39,6 +44,20 @@ class Player {
clog.log(`GUILD : ${this.guildId} - Une connexion existe déjà pour ce serveur`)
return
}
this.joinChannel(channel)
this.player = createAudioPlayer()
this.generatePlayerEvents()
}
isConnected() {
return this.connected
}
joinChannel(channel) {
this.channelId = channel.id
this.connection = joinVoiceChannel({
channelId: channel.id,
guildId: channel.guild.id,
@@ -47,11 +66,6 @@ class Player {
selfMute: false
});
this.channelId = channel.id
this.player = createAudioPlayer()
this.generatePlayerEvents()
this.connection.on('stateChange', (oldState, newState) => {
clog.log(`GUILD : ${this.guildId} - [STATE] OLD : "${oldState.status}" NEW : "${newState.status}"`);
@@ -61,7 +75,8 @@ class Player {
this.leave()
}
});
this.connected = true
process.emit("PLAYERS_UPDATE")
}
generatePlayerEvents() {
@@ -71,20 +86,30 @@ class Player {
this.player.on('error', error => {
plog.error(`GUILD : ${this.guildId} - Une erreur est survenue dans le player`);
plog.error(error);
console.error(error);
process.emit("PLAYERS_UPDATE")
});
this.player.on(AudioPlayerStatus.Idle, () => {
// Si la musique est en boucle, on relance la musique
if(this.loop) {
this.play(this.queue.current)
return
}
// Si la musique n'est pas en boucle, on passe à la musique suivante
Activity.idleActivity()
this.queue.setCurrent(null)
if(this.queue.next.length > 0) {
this.play(this.queue.nextSong())
}
process.emit("PLAYERS_UPDATE")
});
this.player.on(AudioPlayerStatus.Playing, () => {
plog.log(`GUILD : ${this.guildId} - Le player est en train de jouer le contenu suivant : ${this.queue.current.title}`);
Activity.setMusicActivity(this.queue.current.title, this.queue.current.author, this.queue.current.thumbnail)
process.emit("PLAYERS_UPDATE")
});
}
@@ -101,6 +126,45 @@ class Player {
}
}
getState() {
const state = {
current: this.queue.current,
next: this.queue.next,
previous: this.queue.previous,
loop: this.loop,
shuffle: this.queue.shuffle,
paused: this.player.state.status == AudioPlayerStatus.Paused,
playing: this.player.state.status == AudioPlayerStatus.Playing,
duration: this.getDuration(),
playerState: this.player.state.status,
connectionState: this.connection.state.status,
channelId: this.channelId
}
return state
}
async setLoop() {
if(this.checkConnection()) return
this.loop = !this.loop
if(this.loop) {
plog.log(`GUILD : ${this.guildId} - La musique est en boucle`)
} else {
plog.log(`GUILD : ${this.guildId} - La musique n'est plus en boucle`)
}
process.emit("PLAYERS_UPDATE")
}
async setShuffle() {
if(this.checkConnection()) return
this.queue.shuffle = !this.queue.shuffle
if(this.queue.shuffle) {
plog.log(`GUILD : ${this.guildId} - La musique est en mode aléatoire`)
} else {
plog.log(`GUILD : ${this.guildId} - La musique n'est plus en mode aléatoire`)
}
process.emit("PLAYERS_UPDATE")
}
async play(song) {
if(this.checkConnection()) return
if(this.queue.current != null) {
@@ -108,18 +172,31 @@ class Player {
}
this.queue.setCurrent(song)
this.stream = await this.getStream(song)
if(song.type == "attachment") {
media.play(this, song)
}
if(song.type == 'youtube') {
youtube.play(this, song)
}
if(song.type == "soundcloud") {
soundcloud.play(this, song)
}
if(this.stream === null) {
plog.error(`GUILD : ${this.guildId} - Impossible de lire la musique : ${song.title} avec le type : ${song.type}`)
return
}
// TODO: Créer une méthode pour les autres types de médias
this.playStream(this.stream)
plog.log(`GUILD : ${this.guildId} - Lecture de la musique : ${song.title} - Type : ${song.type}`)
}
async getStream(song) {
let stream = null
if(song.type == "attachment") {
stream = await media.getStream(song)
}
if(song.type == 'youtube') {
stream = await youtube.getStream(song)
}
if(song.type == "soundcloud") {
stream = await soundcloud.getStream(song)
}
return stream
}
async add(song) {
@@ -156,6 +233,7 @@ class Player {
plog.log(`GUILD : ${this.guildId} - La musique a été mise en pause`)
return true
}
process.emit("PLAYERS_UPDATE")
}
async leave() {
@@ -170,24 +248,80 @@ class Player {
this.player = null
this.connection = null
this.channelId = null
this.connected = false
Activity.idleActivity()
this.queue.destroy()
AllPlayers.delete(this.guildId)
clog.log("Connection détruite avec le guildId : " + this.guildId)
plog.log("Player détruit avec le guildId : " + this.guildId)
process.emit("PLAYERS_UPDATE")
}
async setDuration(duration) {
if (this.checkConnection()) return;
if (this.queue.current == null) return;
if (this.currentResource == null) return;
const maxDuration = this.queue.current.duration;
if (duration > maxDuration) {
plog.error(`GUILD : ${this.guildId} - La durée demandée dépasse la durée maximale de la musique.`);
return;
}
this.stream = await this.getStream(this.queue.current);
if (this.stream === null) {
plog.error(`GUILD : ${this.guildId} - Impossible de lire la musique : ${this.queue.current.title} avec le type : ${this.queue.current.type}`);
return;
}
// Si stream est un lien, ouvrir le stream à partir du lien
if(typeof this.stream === "string") {
this.stream = fs.createReadStream(this.stream)
}
const passThroughStream = new PassThrough();
ffmpeg(this.stream)
.setStartTime(duration) // Démarrer à la position demandée (en secondes)
.outputOptions('-f', 'mp3') // Specify output format if needed
.on('error', (err) => {
plog.error(`GUILD : ${this.guildId} - Une erreur est survenue avec ffmpeg : ${err.message}`);
})
.pipe(passThroughStream, { end: true });
this.stream = passThroughStream;
this.playStream(this.stream); // Jouer le nouveau flux
this.currentResource.playbackDuration = duration * 1000; // Mettre à jour la durée de lecture du resource
plog.log(`GUILD : ${this.guildId} - Lecture déplacée à ${duration}s.`);
}
setDuration(duration) {
playStream(stream) {
if(this.checkConnection()) return
if(this.player !== null) this.player.stop();
this.player = createAudioPlayer()
this.generatePlayerEvents()
const resource = createAudioResource(stream, { inputType: StreamType.Arbitrary });
this.setCurrentResource(resource)
this.player.play(resource);
this.connection.subscribe(this.player);
process.emit("PLAYERS_UPDATE")
}
getDuration() {
// Return the duration of player
if(this.checkConnection()) return
if(this.queue.current == null) return
if(this.currentResource == null) return
var maxduration = this.queue.current.duration
if(duration > maxduration) return
this.player.stop(); // Arrête la lecture actuelle
this.player.play(this.currentResource, {
startTime: duration * 1000 // Convertit le timecode en millisecondes
});
return this.currentResource.playbackDuration / 1000
}
@@ -195,6 +329,22 @@ class Player {
this.currentResource = value;
}
changeChannel(channel) {
if(this.checkConnection()) return
if(this.connection === null) return
if(this.connection.channelId === channel.id) return
this.connection.destroy()
this.joinChannel(channel)
// Si la musique est en cours de lecture, on la relance avec le bon timecode
if(this.player) {
this.connection.subscribe(this.player);
}
process.emit("PLAYERS_UPDATE")
}
async skip() {
if(this.checkConnection()) return "no_music"
@@ -203,6 +353,7 @@ class Player {
}
const songSkip = this.queue.nextSong()
this.play(songSkip)
process.emit("PLAYERS_UPDATE")
return songSkip
}
@@ -215,11 +366,37 @@ class Player {
const songPrevious = this.queue.previousSong()
this.play(songPrevious)
process.emit("PLAYERS_UPDATE")
return songPrevious
}
}
module.exports = {Player, AllPlayers}
/**
*
* @param {string} guildId
* @returns {Player} player
*/
function getPlayer(guildId) {
if(AllPlayers.has(guildId)) {
return AllPlayers.get(guildId)
} else {
return new Player(guildId)
}
}
function getAllPlayers() {
const players = new Array()
AllPlayers.forEach((player) => {
players.push(player)
})
}
function isPlayer(guildId) {
return AllPlayers.has(guildId)
}
module.exports = {Player, AllPlayers, getPlayer, isPlayer, getAllPlayers}
/*