Merge pull request 'backend-0.2.0 => main' (#1) from backend-0.2.0 into main
Reviewed-on: #1
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -141,4 +141,6 @@ docs/_book
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
|
||||
data/
|
||||
data/
|
||||
|
||||
__DEBUG.js
|
4896
backend/package-lock.json
generated
4896
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chopin-backend",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Discord Bot for music - Fetching everywhere !",
|
||||
"main": "src/main.js",
|
||||
"nodemonConfig": {
|
||||
@@ -18,13 +18,25 @@
|
||||
"author": "Raphix",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.18.0",
|
||||
"@distube/ytdl-core": "^4.11.5",
|
||||
"@distube/ytsr": "2.0.4",
|
||||
"cors": "^2.8.5",
|
||||
"discord-player": "^7.1.0",
|
||||
"discord.js": "^14.18.0",
|
||||
"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",
|
||||
"nodemon": "^3.1.9",
|
||||
"pm2": "^5.4.3",
|
||||
"socket.io": "^4.8.1",
|
||||
"soundcloud.ts": "^0.6.3",
|
||||
"spotify-web-api-node": "^5.0.2",
|
||||
"uuid": "^11.1.0",
|
||||
"webmetrik": "^0.1.4"
|
||||
"webmetrik": "^0.1.4",
|
||||
"ytfps": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
@@ -13,10 +13,7 @@ function setMusicActivity(songName, artistName, imageUrl) {
|
||||
const client = bot.getClient()
|
||||
client.user.setActivity(`${songName} - ${artistName}`,{
|
||||
type: ActivityType.Listening,
|
||||
/*assets: {
|
||||
largeImage: imageUrl,
|
||||
largeText: songName
|
||||
}*/
|
||||
url: imageUrl
|
||||
});
|
||||
dlog.log(`Activité mise à jour : ${songName} - ${artistName}`)
|
||||
}
|
||||
|
@@ -5,9 +5,13 @@ const { __glob } = require("../utils/GlobalVars")
|
||||
const { LogType } = require("loguix")
|
||||
const config = require("../utils/Database/Configuration")
|
||||
const metric = require("webmetrik")
|
||||
const { Player } = require("../player/Player")
|
||||
|
||||
const dlog = new LogType("Discord")
|
||||
|
||||
const membersVoices = new Map()
|
||||
const timers = new Map()
|
||||
|
||||
const client = new Client({
|
||||
intents:[GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers],
|
||||
})
|
||||
@@ -20,7 +24,7 @@ function getClient() {
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
|
||||
client.once('ready', () => {
|
||||
dlog.log("Connexion au Bot Discord réussi ! Connecté en tant que : " + client.user.tag)
|
||||
|
||||
@@ -68,6 +72,44 @@ function init() {
|
||||
}
|
||||
})
|
||||
|
||||
client.on("voiceStateUpdate", (oldMember, newMember) => {
|
||||
membersVoices.set(newMember.id, {
|
||||
guildId: newMember.guild.id,
|
||||
channelId: newMember.channelId,
|
||||
})
|
||||
|
||||
const player = new Player(newMember.guild.id)
|
||||
|
||||
if(player.connection && player.channelId) {
|
||||
client.channels.fetch(player.channelId).then(channel => {
|
||||
|
||||
if(channel.members.size <= 1) {
|
||||
|
||||
// If the player is alone in the channel, we will destroy it in 10 minutes
|
||||
// 10 minutes = 600000 ms
|
||||
// 10 second = 10000 ms
|
||||
timers.set(newMember.guild.id, setTimeout(() => {
|
||||
const getPlayer = new Player(newMember.guild.id)
|
||||
if(getPlayer.connection && player.channelId) {
|
||||
getPlayer.leave()
|
||||
dlog.log("[Automatic Task] Guild Id :" + newMember.guild.id + " - Player supprimé : " + channel.name)
|
||||
}
|
||||
|
||||
}, 10000))
|
||||
dlog.log("[Automatic Task] Guild Id :" + newMember.guild.id + " - Player supprimé dans 10 minutess : " + channel.name)
|
||||
} else {
|
||||
dlog.log("[Automatic Task] Guild Id :" + newMember.guild.id + " - Player n'est pas seul dans le channel : " + channel.name)
|
||||
clearTimeout(timers.get(newMember.guild.id))
|
||||
timers.delete(newMember.guild.id)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
client.login(config.getToken())
|
||||
|
@@ -53,10 +53,18 @@ class Command {
|
||||
})
|
||||
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)}}
|
||||
|
||||
}
|
||||
|
@@ -50,10 +50,6 @@ client.commands = new Collection()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
dlog.step.end("d_commands_refresh")
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
|
@@ -11,7 +11,7 @@ const command = new Command("about", "Affiche des informations sur le bot", (cli
|
||||
const seconds = Math.floor(uptime % 60);
|
||||
|
||||
const embed = new Embed()
|
||||
embed.setColor(0xb0f542)
|
||||
embed.setColor(237, 12, 91)
|
||||
embed.setThumbnail("https://cdn.discordapp.com/avatars/" + client.user.id + "/" + client.user.avatar + ".png")
|
||||
embed.setTitle('Subsonics - Chopin')
|
||||
embed.addField('Informations',"")
|
||||
|
@@ -18,7 +18,7 @@ const command = new Command("help", "Affiche la liste des commandes", (client, i
|
||||
option.choices.forEach(choice => {
|
||||
choices.push(choice.name)
|
||||
})
|
||||
CommandName += " " + choices.join(" | ")
|
||||
CommandName += " <" + choices.join(" | ") +">"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
28
backend/src/discord/Commands/Leave.js
Normal file
28
backend/src/discord/Commands/Leave.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const {Command} = require("../Command")
|
||||
const {Embed, EmbedError} = require("../Embed")
|
||||
const {Player, AllPlayers} = require("../../player/Player")
|
||||
|
||||
const command = new Command("leave", "Quitter le salon vocal", (client, interaction) => {
|
||||
|
||||
if(!interaction.member.voice.channel) return new EmbedError("Vous devez rejoindre un salon vocal pour arrêter le bot !").send(interaction)
|
||||
const channel = interaction.member.voice.channel
|
||||
var embed = new Embed()
|
||||
if(AllPlayers.has(channel.guildId)) {
|
||||
const player = AllPlayers.get(channel.guildId)
|
||||
player.leave()
|
||||
|
||||
|
||||
embed.setColor(200, 20, 20)
|
||||
embed.setTitle('**Déconnexion**')
|
||||
embed.setDescription('Déconnexion du salon vocal')
|
||||
embed.setThumbnail("https://www.iconsdb.com/icons/download/white/phone-51-64.png")
|
||||
|
||||
} else {
|
||||
|
||||
embed = new EmbedError("Le bot n'est pas connecté à ce salon vocal")
|
||||
}
|
||||
embed.send(interaction)
|
||||
|
||||
})
|
||||
|
||||
module.exports = {command}
|
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 new EmbedError("Vous devez rejoindre un salon vocal pour jouer un média !").send(interaction, 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}
|
44
backend/src/discord/Commands/Pause.js
Normal file
44
backend/src/discord/Commands/Pause.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const {Command} = require("../Command")
|
||||
const {Embed, EmbedError} = require("../Embed")
|
||||
const { Player } = require("../../player/Player")
|
||||
|
||||
const command = new Command("pause", "Mettre en pause / Reprendre 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)
|
||||
const result = player.pause()
|
||||
|
||||
|
||||
var embed = new Embed()
|
||||
embed.setColor(0x03ff2d)
|
||||
|
||||
result.then((pause) => {
|
||||
|
||||
if(pause == "no_music") {
|
||||
embed = new EmbedError("Il n'y a pas de musique en cours de lecture")
|
||||
|
||||
} else if(pause) {
|
||||
embed.setTitle('Musique en pause')
|
||||
embed.setDescription("La musique a été mise en pause")
|
||||
embed.setThumbnail("https://www.iconsdb.com/icons/download/white/pause-64.png")
|
||||
|
||||
|
||||
} else {
|
||||
embed.setTitle('Musique reprise')
|
||||
embed.setDescription("La musique a été reprise")
|
||||
embed.setThumbnail("https://www.iconsdb.com/icons/download/white/play-64.png")
|
||||
}
|
||||
|
||||
embed.send(interaction)
|
||||
})
|
||||
|
||||
// Réponse en embed
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
module.exports = {command}
|
94
backend/src/discord/Commands/Play.js
Normal file
94
backend/src/discord/Commands/Play.js
Normal file
@@ -0,0 +1,94 @@
|
||||
const { Command } = require("../Command");
|
||||
const { Embed, EmbedError } = require("../Embed");
|
||||
const { Player } = require("../../player/Player");
|
||||
const Finder = require("../../player/Finder");
|
||||
const { Playlist } = require("../../player/Playlist");
|
||||
const spotify = require("../../media/SpotifyInformation");
|
||||
|
||||
const command = new Command("play", "Jouer une musique à partir d'un lien dans un salon vocal", async (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
|
||||
const now = interaction.options.getBoolean("now") || false
|
||||
await Finder.search(url.value).then(async (song) => {
|
||||
if(!song) return new EmbedError("Impossible de trouver la musique à partir du lien donné ou des mots clés donnés").send(interaction)
|
||||
|
||||
const player = new Player(channel.guildId)
|
||||
player.join(channel)
|
||||
|
||||
const embed = new Embed()
|
||||
embed.setColor(0x15e6ed)
|
||||
|
||||
// Check if song is playlist
|
||||
if(song instanceof Playlist) {
|
||||
|
||||
embed.setDescription('**Playlist : **' + song.songs.length + ' musiques')
|
||||
embed.addField('**Titre : **' + song.title, "")
|
||||
embed.addField('**Demandé par : **', interaction.member.user.username,)
|
||||
embed.addField('**Auteur : **', song.author)
|
||||
embed.addField('**Provient de : **', song.type.replace(/^\w/, (c) => c.toUpperCase()))
|
||||
if(!song.type == "spotify") {
|
||||
embed.addField('**Durée : **', song.readduration)
|
||||
}
|
||||
embed.addField('**Lien : **', song.url)
|
||||
embed.addField(":warning: La récupération des musiques peut prendre du temps", "Veuillez patienter ... et éviter de lancer d'autres commandes")
|
||||
|
||||
|
||||
embed.setThumbnail(song.thumbnail)
|
||||
|
||||
} else {
|
||||
|
||||
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)
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(now) {
|
||||
embed.setTitle("Lecture immédiate")
|
||||
} else {
|
||||
embed.setTitle("Ajoutée à la file d'attente")
|
||||
}
|
||||
|
||||
embed.send(interaction)
|
||||
|
||||
if(song instanceof Playlist) {
|
||||
if(song.type == "spotify") {
|
||||
song = await spotify.getTracks(song)
|
||||
}
|
||||
if(now) {
|
||||
player.readPlaylist(song, true)
|
||||
|
||||
} else {
|
||||
player.readPlaylist(song)
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
if(now) {
|
||||
|
||||
player.play(song)
|
||||
|
||||
|
||||
} else {
|
||||
player.add(song)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
}, [{type: "STRING", name: "url", description: "Recherche / Lien audio (Youtube / Soundclound / Spotify)", required: true},
|
||||
{type:"BOOLEAN", name: "now", description: "Lire le média maintenant", required: false}]
|
||||
)
|
||||
|
||||
module.exports = {command}
|
52
backend/src/discord/Commands/Previous.js
Normal file
52
backend/src/discord/Commands/Previous.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const {Command} = require("../Command")
|
||||
const {Embed, EmbedError} = require("../Embed")
|
||||
const { Player, AllPlayers } = require("../../player/Player")
|
||||
|
||||
const command = new Command("previous", "Passe à la musique précédente", (client, interaction) => {
|
||||
|
||||
|
||||
if(!interaction.member.voice.channel) return new EmbedError("Vous devez rejoindre un salon vocal pour passer à la musique suivante !").send(interaction)
|
||||
|
||||
const channel = interaction.member.voice.channel
|
||||
|
||||
if(AllPlayers.has(channel.guildId)) {
|
||||
|
||||
const player = new Player(channel.guildId)
|
||||
const result = player.previous()
|
||||
|
||||
|
||||
var embed = new Embed()
|
||||
embed.setColor(0x15e6ed)
|
||||
|
||||
result.then((song) => {
|
||||
|
||||
if(song == "no_music") {
|
||||
embed = new EmbedError("Il n'y a pas de musique précédemment jouée")
|
||||
|
||||
} else if(song) {
|
||||
|
||||
// Result is a song
|
||||
|
||||
|
||||
|
||||
embed.setTitle('**Musique précédente !**')
|
||||
embed.setDescription('**Titre : **' + song.title)
|
||||
embed.addField('**Durée : **'+ song.readduration, "")
|
||||
embed.addField("**Artiste : **" + song.author, "")
|
||||
embed.setThumbnail(song.thumbnail)
|
||||
|
||||
|
||||
}
|
||||
|
||||
embed.send(interaction)
|
||||
})
|
||||
|
||||
} else {
|
||||
return new EmbedError("Le bot n'est pas connecté").send(interaction)
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
module.exports = {command}
|
42
backend/src/discord/Commands/Queue.js
Normal file
42
backend/src/discord/Commands/Queue.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const {Command} = require("../Command")
|
||||
const {Embed, EmbedError} = require("../Embed")
|
||||
const { Player, AllPlayers } = require("../../player/Player")
|
||||
|
||||
const command = new Command("liste", "Affiche la file d'attente", (client, interaction) => {
|
||||
|
||||
const channel = interaction.member.voice.channel
|
||||
|
||||
if(AllPlayers.has(channel.guildId)) {
|
||||
|
||||
const player = new Player(channel.guildId)
|
||||
const queue = player.queue.getNext()
|
||||
|
||||
var embed = new Embed()
|
||||
embed.setColor(0x15e6ed)
|
||||
|
||||
if(queue.length == 0) {
|
||||
embed = new EmbedError("Il n'y a pas de musique en file d'attente")
|
||||
|
||||
} else if(queue.length > 0) {
|
||||
|
||||
// Result is a song
|
||||
embed.setColor(0x15e6ed)
|
||||
embed.setThumbnail("https://www.iconsdb.com/icons/download/white/list-2-64.png")
|
||||
embed.setTitle('**File d\'attente :**')
|
||||
embed.setDescription('**' + queue.length + ' musiques**')
|
||||
queue.forEach((song, index) => {
|
||||
// max 24 fields
|
||||
if(index > 10) return
|
||||
embed.addField(`**${index+1} - ${song.title}**`, `**Durée : **${song.readduration}\n**Artiste : **${song.author}`)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
embed.send(interaction)
|
||||
|
||||
} else {
|
||||
return new EmbedError("Le bot n'est pas connecté").send(interaction)
|
||||
}
|
||||
})
|
||||
|
||||
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}
|
49
backend/src/discord/Commands/Skip.js
Normal file
49
backend/src/discord/Commands/Skip.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const {Command} = require("../Command")
|
||||
const {Embed, EmbedError} = require("../Embed")
|
||||
const { Player, AllPlayers } = require("../../player/Player")
|
||||
|
||||
const command = new Command("skip", "Passe à la musique suivante", (client, interaction) => {
|
||||
|
||||
if(!interaction.member.voice.channel) return new EmbedError("Vous devez rejoindre un salon vocal pour passer à la musique suivante !").send(interaction)
|
||||
|
||||
const channel = interaction.member.voice.channel
|
||||
|
||||
if(AllPlayers.has(channel.guildId)) {
|
||||
|
||||
const player = new Player(channel.guildId)
|
||||
const result = player.skip()
|
||||
|
||||
|
||||
var embed = new Embed()
|
||||
embed.setColor(0x15e6ed)
|
||||
result.then((song) => {
|
||||
|
||||
if(song == "no_music") {
|
||||
embed = new EmbedError("Il n'y a pas de musique en file d'attente")
|
||||
|
||||
} else if(song) {
|
||||
|
||||
// Result is a song
|
||||
embed.setColor(0x15e6ed)
|
||||
|
||||
embed.setTitle('**Musique suivante !**')
|
||||
embed.setDescription('**Titre : **' + song.title)
|
||||
embed.addField('**Durée : **'+ song.readduration, "")
|
||||
embed.addField("**Artiste : **" + song.author, "")
|
||||
embed.setThumbnail(song.thumbnail)
|
||||
|
||||
|
||||
}
|
||||
|
||||
embed.send(interaction)
|
||||
})
|
||||
|
||||
} else {
|
||||
return new EmbedError("Le bot n'est pas connecté").send(interaction)
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
module.exports = {command}
|
34
backend/src/discord/Commands/State.js
Normal file
34
backend/src/discord/Commands/State.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const {Command} = require("../Command")
|
||||
const {Embed, EmbedError} = require("../Embed")
|
||||
const {Player} = require("../../player/Player")
|
||||
|
||||
const command = new Command("state", "Affiche la musique en cours", (client, interaction) => {
|
||||
|
||||
const channel = interaction.member.voice.channel
|
||||
const player = new Player(channel.guildId)
|
||||
const song = player.queue.getCurrent()
|
||||
|
||||
var embed = new Embed()
|
||||
embed.setColor(0x15e6ed)
|
||||
|
||||
if(!song) {
|
||||
embed = new EmbedError("Il n'y a pas de musique en cours de lecture")
|
||||
|
||||
} else if(song) {
|
||||
|
||||
// Result is a song
|
||||
embed.setColor(0x15e6ed)
|
||||
|
||||
embed.setTitle('**Musique en cours :**')
|
||||
embed.setDescription('**Titre : **' + song.title)
|
||||
embed.addField('**Durée : **', song.readduration)
|
||||
embed.addField("**Artiste : **", song.author)
|
||||
embed.setThumbnail(song.thumbnail)
|
||||
|
||||
|
||||
}
|
||||
|
||||
embed.send(interaction)
|
||||
})
|
||||
|
||||
module.exports = {command}
|
@@ -81,9 +81,23 @@ class Embed {
|
||||
return this.embed
|
||||
}
|
||||
|
||||
send(interaction) {
|
||||
interaction.reply({embeds: [this.build()]})
|
||||
send(interaction, ephemeral) {
|
||||
if(ephemeral) {
|
||||
interaction.reply({embeds: [this.build()], ephemeral: true})
|
||||
} else {
|
||||
interaction.reply({embeds: [this.build()]})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
@@ -18,7 +18,7 @@ metric.publishMetrics("8001", "raphraph")
|
||||
|
||||
setup();
|
||||
|
||||
function setup() {
|
||||
async function setup() {
|
||||
const DiscordBot = require("./discord/Bot")
|
||||
DiscordBot.init()
|
||||
}
|
32
backend/src/media/MediaInformation.js
Normal file
32
backend/src/media/MediaInformation.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const ffprobe = require('ffprobe');
|
||||
const ffprobeStatic = require('ffprobe-static');
|
||||
const { getReadableDuration } = require('../utils/TimeConverter');
|
||||
const clog = require("loguix").getInstance("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 : " + media.attachment.name)
|
||||
clog.error(err)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {getMediaInformation}
|
85
backend/src/media/SoundcloudInformation.js
Normal file
85
backend/src/media/SoundcloudInformation.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const {LogType} = require('loguix');
|
||||
const clog = new LogType("SoundcloudInformation");
|
||||
const {Song} = require('../player/Song');
|
||||
const {Playlist} = require('../player/Playlist');
|
||||
const {Soundcloud} = require('soundcloud.ts')
|
||||
const {getReadableDuration} = require('../utils/TimeConverter');
|
||||
|
||||
const soundcloud = new Soundcloud();
|
||||
|
||||
async function getTrack(url) {
|
||||
try {
|
||||
const info = await soundcloud.tracks.get(url)
|
||||
|
||||
if(!info) {
|
||||
clog.error("Impossible de récupérer les informations de la piste Soundcloud à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
const song = new Song();
|
||||
song.title = info.title;
|
||||
song.author = info.user.username;
|
||||
song.url = info.permalink_url;
|
||||
song.thumbnail = info.artwork_url;
|
||||
song.id = info.id;
|
||||
song.duration = info.duration / 1000;
|
||||
song.readduration = getReadableDuration(info.duration / 1000);
|
||||
song.type = "soundcloud";
|
||||
|
||||
return song;
|
||||
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche Soundcloud (Track): ' + error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getPlaylist(url) {
|
||||
|
||||
try {
|
||||
|
||||
const info = await soundcloud.playlists.get(url)
|
||||
|
||||
if(!info) {
|
||||
clog.error("Impossible de récupérer les informations de la playlist Soundcloud à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
const playlist = new Playlist();
|
||||
|
||||
playlist.title = info.title;
|
||||
playlist.author = info.user.username;
|
||||
playlist.url = info.permalink_url;
|
||||
playlist.thumbnail = info.artwork_url;
|
||||
playlist.id = info.id;
|
||||
playlist.duration = 0;
|
||||
playlist.songs = [];
|
||||
playlist.type = "soundcloud";
|
||||
|
||||
for(const track of info.tracks) {
|
||||
const song = new Song();
|
||||
song.title = track.title;
|
||||
song.author = track.user.username;
|
||||
song.url = track.permalink_url;
|
||||
song.thumbnail = track.artwork_url;
|
||||
song.id = track.id;
|
||||
song.duration = track.duration / 1000;
|
||||
song.readduration = getReadableDuration(track.duration / 1000);
|
||||
song.type = "soundcloud";
|
||||
|
||||
playlist.duration += track.duration / 1000;
|
||||
playlist.songs.push(song);
|
||||
}
|
||||
|
||||
playlist.readduration = getReadableDuration(playlist.duration);
|
||||
|
||||
return playlist;
|
||||
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche Soundcloud (Playlist): ' + error);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {getTrack, getPlaylist}
|
179
backend/src/media/SpotifyInformation.js
Normal file
179
backend/src/media/SpotifyInformation.js
Normal file
@@ -0,0 +1,179 @@
|
||||
const {LogType} = require('loguix');
|
||||
const clog = new LogType("SpotifyInformation");
|
||||
const config = require('../utils/Database/Configuration');
|
||||
const SPOTIFY_CLIENT_ID = config.getSpotifyClientId()
|
||||
const SPOTIFY_CLIENT_SECRET = config.getSpotifyClientSecret()
|
||||
const SpotifyWebApi = require('spotify-web-api-node');
|
||||
const {Playlist} = require('../player/Playlist');
|
||||
const {Song} = require('../player/Song');
|
||||
const youtube = require("../media/YoutubeInformation");
|
||||
const {getReadableDuration} = require('../utils/TimeConverter');
|
||||
|
||||
const spotifyApi = new SpotifyWebApi({
|
||||
clientId: SPOTIFY_CLIENT_ID,
|
||||
clientSecret: SPOTIFY_CLIENT_SECRET,
|
||||
});
|
||||
|
||||
async function getSong(url) {
|
||||
try {
|
||||
const data = await spotifyApi.clientCredentialsGrant();
|
||||
spotifyApi.setAccessToken(data.body['access_token']);
|
||||
|
||||
const parts = url.split('/');
|
||||
const trackId = parts[parts.length - 1];
|
||||
|
||||
if(!trackId) {
|
||||
clog.error("Impossible de récupérer l'identifiant de la piste Spotify à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
const trackInfo = await spotifyApi.getTrack(trackId);
|
||||
|
||||
const trackName = trackInfo.body.name;
|
||||
const artistName = trackInfo.body.artists[0].name;
|
||||
|
||||
return `${trackName} - ${artistName}`;
|
||||
} catch (error) {
|
||||
|
||||
clog.error("Impossible de récupérer les informations de la piste Spotify à partir de l'URL");
|
||||
clog.error(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getAlbum(url) {
|
||||
|
||||
try {
|
||||
|
||||
|
||||
const creditdata = await spotifyApi.clientCredentialsGrant();
|
||||
spotifyApi.setAccessToken(creditdata.body['access_token']);
|
||||
|
||||
const parts = url.split('/');
|
||||
const albumId = parts[parts.indexOf('album') + 1].split('?')[0];
|
||||
|
||||
const data = await spotifyApi.getAlbum(albumId);
|
||||
const info = data.body;
|
||||
|
||||
if(!info) {
|
||||
clog.error("Impossible de récupérer les informations de l'album Spotify à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
clog.log("Informations de l'album récupérées : " + info.name);
|
||||
|
||||
const playlist = new Playlist()
|
||||
playlist.title = info.name;
|
||||
playlist.author = info.artists[0].name;
|
||||
playlist.authorId = info.artists[0].id;
|
||||
playlist.thumbnail = info.images[0].url;
|
||||
playlist.url = info.external_urls.spotify;
|
||||
playlist.id = albumId;
|
||||
playlist.type = "spotify";
|
||||
playlist.songs = info.tracks.items;
|
||||
|
||||
return playlist;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
clog.error("Impossible de récupérer les informations de l'album Spotify à partir de l'URL");
|
||||
clog.error(error);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async function getPlaylist(url) {
|
||||
// Get the playlist and return a Playlist Object
|
||||
|
||||
try {
|
||||
const creditdata = await spotifyApi.clientCredentialsGrant();
|
||||
spotifyApi.setAccessToken(creditdata.body['access_token']);
|
||||
|
||||
const parts = url.split('/');
|
||||
const playlistId = parts[parts.indexOf('playlist') + 1].split('?')[0];
|
||||
|
||||
const data = await spotifyApi.getPlaylist(playlistId)
|
||||
|
||||
const info = data.body;
|
||||
|
||||
if(!info) {
|
||||
clog.error("Impossible de récupérer les informations de la playlist Spotify à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
clog.log("Informations de la playlist récupérées : " + info.name);
|
||||
|
||||
const playlist = new Playlist()
|
||||
playlist.title = info.name;
|
||||
playlist.author = info.owner.display_name;
|
||||
playlist.authorId = info.owner.id;
|
||||
playlist.thumbnail = info.images[0].url;
|
||||
playlist.url = info.external_urls.spotify;
|
||||
playlist.id = playlistId;
|
||||
playlist.type = "spotify";
|
||||
|
||||
for(const track of info.tracks.items) {
|
||||
playlist.songs.push(track.track);
|
||||
}
|
||||
|
||||
return playlist;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
clog.error("Impossible de récupérer les informations de l'album Spotify à partir de l'URL");
|
||||
clog.error(error);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function getTracks(playlist) {
|
||||
|
||||
const tracks = playlist.songs
|
||||
playlistSongs = [];
|
||||
for(const track of tracks) {
|
||||
|
||||
var trackName = track.name;
|
||||
var artistName = track.artists[0].name;
|
||||
var queryForYoutube = `${trackName} - ${artistName}`;
|
||||
|
||||
var urlYoutubeFounded = await youtube.getQuery(queryForYoutube).then(function(songFind) {
|
||||
if(!songFind) return null;
|
||||
return songFind.url;
|
||||
});
|
||||
|
||||
clog.log("URL de la vidéo YouTube trouvée : " + urlYoutubeFounded);
|
||||
|
||||
if(!urlYoutubeFounded) {
|
||||
clog.error("Impossible de récupérer l'URL de la vidéo YouTube à partir de la requête " + queryForYoutube);
|
||||
|
||||
} else {
|
||||
const song = new Song();
|
||||
|
||||
song.title = track.name;
|
||||
song.author = track.artists[0].name;
|
||||
song.url = urlYoutubeFounded;
|
||||
song.thumbnail = playlist.thumbnail;
|
||||
song.id = track.id;
|
||||
song.duration = track.duration_ms / 1000;
|
||||
song.readduration = getReadableDuration(track.duration_ms / 1000);
|
||||
song.type = "youtube";
|
||||
|
||||
playlist.duration += track.duration_ms / 1000;
|
||||
playlistSongs.push(song);
|
||||
}
|
||||
|
||||
// When finish do this
|
||||
|
||||
}
|
||||
|
||||
playlist.readduration = getReadableDuration(playlist.duration);
|
||||
playlist.songs = playlistSongs;
|
||||
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {getSong, getAlbum, getPlaylist, getTracks}
|
101
backend/src/media/YoutubeInformation.js
Normal file
101
backend/src/media/YoutubeInformation.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const { LogType } = require('loguix');
|
||||
const clog = new LogType("YoutubeInformation");
|
||||
const { Song } = require('../player/Song');
|
||||
const { Playlist } = require('../player/Playlist');
|
||||
const { getReadableDuration } = require('../utils/TimeConverter');
|
||||
const ytsr = require('@distube/ytsr');
|
||||
const ytfps = require('ytfps');
|
||||
|
||||
async function getQuery(query) {
|
||||
if (query === null || typeof query !== 'string') {
|
||||
clog.error("Impossible de rechercher une vidéo YouTube, car la requête est nulle");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const searchResults = await ytsr(query, { limit: 1 });
|
||||
const video = searchResults.items.find(item => item.type === 'video');
|
||||
|
||||
if (!video) {
|
||||
clog.error("Impossible de récupérer le lien de la vidéo YouTube à partir de la requête");
|
||||
return null;
|
||||
}
|
||||
|
||||
const song = await getVideo(video.url);
|
||||
return song;
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche YouTube: ' + error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getVideo(url) {
|
||||
const videoId = url.match(/(?:youtu\.be\/|youtube\.com\/|music\.youtube\.com\/)(?:watch\?v=)?([a-zA-Z0-9_-]{11})/);
|
||||
if (videoId === null) {
|
||||
clog.error("Impossible de récupérer l'identifiant de la vidéo YouTube à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const searchResults = await ytsr(videoId[1], { limit: 1 });
|
||||
const video = searchResults.items.find(item => item.type === 'video');
|
||||
|
||||
if (video) {
|
||||
const songReturn = new Song();
|
||||
await songReturn.processYoutubeVideo(video);
|
||||
return songReturn;
|
||||
} else {
|
||||
clog.error("Impossible de récupérer la vidéo YouTube à partir de l'identifiant");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche de la vidéo YouTube:' + error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getPlaylist(url) {
|
||||
if (url === null || typeof url !== 'string') {
|
||||
clog.error("Impossible de rechercher une playlist YouTube, car la requête est nulle");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const playlistId = url.match(/(?:youtu\.be\/|youtube\.com\/|music\.youtube\.com\/)(?:playlist\?list=)?([a-zA-Z0-9_-]{34})/);
|
||||
if (playlistId === null) {
|
||||
clog.error("Impossible de récupérer l'identifiant de la playlist YouTube à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
|
||||
const playlistInfo = await ytfps(playlistId[1]);
|
||||
|
||||
if (!playlistInfo) {
|
||||
clog.error("Impossible de récupérer la playlist YouTube à partir de l'identifiant");
|
||||
return null;
|
||||
}
|
||||
|
||||
const playlist = new Playlist();
|
||||
playlist.type = "youtube";
|
||||
playlist.author = playlistInfo.author.name;
|
||||
playlist.authorId = playlistInfo.author.url;
|
||||
playlist.title = playlistInfo.title;
|
||||
playlist.thumbnail = playlistInfo.thumbnail_url;
|
||||
playlist.description = playlistInfo.description;
|
||||
playlist.url = `https://www.youtube.com/playlist?list=${playlistId[1]}`;
|
||||
playlist.id = playlistId[1];
|
||||
|
||||
for (const video of playlistInfo.videos) {
|
||||
const song = new Song();
|
||||
await song.processYoutubeVideo(video, true);
|
||||
playlist.duration += song.duration;
|
||||
playlist.songs.push(song);
|
||||
}
|
||||
playlist.readduration = getReadableDuration(playlist.duration);
|
||||
return playlist;
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche YouTube: ' + error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { getQuery, getVideo, getPlaylist };
|
46
backend/src/player/Finder.js
Normal file
46
backend/src/player/Finder.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const Resolver = require('../utils/Resolver');
|
||||
const { QueryType } = require('../utils/QueryType');
|
||||
const { Links } = require('../utils/Links');
|
||||
const youtube = require("../media/YoutubeInformation")
|
||||
const spotify = require("../media/SpotifyInformation")
|
||||
const soundcloud = require("../media/SoundcloudInformation")
|
||||
|
||||
|
||||
async function search(query) {
|
||||
const type = Resolver.getQueryType(query)
|
||||
if(type == QueryType.YOUTUBE_SEARCH) {
|
||||
|
||||
return await youtube.getQuery(query)
|
||||
|
||||
}
|
||||
if(type == QueryType.YOUTUBE_VIDEO) {
|
||||
|
||||
return await youtube.getVideo(query)
|
||||
}
|
||||
if(type == QueryType.YOUTUBE_PLAYLIST) {
|
||||
|
||||
return await youtube.getPlaylist(query)
|
||||
}
|
||||
if(type == QueryType.SPOTIFY_SONG) {
|
||||
return await youtube.getQuery(await spotify.getSong(query))
|
||||
|
||||
}
|
||||
if(type == QueryType.SPOTIFY_ALBUM) {
|
||||
return await spotify.getAlbum(query)
|
||||
}
|
||||
if(type == QueryType.SPOTIFY_PLAYLIST) {
|
||||
return await spotify.getPlaylist(query)
|
||||
|
||||
}
|
||||
if(type == QueryType.SOUNDCLOUD_TRACK) {
|
||||
return await soundcloud.getTrack(query)
|
||||
|
||||
}
|
||||
if(type == QueryType.SOUNDCLOUD_PLAYLIST) {
|
||||
return await soundcloud.getPlaylist(query)
|
||||
|
||||
}
|
||||
// TODO: Add more providers
|
||||
}
|
||||
|
||||
module.exports = {search}
|
213
backend/src/player/List.js
Normal file
213
backend/src/player/List.js
Normal file
@@ -0,0 +1,213 @@
|
||||
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 { Song } = require('./Song')
|
||||
|
||||
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() {
|
||||
if(this.current != null) {
|
||||
this.addPreviousSong(this.current)
|
||||
}
|
||||
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)
|
||||
|
||||
}
|
||||
this.setCurrent(song)
|
||||
return song
|
||||
}
|
||||
|
||||
|
||||
clearNext() {
|
||||
this.next = new Array();
|
||||
}
|
||||
|
||||
addNextSong(song) {
|
||||
this.next.push(song)
|
||||
}
|
||||
|
||||
firstNext(song) {
|
||||
this.next.unshift(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() {
|
||||
const previousList = new Array()
|
||||
|
||||
for(const song of PreviousDB.data[this.guildId]) {
|
||||
previousList.push(new Song(song))
|
||||
}
|
||||
return previousList;
|
||||
|
||||
}
|
||||
|
||||
getPreviousSong() {
|
||||
if(PreviousDB.data[this.guildId].length > 0) {
|
||||
return new Song(PreviousDB.data[this.guildId][0])
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
previousSong() {
|
||||
if(this.current != null) {
|
||||
this.firstNext(this.current)
|
||||
}
|
||||
if(PreviousDB.data[this.guildId].length > 0) {
|
||||
const song = PreviousDB.data[this.guildId][0]
|
||||
// Remove the song from the previous list
|
||||
PreviousDB.data[this.guildId].splice(0, 1)
|
||||
savePrevious()
|
||||
return new Song(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;
|
||||
AllLists.delete(this.guildId)
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
addNextPlaylist(playlist, firstAlreadyPlayed) {
|
||||
if(firstAlreadyPlayed) {
|
||||
playlist.songs.shift()
|
||||
}
|
||||
|
||||
for(const song of playlist.songs) {
|
||||
this.addNextSong(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 }
|
28
backend/src/player/Method/Media.js
Normal file
28
backend/src/player/Method/Media.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const {createAudioResource, VoiceConnectionStatus, createAudioPlayer, StreamType} = require('@discordjs/voice');
|
||||
const {LogType} = require('loguix')
|
||||
const clog = new LogType("Media")
|
||||
const plog = require("loguix").getInstance("Player")
|
||||
|
||||
async function play(instance, song) {
|
||||
try {
|
||||
|
||||
instance.player = createAudioPlayer()
|
||||
instance.generatePlayerEvents()
|
||||
const player = instance.player
|
||||
var resource = await createAudioResource(song.url, {
|
||||
inputType: StreamType.Arbitrary
|
||||
}) // Remplace par ton fichier audio
|
||||
|
||||
player.play(resource);
|
||||
instance.connection.subscribe(player);
|
||||
clog.log(`GUILD : ${instance.guildId} - Lecture de la musique (Media): ${song.title} - id : ${song.id}`)
|
||||
|
||||
} catch(e) {
|
||||
clog.error("Erreur lors de la lecture de la musique : " + song.title)
|
||||
clog.error(e)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = {play}
|
31
backend/src/player/Method/Soundcloud.js
Normal file
31
backend/src/player/Method/Soundcloud.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const {createAudioResource, VoiceConnectionStatus, createAudioPlayer, StreamType} = require('@discordjs/voice');
|
||||
const {LogType} = require('loguix')
|
||||
const clog = new LogType("Soundcloud")
|
||||
const plog = require("loguix").getInstance("Player")
|
||||
const {Soundcloud} = require('soundcloud.ts')
|
||||
|
||||
const soundcloud = new Soundcloud();
|
||||
|
||||
async function play(instance, song) {
|
||||
try {
|
||||
|
||||
instance.player = createAudioPlayer()
|
||||
instance.generatePlayerEvents()
|
||||
const player = instance.player
|
||||
|
||||
const stream = await soundcloud.util.streamTrack(song.url)
|
||||
var resource = await createAudioResource(stream)
|
||||
|
||||
player.play(resource);
|
||||
instance.connection.subscribe(player);
|
||||
clog.log(`GUILD : ${instance.guildId} - Lecture de la musique (Soundcloud): ${song.title} - id : ${song.id}`)
|
||||
|
||||
} catch(e) {
|
||||
clog.error("Erreur lors de la lecture de la musique : " + song.title)
|
||||
clog.error(e)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = {play}
|
37
backend/src/player/Method/Youtube.js
Normal file
37
backend/src/player/Method/Youtube.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const {createAudioResource, VoiceConnectionStatus, createAudioPlayer, StreamType} = require('@discordjs/voice');
|
||||
const {LogType} = require('loguix')
|
||||
const clog = new LogType("Youtube")
|
||||
const plog = require("loguix").getInstance("Player")
|
||||
const ytdl = require('@distube/ytdl-core')
|
||||
|
||||
async function play(instance, song) {
|
||||
try {
|
||||
|
||||
instance.player = createAudioPlayer()
|
||||
instance.generatePlayerEvents()
|
||||
const player = instance.player
|
||||
const stream = ytdl(song.url, {
|
||||
quality: 'highestaudio',
|
||||
highWaterMark: 1 << 30,
|
||||
liveBuffer: 20000,
|
||||
dlChunkSize: 0,
|
||||
bitrate: 128,
|
||||
|
||||
});
|
||||
|
||||
// Add compressor to the audio resource
|
||||
var resource = createAudioResource(stream);
|
||||
|
||||
|
||||
|
||||
player.play(resource);
|
||||
instance.connection.subscribe(player);
|
||||
clog.log(`GUILD : ${instance.guildId} - Lecture de la musique (Youtube): ${song.title} - id : ${song.id}`)
|
||||
|
||||
} catch(e) {
|
||||
clog.error("Erreur lors de la lecture de la musique : " + song.title)
|
||||
clog.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {play}
|
213
backend/src/player/Player.js
Normal file
213
backend/src/player/Player.js
Normal file
@@ -0,0 +1,213 @@
|
||||
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 youtube = require('./Method/Youtube');
|
||||
const soundcloud = require('./Method/Soundcloud');
|
||||
|
||||
const AllPlayers = new Map()
|
||||
|
||||
class Player {
|
||||
connection;
|
||||
player;
|
||||
guildId;
|
||||
channelId;
|
||||
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,
|
||||
selfDeaf: false,
|
||||
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}"`);
|
||||
|
||||
// Si la connection est fermée, on détruit le player
|
||||
|
||||
if(newState.status === VoiceConnectionStatus.Disconnected) {
|
||||
this.leave()
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
generatePlayerEvents() {
|
||||
|
||||
const Activity = require('../discord/Activity');
|
||||
|
||||
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, () => {
|
||||
Activity.idleActivity()
|
||||
this.queue.setCurrent(null)
|
||||
if(this.queue.next.length > 0) {
|
||||
this.play(this.queue.nextSong())
|
||||
}
|
||||
});
|
||||
|
||||
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)
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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(this.checkConnection()) return
|
||||
if(this.queue.current != null) {
|
||||
this.player.stop()
|
||||
}
|
||||
|
||||
this.queue.setCurrent(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)
|
||||
}
|
||||
|
||||
// TODO: Créer une méthode pour les autres types de médias
|
||||
}
|
||||
|
||||
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)
|
||||
plog.log(`GUILD : ${this.guildId} - La musique a été ajoutée à la liste de lecture : ${song.title}`)
|
||||
}
|
||||
|
||||
async readPlaylist(playlist, now) {
|
||||
if(this.player.state.status == AudioPlayerStatus.Idle && this.queue.current === null && this.queue.next.length === 0) {
|
||||
this.play(playlist.songs[0])
|
||||
}
|
||||
if(now) {
|
||||
this.play(playlist.songs[0])
|
||||
this.queue.addNextPlaylist(playlist, true)
|
||||
} else {
|
||||
this.queue.addNextPlaylist(playlist)
|
||||
}
|
||||
plog.log(`GUILD : ${this.guildId} - La playlist a été ajoutée à la liste de lecture : ${playlist.title}`)
|
||||
}
|
||||
|
||||
async pause() {
|
||||
if(this.checkConnection()) return "no_music"
|
||||
if(this.player.state.status == AudioPlayerStatus.Paused) {
|
||||
this.player.unpause()
|
||||
plog.log(`GUILD : ${this.guildId} - La musique a été reprise`)
|
||||
return false
|
||||
} else {
|
||||
this.player.pause()
|
||||
plog.log(`GUILD : ${this.guildId} - La musique a été mise en pause`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
async leave() {
|
||||
const Activity = require('../discord/Activity');
|
||||
if(this.checkConnection()) return
|
||||
if(this.queue.current != null) {
|
||||
this.queue.addPreviousSong(this.queue.current)
|
||||
}
|
||||
// Détruit la connection et le player et l'enlève de la liste des
|
||||
this.connection.destroy()
|
||||
this.player.stop()
|
||||
this.player = null
|
||||
this.connection = null
|
||||
this.channelId = null
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
async skip() {
|
||||
|
||||
if(this.checkConnection()) return "no_music"
|
||||
if(this.queue.next.length === 0) {
|
||||
return "no_music"
|
||||
}
|
||||
const songSkip = this.queue.nextSong()
|
||||
this.play(songSkip)
|
||||
return songSkip
|
||||
}
|
||||
|
||||
async previous() {
|
||||
|
||||
if(this.checkConnection()) return "no_music"
|
||||
if(this.queue.getPrevious().length === 0) {
|
||||
return "no_music"
|
||||
}
|
||||
|
||||
const songPrevious = this.queue.previousSong()
|
||||
this.play(songPrevious)
|
||||
return songPrevious
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Player, AllPlayers}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
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);
|
||||
|
||||
*/
|
16
backend/src/player/Playlist.js
Normal file
16
backend/src/player/Playlist.js
Normal file
@@ -0,0 +1,16 @@
|
||||
class Playlist {
|
||||
title = "Aucun titre";
|
||||
id;
|
||||
url;
|
||||
author = "Auteur inconnu";
|
||||
authorId;
|
||||
songs = [];
|
||||
thumbnail = "https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png" ;
|
||||
duration = 0;
|
||||
readduration;
|
||||
description;
|
||||
type;
|
||||
}
|
||||
|
||||
module.exports = {Playlist};
|
||||
|
81
backend/src/player/Song.js
Normal file
81
backend/src/player/Song.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const {LogType} = require('loguix')
|
||||
|
||||
const clog = new LogType("Song")
|
||||
const MediaInformation = require('../media/MediaInformation')
|
||||
const { getReadableDuration, getSecondsDuration } = require('../utils/TimeConverter');
|
||||
|
||||
class Song {
|
||||
title = "Aucun titre";
|
||||
id = "Aucun fichier";
|
||||
author = "Auteur inconnu"
|
||||
authorId;
|
||||
url;
|
||||
thumbnail = "https://radomisol.fr/wp-content/uploads/2016/08/cropped-note-radomisol-musique.png" ;
|
||||
duration;
|
||||
readduration;
|
||||
type;
|
||||
|
||||
constructor(properties) {
|
||||
if(properties) {
|
||||
this.type = properties.type ?? this.type
|
||||
this.title = properties.title ?? this.title
|
||||
this.id = properties.id ?? this.id
|
||||
this.author = properties.author ?? this.author
|
||||
this.url = properties.url ?? this.url
|
||||
this.thumbnail = properties.thumbnail ?? this.thumbnail
|
||||
this.duration = properties.duration ?? this.duration
|
||||
this.readduration = properties.readduration ?? this.readduration
|
||||
this.type = properties.type ?? this.type
|
||||
this.authorId = properties.authorId ?? this.authorId
|
||||
}
|
||||
}
|
||||
|
||||
async processMedia(media, provider) {
|
||||
if(provider) this.author = provider;
|
||||
if(provider) this.authorId = provider;
|
||||
// Check if media is a file or a link
|
||||
if(media.attachment) {
|
||||
this.url = media.attachment.url
|
||||
this.id = media.attachment.name
|
||||
this.type = "attachment"
|
||||
|
||||
// In face, duration is null, get the metadata of the file to get the duration
|
||||
await MediaInformation.getMediaInformation(this, media)
|
||||
|
||||
} else {
|
||||
clog.error("Impossible de traiter le média")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async processYoutubeVideo(video, playlist) {
|
||||
if(playlist) {
|
||||
this.title = video.title
|
||||
this.author = video.author.name
|
||||
this.authorId = video.author.channel_url
|
||||
this.thumbnail = video.thumbnail_url
|
||||
this.url = video.url
|
||||
this.type = "youtube"
|
||||
this.id = video.id
|
||||
|
||||
this.duration = video.milis_length / 1000
|
||||
this.readduration = getReadableDuration(this.duration)
|
||||
} else {
|
||||
this.title = video.name
|
||||
this.author = video.author.name
|
||||
this.authorId = video.author.url
|
||||
this.thumbnail = video.thumbnail
|
||||
this.url = video.url
|
||||
this.type = "youtube"
|
||||
this.id = video.id
|
||||
|
||||
this.duration = getSecondsDuration(video.duration)
|
||||
this.readduration = getReadableDuration(this.duration)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {Song}
|
@@ -10,6 +10,13 @@ const config = new Database("config", __glob.DATA + path.sep + "config.json", {
|
||||
report: {
|
||||
channel : "",
|
||||
contact : ""
|
||||
},
|
||||
api: {
|
||||
youtube: "",
|
||||
spotify: {
|
||||
clientId: "",
|
||||
clientSecret: ""
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -25,9 +32,22 @@ function getReportContact() {
|
||||
return config.data.report.contact
|
||||
}
|
||||
|
||||
function getYoutubeApiKey() {
|
||||
return config.data.api.youtube
|
||||
}
|
||||
|
||||
function getSpotifyClientId() {
|
||||
return config.data.api.spotify.clientId
|
||||
}
|
||||
|
||||
function getSpotifyClientSecret() {
|
||||
|
||||
return config.data.api.spotify.clientSecret
|
||||
}
|
||||
|
||||
if(getToken() == "") {
|
||||
clog.error("Impossible de démarrer sans token valide")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
module.exports = {getToken, getReportChannel, getReportContact}
|
||||
module.exports = {getToken, getReportChannel, getReportContact, getYoutubeApiKey, getSpotifyClientId, getSpotifyClientSecret}
|
@@ -61,6 +61,9 @@ class Database {
|
||||
clog.error(`Erreur lors de la sauvegarde de la base de données '${this.name}'`)
|
||||
clog.error(e)
|
||||
}
|
||||
|
||||
// Assure that the database is up to date and reloaded
|
||||
this.update()
|
||||
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,8 @@ const __glob = {
|
||||
LOGS: root + path.sep + "logs",
|
||||
DATA: root + path.sep + "data",
|
||||
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}
|
79
backend/src/utils/Links.js
Normal file
79
backend/src/utils/Links.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const YoutubeLinks = [
|
||||
"youtube.com",
|
||||
"youtu.be",
|
||||
"music.youtube.com",
|
||||
"gaming.youtube.com",
|
||||
"www.youtube.com",
|
||||
"m.youtube.com"
|
||||
]
|
||||
|
||||
|
||||
var youtubePlaylistRegex = new RegExp(/^https?:\/\/(www.)?youtube.com\/playlist\?list=((PL|FL|UU|LL|RD|OL)[a-zA-Z0-9-_]{16,41})$/)
|
||||
var youtubeVideoURLRegex = new RegExp(/^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:watch\?v=))([\w-]{11})(\S+)?$/)
|
||||
var youtubeVideoIdRegex = new RegExp(/^[a-zA-Z0-9-_]{11}$/)
|
||||
|
||||
|
||||
|
||||
|
||||
const SpotifyLinks = [
|
||||
"open.spotify.com",
|
||||
"embed.spotify.com"
|
||||
]
|
||||
|
||||
var spotifySongRegex = new RegExp(/^https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(intl-([a-z]|[A-Z])+\/)?(?:track\/|\?uri=spotify:track:)((\w|-){22})(\?si=.+)?$/)
|
||||
var spotifyPlaylistRegex = new RegExp(/^https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(intl-([a-z]|[A-Z])+\/)?(?:playlist\/|\?uri=spotify:playlist:)((\w|-){22})(\?si=.+)?$/)
|
||||
var spotifyAlbumRegex = new RegExp(/^https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(intl-([a-z]|[A-Z])+\/)?(?:album\/|\?uri=spotify:album:)((\w|-){22})(\?si=.+)?$/)
|
||||
|
||||
|
||||
const SoundcloudLinks = [
|
||||
"soundcloud.com"
|
||||
]
|
||||
|
||||
|
||||
var soundcloudTrackRegex = new RegExp(/^https?:\/\/(m.|www.)?soundcloud.com\/(\w|-)+\/(\w|-)+(.+)?$/)
|
||||
var soundcloudPlaylistRegex = new RegExp(/^https?:\/\/(m.|www.)?soundcloud.com\/(\w|-)+\/sets\/(\w|-)+(.+)?$/)
|
||||
|
||||
/**
|
||||
* @typedef {Object} Links
|
||||
* @property {Object} regex
|
||||
* @property {Object} regex.youtube
|
||||
* @property {RegExp} regex.youtube.playlist
|
||||
* @property {RegExp} regex.youtube.videoURL
|
||||
* @property {RegExp} regex.youtube.videoId
|
||||
* @property {Object} regex.spotify
|
||||
* @property {RegExp} regex.spotify.song
|
||||
* @property {RegExp} regex.spotify.playlist
|
||||
* @property {RegExp} regex.spotify.album
|
||||
* @property {Object} regex.soundcloud
|
||||
* @property {RegExp} regex.soundcloud.track
|
||||
* @property {RegExp} regex.soundcloud.playlist
|
||||
* @property {Object} types
|
||||
* @property {Array<String>} types.youtube
|
||||
* @property {Array<String>} types.spotify
|
||||
* @property {Array<String>} types.soundcloud
|
||||
*/
|
||||
const Links = {
|
||||
regex: {
|
||||
youtube: {
|
||||
playlist: youtubePlaylistRegex,
|
||||
videoURL: youtubeVideoURLRegex,
|
||||
videoId: youtubeVideoIdRegex
|
||||
},
|
||||
spotify: {
|
||||
song: spotifySongRegex,
|
||||
playlist: spotifyPlaylistRegex,
|
||||
album: spotifyAlbumRegex
|
||||
},
|
||||
soundcloud: {
|
||||
track: soundcloudTrackRegex,
|
||||
playlist: soundcloudPlaylistRegex
|
||||
}
|
||||
},
|
||||
types: {
|
||||
youtube: YoutubeLinks,
|
||||
spotify: SpotifyLinks,
|
||||
soundcloud: SoundcloudLinks
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Links}
|
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}
|
16
backend/src/utils/QueryType.js
Normal file
16
backend/src/utils/QueryType.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Enum for query types
|
||||
* @readonly
|
||||
*/
|
||||
const QueryType = {
|
||||
SPOTIFY_PLAYLIST: 'spotify_playlist',
|
||||
SPOTIFY_ALBUM: 'spotify_album',
|
||||
SPOTIFY_SONG: 'spotify_song',
|
||||
YOUTUBE_PLAYLIST: 'youtube_playlist',
|
||||
YOUTUBE_VIDEO: 'youtube_video',
|
||||
SOUNDCLOUD_TRACK: 'soundcloud_track',
|
||||
SOUNDCLOUD_PLAYLIST: 'soundcloud_playlist',
|
||||
YOUTUBE_SEARCH: 'youtube_search',
|
||||
}
|
||||
|
||||
module.exports = { QueryType };
|
32
backend/src/utils/Resolver.js
Normal file
32
backend/src/utils/Resolver.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const {Links} = require('./Links')
|
||||
const {QueryType} = require('./QueryType')
|
||||
|
||||
function getQueryType(url) {
|
||||
// Check if it's string
|
||||
|
||||
if(typeof url !== "string") return "NOT_STRING"
|
||||
// Check if it's a Youtube link
|
||||
|
||||
if(Links.regex.youtube.playlist.test(url)) return QueryType.YOUTUBE_PLAYLIST
|
||||
if(Links.regex.youtube.videoURL.test(url)) return QueryType.YOUTUBE_VIDEO
|
||||
|
||||
// Check if it's a Spotify link
|
||||
|
||||
if(Links.regex.spotify.playlist.test(url)) return QueryType.SPOTIFY_PLAYLIST
|
||||
if(Links.regex.spotify.album.test(url)) return QueryType.SPOTIFY_ALBUM
|
||||
if(Links.regex.spotify.song.test(url)) return QueryType.SPOTIFY_SONG
|
||||
|
||||
// Check if it's a Soundcloud link
|
||||
|
||||
if(Links.regex.soundcloud.playlist.test(url)) return QueryType.SOUNDCLOUD_PLAYLIST
|
||||
if(Links.regex.soundcloud.track.test(url)) return QueryType.SOUNDCLOUD_TRACK
|
||||
|
||||
|
||||
|
||||
return QueryType.YOUTUBE_SEARCH
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = {getQueryType}
|
46
backend/src/utils/TimeConverter.js
Normal file
46
backend/src/utils/TimeConverter.js
Normal file
@@ -0,0 +1,46 @@
|
||||
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
|
||||
}
|
||||
|
||||
function getSecondsDuration(duration) {
|
||||
// Duration is in format hh:mm:ss and can be just m:ss or mm:ss
|
||||
var durationArray = duration.split(":");
|
||||
var seconds = 0;
|
||||
if(durationArray.length == 3) {
|
||||
seconds = parseInt(durationArray[0]) * 3600 + parseInt(durationArray[1]) * 60 + parseInt(durationArray[2]);
|
||||
} else if(durationArray.length == 2) {
|
||||
seconds = parseInt(durationArray[0]) * 60 + parseInt(durationArray[1]);
|
||||
} else {
|
||||
seconds = parseInt(durationArray[0]);
|
||||
}
|
||||
return seconds;
|
||||
}
|
||||
|
||||
module.exports = {getReadableDuration, getSecondsDuration}
|
Reference in New Issue
Block a user