backend-0.2.0 => main #1
4596
backend/package-lock.json
generated
4596
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "chopin-backend",
|
"name": "chopin-backend",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "Discord Bot for music - Fetching everywhere !",
|
"description": "Discord Bot for music - Fetching everywhere !",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
@@ -18,13 +18,21 @@
|
|||||||
"author": "Raphix",
|
"author": "Raphix",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@discordjs/voice": "^0.18.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"discord-player": "^7.1.0",
|
||||||
"discord.js": "^14.18.0",
|
"discord.js": "^14.18.0",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"ffmpeg-static": "^5.2.0",
|
||||||
|
"ffprobe": "^1.1.2",
|
||||||
|
"ffprobe-static": "^3.1.0",
|
||||||
|
"libsodium-wrappers": "^0.7.15",
|
||||||
"loguix": "^1.4.2",
|
"loguix": "^1.4.2",
|
||||||
"nodemon": "^3.1.9",
|
"nodemon": "^3.1.9",
|
||||||
|
"pm2": "^5.4.3",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"webmetrik": "^0.1.4"
|
"webmetrik": "^0.1.4",
|
||||||
|
"ytdl-core": "^4.11.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -53,10 +53,18 @@ class Command {
|
|||||||
})
|
})
|
||||||
SlashCommand.addStringOption(option => option.setName(SelOption.name).setDescription(SelOption.description).setRequired(SelOption.required).addChoices(choices))
|
SlashCommand.addStringOption(option => option.setName(SelOption.name).setDescription(SelOption.description).setRequired(SelOption.required).addChoices(choices))
|
||||||
}
|
}
|
||||||
|
if(SelOption.type === "FILE") {
|
||||||
|
SlashCommand.addAttachmentOption(option => option.setName(SelOption.name).setDescription(SelOption.description).setRequired(SelOption.required))
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {SlashCommandBuilder}
|
||||||
|
* @param {Client} client
|
||||||
|
* @param {Interaction} interaction
|
||||||
|
*/
|
||||||
this.data = {data: SlashCommand, async execute(client, interaction) {callback(client, interaction)}}
|
this.data = {data: SlashCommand, async execute(client, interaction) {callback(client, interaction)}}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -50,10 +50,6 @@ client.commands = new Collection()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dlog.step.end("d_commands_refresh")
|
dlog.step.end("d_commands_refresh")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// And of course, make sure you catch and log any errors!
|
// And of course, make sure you catch and log any errors!
|
||||||
|
@@ -18,7 +18,7 @@ const command = new Command("help", "Affiche la liste des commandes", (client, i
|
|||||||
option.choices.forEach(choice => {
|
option.choices.forEach(choice => {
|
||||||
choices.push(choice.name)
|
choices.push(choice.name)
|
||||||
})
|
})
|
||||||
CommandName += " " + choices.join(" | ")
|
CommandName += " <" + choices.join(" | ") +">"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
51
backend/src/discord/Commands/Media.js
Normal file
51
backend/src/discord/Commands/Media.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const {Command} = require('../Command');
|
||||||
|
const {Embed, EmbedError} = require('../Embed');
|
||||||
|
const { Player } = require('../../player/Player');
|
||||||
|
const { Song } = require('../../player/Song');
|
||||||
|
|
||||||
|
const command = new Command("media", "Lire un média MP3/WAV dans un salon vocal", async (client, interaction) => {
|
||||||
|
|
||||||
|
if(!interaction.member.voice.channel) return interaction.reply({content:"Vous devez rejoindre un salon vocal pour lire un(e) titre / playlist !", ephemeral: true})
|
||||||
|
|
||||||
|
const media = interaction.options.get("media")
|
||||||
|
const now = interaction.options.getBoolean("now") || false
|
||||||
|
|
||||||
|
if(media.attachment.contentType != "audio/mpeg" && media.attachment.contentType != "audio/wav") return new EmbedError("Le média doit être un fichier audio MP3 ou WAV !").send(interaction)
|
||||||
|
|
||||||
|
const channel = interaction.member.voice.channel
|
||||||
|
const song = new Song()
|
||||||
|
await song.processMedia(media, interaction.user.username)
|
||||||
|
|
||||||
|
const player = new Player(channel.guildId)
|
||||||
|
player.join(channel)
|
||||||
|
|
||||||
|
const embed = new Embed()
|
||||||
|
embed.setColor(0x15e6ed)
|
||||||
|
|
||||||
|
|
||||||
|
if(now) {
|
||||||
|
player.play(song)
|
||||||
|
embed.setTitle('**Lecture immédiate**')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
player.add(song)
|
||||||
|
embed.setTitle('**Ajout à liste de lecture**')
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
embed.setDescription('**Titre : **' + song.title)
|
||||||
|
embed.addField('**Durée : **'+ song.readduration, "")
|
||||||
|
embed.addField("**Artiste : **" + song.author, "")
|
||||||
|
embed.addField('**Demandé par **' + interaction.member.user.username,"")
|
||||||
|
embed.setThumbnail(song.thumbnail)
|
||||||
|
|
||||||
|
|
||||||
|
embed.send(interaction)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}, [{type: "FILE", name: "media", description: "Fichier audio à lire", required: true},
|
||||||
|
{type:"BOOLEAN", name: "now", description: "Lire le média maintenant", required: false}]
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = {command}
|
24
backend/src/discord/Commands/Pause.js
Normal file
24
backend/src/discord/Commands/Pause.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const {Command} = require("../Command")
|
||||||
|
const {Embed, EmbedError} = require("../Embed")
|
||||||
|
const { Player } = require("../../player/Player")
|
||||||
|
|
||||||
|
const command = new Command("pause", "Mettre en pause la musique en cours", (client, interaction) => {
|
||||||
|
|
||||||
|
|
||||||
|
if(!interaction.member.voice.channel) return new EmbedError("Vous devez rejoindre un salon vocal pour mettre en pause la musique !").send(interaction)
|
||||||
|
|
||||||
|
const channel = interaction.member.voice.channel
|
||||||
|
const player = new Player(channel.guildId)
|
||||||
|
player.pause()
|
||||||
|
|
||||||
|
// Réponse en embed
|
||||||
|
|
||||||
|
const embed = new Embed()
|
||||||
|
embed.setColor(0x00ff66)
|
||||||
|
embed.setTitle('Musique en pause')
|
||||||
|
embed.setDescription("La musique a été mise en pause")
|
||||||
|
embed.send(interaction)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = {command}
|
15
backend/src/discord/Commands/Play.js
Normal file
15
backend/src/discord/Commands/Play.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const { Command } = require("../Command");
|
||||||
|
const { Embed, EmbedError } = require("../Embed");
|
||||||
|
const { Player } = require("../../player/Player");
|
||||||
|
|
||||||
|
const command = new Command("play", "Jouer une musique à partir d'un lien dans un salon vocal", (client, interaction) => {
|
||||||
|
|
||||||
|
if(!interaction.member.voice.channel) return new EmbedError("Vous devez rejoindre un salon vocal pour jouer une musique !").send(interaction)
|
||||||
|
|
||||||
|
const url = interaction.options.get("url")
|
||||||
|
const channel = interaction.member.voice.channel
|
||||||
|
|
||||||
|
}, [{type: "STRING", name: "url", description: "Lien audio (Youtube / Soundclound / Spotify)", required: true}]
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = {command}
|
20
backend/src/discord/Commands/Restart.js
Normal file
20
backend/src/discord/Commands/Restart.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const {Embed} = require("../Embed")
|
||||||
|
const {Command} = require("../Command")
|
||||||
|
const {restart} = require("../../utils/Maintenance")
|
||||||
|
|
||||||
|
// Nécéssite une raison pour redémarrer le bot
|
||||||
|
|
||||||
|
const command = new Command("restart", "Redémarre le bot", (client, interaction) => {
|
||||||
|
const reason = interaction.options.getString("reason")
|
||||||
|
restart(reason)
|
||||||
|
const embed = new Embed()
|
||||||
|
embed.setColor(150, 20, 20)
|
||||||
|
embed.setTitle('Redémarrage')
|
||||||
|
embed.setDescription("Veuillez patientez, le bot va redémarrer dans un instant ! :arrows_counterclockwise:")
|
||||||
|
embed.addField('Raison', reason)
|
||||||
|
embed.send(interaction)
|
||||||
|
},
|
||||||
|
[{type: "STRING", name: "reason", description: "Raison du redémarrage", required: true}]
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = {command}
|
@@ -86,4 +86,14 @@ class Embed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {Embed}
|
class EmbedError extends Embed {
|
||||||
|
constructor(message) {
|
||||||
|
super()
|
||||||
|
this.setColor(150, 20, 20)
|
||||||
|
this.setTitle('Erreur')
|
||||||
|
this.setThumbnail("https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Dialog-error-round.svg/2048px-Dialog-error-round.svg.png")
|
||||||
|
this.setDescription(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Embed, EmbedError}
|
0
backend/src/player/Finder.js
Normal file
0
backend/src/player/Finder.js
Normal file
173
backend/src/player/List.js
Normal file
173
backend/src/player/List.js
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
const { Database } = require('../utils/Database/Database')
|
||||||
|
const { __glob } = require('../utils/GlobalVars')
|
||||||
|
const PreviousDB = new Database("previous", __glob.PREVIOUSFILE, {})
|
||||||
|
const {LogType} = require("loguix")
|
||||||
|
const clog = new LogType("List")
|
||||||
|
|
||||||
|
const AllLists = new Map() // Map<guildId, List>
|
||||||
|
|
||||||
|
class List {
|
||||||
|
next;
|
||||||
|
current;
|
||||||
|
shuffle;
|
||||||
|
guildId;
|
||||||
|
constructor(guildId) {
|
||||||
|
if(guildId === null) {
|
||||||
|
clog.error("Impossible de créer une liste, car guildId est null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(AllLists.has(guildId)) {
|
||||||
|
return AllLists.get(guildId)
|
||||||
|
}
|
||||||
|
// Add PreviousDB.data[this.guildId]
|
||||||
|
if(PreviousDB.data[guildId] === undefined) {
|
||||||
|
PreviousDB.data[guildId] = new Array()
|
||||||
|
savePrevious()
|
||||||
|
}
|
||||||
|
AllLists.set(guildId, this)
|
||||||
|
this.next = new Array();
|
||||||
|
this.current = null;
|
||||||
|
this.shuffle = false;
|
||||||
|
this.guildId = guildId;
|
||||||
|
}
|
||||||
|
getNext() {
|
||||||
|
return this.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextSong() {
|
||||||
|
if(this.next.length > 0) {
|
||||||
|
return this.next[0];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextSong() {
|
||||||
|
var song = null;
|
||||||
|
if(!this.shuffle) {
|
||||||
|
song = this.next[0]
|
||||||
|
this.next.splice(0, 1)
|
||||||
|
} else {
|
||||||
|
const randomIndex = Math.floor(Math.random() * this.next.length);
|
||||||
|
song = this.next[randomIndex]
|
||||||
|
this.next.splice(randomIndex, 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
return song
|
||||||
|
}
|
||||||
|
|
||||||
|
clearNext() {
|
||||||
|
this.next = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
addNextSong(song) {
|
||||||
|
this.next.push(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNextByIndex(index) {
|
||||||
|
this.next.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
moveSongToUpNext(index) {
|
||||||
|
const song = this.next[index]
|
||||||
|
this.next.splice(index, 1)
|
||||||
|
this.next.unshift(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrevious() {
|
||||||
|
return PreviousDB.data[this.guildId];
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreviousSong() {
|
||||||
|
if(PreviousDB.data[this.guildId].length > 0) {
|
||||||
|
const song = PreviousDB.data[this.guildId][0]
|
||||||
|
PreviousDB.data[this.guildId].splice(0, 1)
|
||||||
|
savePrevious()
|
||||||
|
return song
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearPrevious() {
|
||||||
|
PreviousDB.data[this.guildId] = new Array();
|
||||||
|
savePrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
addPreviousSongToNextByIndex(index) {
|
||||||
|
const song = PreviousDB.data[this.guildId][index]
|
||||||
|
this.next.push(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
addPreviousSong(song) {
|
||||||
|
PreviousDB.data[this.guildId].unshift(song)
|
||||||
|
savePrevious()
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrent() {
|
||||||
|
return this.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrent(value) {
|
||||||
|
this.current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.clearNext();
|
||||||
|
this.current = null
|
||||||
|
this.shuffle = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setShuffle(value) {
|
||||||
|
this.shuffle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
isShuffle() {
|
||||||
|
return this.shuffle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play the song with the index in the queue and delete it from the queue
|
||||||
|
playByIndex(index, typelist) {
|
||||||
|
|
||||||
|
var index = data[0]
|
||||||
|
var list = data[1]
|
||||||
|
|
||||||
|
if(typelist == ListType.NEXT) {
|
||||||
|
|
||||||
|
const song = this.next[index]
|
||||||
|
this.next.splice(index, 1)
|
||||||
|
|
||||||
|
return song
|
||||||
|
|
||||||
|
} else if(typelist == ListType.PREVIOUS) {
|
||||||
|
|
||||||
|
const song = PreviousDB.data[this.guildId][index]
|
||||||
|
return song
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ListType = {
|
||||||
|
NEXT: "0",
|
||||||
|
PREVIOUS: "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
function savePrevious() {
|
||||||
|
|
||||||
|
for(const guildId in PreviousDB.data) {
|
||||||
|
if(PreviousDB.data[guildId].length > 50) {
|
||||||
|
PreviousDB.data[guildId].splice(50, PreviousDB.data[guildId].length - 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviousDB.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { List, ListType }
|
18
backend/src/player/Method/Media.js
Normal file
18
backend/src/player/Method/Media.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const {createAudioResource, VoiceConnectionStatus} = require('@discordjs/voice');
|
||||||
|
const {LogType} = require('loguix')
|
||||||
|
const clog = new LogType("Media")
|
||||||
|
const plog = require("loguix").getInstance("Player")
|
||||||
|
|
||||||
|
async function play(instance, song) {
|
||||||
|
//const resource = await song.getResource()
|
||||||
|
const resource = createAudioResource(song.url)
|
||||||
|
console.log(resource)
|
||||||
|
// Wait until connection is ready
|
||||||
|
instance.connection.once(VoiceConnectionStatus.Ready, async () => {
|
||||||
|
instance.player.play(resource)
|
||||||
|
})
|
||||||
|
|
||||||
|
plog.log(`GUILD : ${instance.guildId} - Lecture de la musique : ${song.title}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {play}
|
126
backend/src/player/Player.js
Normal file
126
backend/src/player/Player.js
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
const { joinVoiceChannel, getVoiceConnection, entersState, VoiceConnectionStatus, createAudioPlayer, AudioPlayerStatus } = require('@discordjs/voice');
|
||||||
|
const {List} = require('./List')
|
||||||
|
const {LogType} = require("loguix");
|
||||||
|
|
||||||
|
const plog = new LogType("Player")
|
||||||
|
const clog = new LogType("Signal")
|
||||||
|
|
||||||
|
const media = require('./Method/Media');
|
||||||
|
|
||||||
|
const AllPlayers = new Map()
|
||||||
|
|
||||||
|
class Player {
|
||||||
|
connection;
|
||||||
|
player;
|
||||||
|
guildId;
|
||||||
|
queue;
|
||||||
|
constructor(guildId) {
|
||||||
|
if(this.guildId === null) {
|
||||||
|
clog.error("Impossible de créer un Player, car guildId est null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(AllPlayers.has(guildId)) {
|
||||||
|
return AllPlayers.get(guildId)
|
||||||
|
}
|
||||||
|
this.connection = null
|
||||||
|
this.player = null
|
||||||
|
this.guildId = guildId
|
||||||
|
this.queue = new List(guildId)
|
||||||
|
AllPlayers.set(guildId, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async join(channel) {
|
||||||
|
|
||||||
|
if(getVoiceConnection(channel.guild.id)) {
|
||||||
|
clog.log(`GUILD : ${this.guildId} - Une connexion existe déjà pour ce serveur`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.connection = joinVoiceChannel({
|
||||||
|
channelId: channel.id,
|
||||||
|
guildId: channel.guild.id,
|
||||||
|
adapterCreator: channel.guild.voiceAdapterCreator,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player = createAudioPlayer()
|
||||||
|
|
||||||
|
this.connection.subscribe(this.player)
|
||||||
|
|
||||||
|
this.connection.on('stateChange', (oldState, newState) => {
|
||||||
|
clog.log(`GUILD : ${this.guildId} - [STATE] OLD : "${oldState.status}" NEW : "${newState.status}"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player.on('error', error => {
|
||||||
|
plog.error(`GUILD : ${this.guildId} - Une erreur est survenue dans le player`);
|
||||||
|
plog.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player.on(AudioPlayerStatus.Idle, () => {
|
||||||
|
if(this.queue.next.length > 0) {
|
||||||
|
//TODO : Play next song
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player.on(AudioPlayerStatus.Playing, () => {
|
||||||
|
plog.log(`GUILD : ${this.guildId} - Le player est en train de jouer le contenu suivant : ${this.queue.current.title}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkConnection() {
|
||||||
|
if(this.connection === null) {
|
||||||
|
clog.error(`GUILD : ${this.guildId} - La connection n'est pas définie`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if(this.player === null) {
|
||||||
|
plog.error(`GUILD : ${this.guildId} - Le player n'est pas défini`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async play(song) {
|
||||||
|
if(song.type = "attachment") {
|
||||||
|
media.play(this, song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(song) {
|
||||||
|
if(this.player.state.status = AudioPlayerStatus.Idle && this.queue.current === null && this.queue.next.length === 0) {
|
||||||
|
this.play(song)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue.addNextSong(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
async pause() {
|
||||||
|
if(this.player.state.status = AudioPlayerStatus.Paused) {
|
||||||
|
this.player.unpause()
|
||||||
|
} else {
|
||||||
|
this.player.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async leave() {
|
||||||
|
// Détruit la connection et le player et l'enlève de la liste des
|
||||||
|
this.connection.destroy()
|
||||||
|
this.player.stop()
|
||||||
|
AllPlayers.delete(this.guildId)
|
||||||
|
clog.log("Connection détruite avec le guildId : " + this.guildId)
|
||||||
|
plog.log("Player détruit avec le guildId : " + this.guildId)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Player}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
You can access created connections elsewhere in your code without having to track the connections yourself. It is best practice to not track the voice connections yourself as you may forget to clean them up once they are destroyed, leading to memory leaks.
|
||||||
|
|
||||||
|
const connection = getVoiceConnection(myVoiceChannel.guild.id);
|
||||||
|
|
||||||
|
*/
|
77
backend/src/player/Song.js
Normal file
77
backend/src/player/Song.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
const {LogType} = require('loguix')
|
||||||
|
const { createAudioResource, StreamType } = require('@discordjs/voice');
|
||||||
|
const ffprobe = require('ffprobe');
|
||||||
|
const ffprobeStatic = require('ffprobe-static');
|
||||||
|
const { getReadableDuration } = require('../utils/TimeConverter');
|
||||||
|
|
||||||
|
const clog = new LogType("Song")
|
||||||
|
|
||||||
|
class Song {
|
||||||
|
title = "Aucun titre";
|
||||||
|
author = "Auteur inconnu"
|
||||||
|
url;
|
||||||
|
thumbnail;
|
||||||
|
duration;
|
||||||
|
readduration;
|
||||||
|
type;
|
||||||
|
|
||||||
|
async processMedia(media, provider) {
|
||||||
|
if(provider) this.author = provider
|
||||||
|
// Check if media is a file or a link
|
||||||
|
if(media.attachment) {
|
||||||
|
this.url = media.attachment.url
|
||||||
|
this.type = "attachment"
|
||||||
|
|
||||||
|
// In face, duration is null, get the metadata of the file to get the duration
|
||||||
|
await getMediaInformation(this, media)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
clog.error("Impossible de traiter le média")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getResource() {
|
||||||
|
|
||||||
|
const resource = createAudioResource(this.url, {
|
||||||
|
inputType: StreamType.Arbitrary
|
||||||
|
})
|
||||||
|
return resource
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Song}
|
||||||
|
|
||||||
|
async function getMediaInformation(instance, media, provider) {
|
||||||
|
try {
|
||||||
|
const info = await ffprobe(media.attachment.url, { path: ffprobeStatic.path });
|
||||||
|
if (info.streams?.[0]?.duration_ts) {
|
||||||
|
instance.duration = info.streams[0].duration;
|
||||||
|
instance.readduration = getReadableDuration(instance.duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérification pour éviter une erreur si `streams[0]` ou `tags` n'existe pas
|
||||||
|
instance.thumbnail = info.streams?.[0]?.tags?.thumbnail ??
|
||||||
|
"https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png";
|
||||||
|
|
||||||
|
// Obtenir le titre (sinon utiliser le nom du fichier)
|
||||||
|
instance.title = info.streams?.[0]?.tags?.title ?? media.attachment.name;
|
||||||
|
|
||||||
|
// Obtenir l'auteur (s'il existe)
|
||||||
|
instance.author = info.streams?.[0]?.tags?.artist ?? instance.author;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
clog.error("Impossible de récupérer les informations de la musique : " + this.name)
|
||||||
|
clog.error(err)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@@ -62,6 +62,9 @@ class Database {
|
|||||||
clog.error(e)
|
clog.error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assure that the database is up to date and reloaded
|
||||||
|
this.update()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
@@ -8,7 +8,8 @@ const __glob = {
|
|||||||
LOGS: root + path.sep + "logs",
|
LOGS: root + path.sep + "logs",
|
||||||
DATA: root + path.sep + "data",
|
DATA: root + path.sep + "data",
|
||||||
COMMANDS: root + path.sep + "src" + path.sep + "discord" + path.sep + "commands",
|
COMMANDS: root + path.sep + "src" + path.sep + "discord" + path.sep + "commands",
|
||||||
METRIC_FILE: root + path.sep + "data" + path.sep + "metrics.json"
|
METRIC_FILE: root + path.sep + "data" + path.sep + "metrics.json",
|
||||||
|
PREVIOUSFILE: root + path.sep + "data" + path.sep + "previous.json",
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {__glob}
|
module.exports = {__glob}
|
11
backend/src/utils/Maintenance.js
Normal file
11
backend/src/utils/Maintenance.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const pm2 = require("pm2")
|
||||||
|
const { LogType } = require("loguix")
|
||||||
|
const clog = new LogType("Maintenance")
|
||||||
|
|
||||||
|
function restart(reason) {
|
||||||
|
clog.warn("Redémarrage du serveur Subsonics")
|
||||||
|
clog.warn(`Reason: ${reason}`)
|
||||||
|
pm2.restart("Subsonics")
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {restart}
|
32
backend/src/utils/TimeConverter.js
Normal file
32
backend/src/utils/TimeConverter.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
function getReadableDuration(duration) {
|
||||||
|
var max = ""
|
||||||
|
duration *= 1000;
|
||||||
|
|
||||||
|
const maxhours = Math.floor(duration / 3600000);
|
||||||
|
|
||||||
|
var maxmin = Math.trunc(duration / 60000) - (Math.floor(duration / 60000 / 60) * 60);
|
||||||
|
var maxsec = Math.floor(duration / 1000) - (Math.floor(duration / 1000 / 60) * 60);
|
||||||
|
|
||||||
|
|
||||||
|
if (maxsec < 10) {
|
||||||
|
maxsec = `0${maxsec}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(maxhours != 0) {
|
||||||
|
|
||||||
|
if (maxmin < 10) {
|
||||||
|
maxmin = `0${maxmin}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
max = maxhours + "h" + maxmin + "m" + maxsec
|
||||||
|
} else {
|
||||||
|
max = maxmin + "m" + maxsec + "s"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {getReadableDuration}
|
Reference in New Issue
Block a user