Version 0.3.0 - Ajout des premières fonctionnalités du Player
This commit is contained in:
61
backend/package-lock.json
generated
61
backend/package-lock.json
generated
@@ -24,9 +24,11 @@
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"node_modules/@derhuerst/http-basic": {
|
||||
@@ -65,6 +67,16 @@
|
||||
"spotify-url-info": "^3.2.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@discord-player/extractor/node_modules/soundcloud.ts": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/soundcloud.ts/-/soundcloud.ts-0.5.5.tgz",
|
||||
"integrity": "sha512-bygjhC1w/w26Nk0Y+4D4cWSEJ1TdxLaE6+w4pCazFzPF+J4mzuB62ggWmFa7BiwnirzNf9lgPbjzrQYGege4Ew==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici": "^6.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@discord-player/ffmpeg": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@discord-player/ffmpeg/-/ffmpeg-7.1.0.tgz",
|
||||
@@ -1560,6 +1572,32 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
|
||||
"integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios/node_modules/form-data": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -5569,14 +5607,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/soundcloud.ts": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/soundcloud.ts/-/soundcloud.ts-0.5.5.tgz",
|
||||
"integrity": "sha512-bygjhC1w/w26Nk0Y+4D4cWSEJ1TdxLaE6+w4pCazFzPF+J4mzuB62ggWmFa7BiwnirzNf9lgPbjzrQYGege4Ew==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici": "^6.17.0"
|
||||
}
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/soundcloud.ts/-/soundcloud.ts-0.6.3.tgz",
|
||||
"integrity": "sha512-Ri5bO0jQKKACijGP1/OVbWXhHREDST2T6QUSAPWlzQjUScXVyh+7YJfN1mTnyuAA7vZjKyZ1FMlWC2hKd7jmHQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
@@ -6559,6 +6593,15 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ytfps": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ytfps/-/ytfps-1.2.0.tgz",
|
||||
"integrity": "sha512-DLcW0opwT0zO+4C5YqcCgPiOIzAtge6q6q3nDW0gCBy4kPufEdyxmjd1O9GUV4WeAFxfA2XNhZLmaohrGKV1WA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,8 +33,10 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
@@ -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],
|
||||
})
|
||||
@@ -68,7 +72,42 @@ function init() {
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: Implement the disconnect event for the bot
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
@@ -3,6 +3,7 @@ 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) => {
|
||||
|
||||
@@ -11,7 +12,7 @@ const command = new Command("play", "Jouer une musique à partir d'un lien dans
|
||||
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((song) => {
|
||||
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)
|
||||
@@ -23,34 +24,22 @@ const command = new Command("play", "Jouer une musique à partir d'un lien dans
|
||||
// Check if song is playlist
|
||||
if(song instanceof Playlist) {
|
||||
|
||||
if(now) {
|
||||
player.readPlaylist(song, true)
|
||||
embed.setTitle('**Lecture immédiate**')
|
||||
} else {
|
||||
player.readPlaylist(song)
|
||||
embed.setTitle('**Ajout à la liste de lecture**')
|
||||
}
|
||||
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('**Durée : **', song.readduration)
|
||||
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 {
|
||||
|
||||
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)
|
||||
@@ -60,9 +49,42 @@ const command = new Command("play", "Jouer une musique à partir d'un lien dans
|
||||
|
||||
}
|
||||
|
||||
|
||||
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},
|
||||
|
@@ -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}
|
@@ -27,33 +27,83 @@ async function getSong(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) {
|
||||
console.error('Erreur lors de la récupération des données :', 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(albumId) {
|
||||
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
|
||||
const data = await spotifyApi.clientCredentialsGrant();
|
||||
spotifyApi.setAccessToken(data.body['access_token']);
|
||||
|
||||
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];
|
||||
|
||||
spotifyApi.getPlaylist(playlistId)
|
||||
.then(function(data) {
|
||||
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;
|
||||
@@ -63,50 +113,67 @@ async function getPlaylist(url) {
|
||||
playlist.id = playlistId;
|
||||
playlist.type = "spotify";
|
||||
|
||||
const tracks = info.tracks.items;
|
||||
tracks.forEach(async function(track) {
|
||||
for(const track of info.tracks.items) {
|
||||
playlist.songs.push(track.track);
|
||||
}
|
||||
|
||||
var trackName = track.track.name;
|
||||
var artistName = track.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.track.name;
|
||||
song.author = track.track.artists[0].name;
|
||||
song.url = urlYoutubeFounded;
|
||||
song.thumbnail = track.track.album.images[0].url;
|
||||
song.id = track.track.id;
|
||||
song.duration = track.track.duration_ms / 1000;
|
||||
song.readduration = getReadableDuration(track.track.duration_ms);
|
||||
|
||||
playlist.duration += track.track.duration_ms;
|
||||
|
||||
|
||||
playlist.songs.push(song);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
playlist.readduration = getReadableDuration(playlist.duration);
|
||||
return playlist;
|
||||
|
||||
}, function(err) {
|
||||
clog.error('Une erreur s\'est produite lors de la récupération de la playlist');
|
||||
clog.error(err);
|
||||
});
|
||||
} 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}
|
||||
module.exports = {getSong, getAlbum, getPlaylist, getTracks}
|
@@ -1,35 +1,28 @@
|
||||
const {LogType} = require('loguix');
|
||||
const { LogType } = require('loguix');
|
||||
const clog = new LogType("YoutubeInformation");
|
||||
const config = require('../utils/Database/Configuration');
|
||||
const YOUTUBE_API_KEY = config.getYoutubeApiKey()
|
||||
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) {
|
||||
// Check Query not null and a string
|
||||
if(query === null && typeof query !== 'string') {
|
||||
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;
|
||||
}
|
||||
// * Fetch
|
||||
try {
|
||||
const response = await fetch(`https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=${encodeURIComponent(query)}&key=${YOUTUBE_API_KEY}`);
|
||||
const data = await response.json();
|
||||
|
||||
var videoLink = null
|
||||
const videoId = data.items[0]?.id.videoId;
|
||||
if(videoId) videoLink = `https://www.youtube.com/watch?v=${videoId}`;
|
||||
if(videoLink === 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(videoLink);
|
||||
|
||||
const song = await getVideo(video.url);
|
||||
return song;
|
||||
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche YouTube: ' + error);
|
||||
return null;
|
||||
@@ -37,103 +30,72 @@ getQuery(query) {
|
||||
}
|
||||
|
||||
async function getVideo(url) {
|
||||
// Extract video ID from URL if it exists and is valid (11 characters) and if not return "NOT_VALID"
|
||||
// Extract id from youtu.be youtube.com and music.youtube.com
|
||||
const videoId = url.match(/(?:youtu\.be\/|youtube\.com\/|music\.youtube\.com\/)(?:watch\?v=)?([a-zA-Z0-9_-]{11})/);
|
||||
if(videoId === null) {
|
||||
if (videoId === null) {
|
||||
clog.error("Impossible de récupérer l'identifiant de la vidéo YouTube à partir de l'URL");
|
||||
return null;
|
||||
}
|
||||
// Fetch video information
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${videoId[1]}&key=${YOUTUBE_API_KEY}`);
|
||||
const data = await response.json();
|
||||
const searchResults = await ytsr(videoId[1], { limit: 1 });
|
||||
const video = searchResults.items.find(item => item.type === 'video');
|
||||
|
||||
const video = data.items[0];
|
||||
if(video) {
|
||||
|
||||
const songReturn = new Song()
|
||||
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);
|
||||
clog.error('Erreur lors de la recherche de la vidéo YouTube:' + error);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
async function getPlaylist(url) {
|
||||
// Check Query not null and a string
|
||||
if(url === null && typeof url !== 'string') {
|
||||
if (url === null || typeof url !== 'string') {
|
||||
clog.error("Impossible de rechercher une playlist YouTube, car la requête est nulle");
|
||||
return null;
|
||||
}
|
||||
// * Fetch
|
||||
|
||||
try {
|
||||
// For Playlist
|
||||
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 vidéo YouTube à partir de l'URL");
|
||||
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 response = await fetch(`https://www.googleapis.com/youtube/v3/search?part=snippet&type=playlist&q=${encodeURIComponent(playlistId[1])}&key=${YOUTUBE_API_KEY}`);
|
||||
const data = await response.json();
|
||||
|
||||
if(data.items.length === 0) {
|
||||
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 = data.items[0].snippet.channelTitle
|
||||
playlist.authorId = data.items[0].snippet.channelId
|
||||
playlist.title = data.items[0].snippet.title
|
||||
playlist.thumbnail = data.items[0].snippet.thumbnails.high.url
|
||||
playlist.description = data.items[0].snippet.description
|
||||
playlist.url = `https://www.youtube.com/playlist?list=${playlistId[1]}`
|
||||
playlist.id = playlistId[1]
|
||||
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];
|
||||
|
||||
|
||||
|
||||
|
||||
// Get all songs from playlist
|
||||
|
||||
const responsePlaylist = await fetch(`https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=${playlistId[1]}&key=${YOUTUBE_API_KEY}&maxResults=100`);
|
||||
const dataPlaylist = await responsePlaylist.json();
|
||||
|
||||
if(dataPlaylist.items.length === 0) {
|
||||
clog.error("Impossible de récupérer les vidéos de la playlist YouTube à partir de l'identifiant ou la playlist est vide");
|
||||
return null;
|
||||
for (const video of playlistInfo.videos) {
|
||||
const song = new Song();
|
||||
await song.processYoutubeVideo(video, true);
|
||||
playlist.duration += song.duration;
|
||||
playlist.songs.push(song);
|
||||
}
|
||||
|
||||
for (const video of dataPlaylist.items) {
|
||||
const song = new Song()
|
||||
video.id = video.snippet.resourceId.videoId
|
||||
await song.processYoutubeVideo(video)
|
||||
//? Add seconds to playlist duration
|
||||
playlist.duration += song.duration
|
||||
playlist.songs.push(song)
|
||||
}
|
||||
playlist.readduration = getReadableDuration(playlist.duration)
|
||||
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}
|
||||
module.exports = { getQuery, getVideo, getPlaylist };
|
||||
|
@@ -3,6 +3,7 @@ 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) {
|
||||
@@ -25,16 +26,18 @@ async function search(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
|
||||
|
@@ -9,11 +9,11 @@ async function play(instance, song) {
|
||||
instance.player = createAudioPlayer()
|
||||
instance.generatePlayerEvents()
|
||||
const player = instance.player
|
||||
song.resource = await createAudioResource(song.url, {
|
||||
var resource = await createAudioResource(song.url, {
|
||||
inputType: StreamType.Arbitrary
|
||||
}) // Remplace par ton fichier audio
|
||||
|
||||
player.play(song.resource);
|
||||
player.play(resource);
|
||||
instance.connection.subscribe(player);
|
||||
clog.log(`GUILD : ${instance.guildId} - Lecture de la musique (Media): ${song.title} - id : ${song.id}`)
|
||||
|
||||
|
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}
|
@@ -20,13 +20,13 @@ async function play(instance, song) {
|
||||
});
|
||||
|
||||
// Add compressor to the audio resource
|
||||
song.resource = createAudioResource(stream);
|
||||
var resource = createAudioResource(stream);
|
||||
|
||||
|
||||
|
||||
player.play(song.resource);
|
||||
player.play(resource);
|
||||
instance.connection.subscribe(player);
|
||||
clog.log(`GUILD : ${instance.guildId} - Lecture de la musique (Media): ${song.title} - id : ${song.id}`)
|
||||
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)
|
||||
|
@@ -7,7 +7,7 @@ const clog = new LogType("Signal")
|
||||
|
||||
const media = require('./Method/Media');
|
||||
const youtube = require('./Method/Youtube');
|
||||
const Activity = require('../discord/Activity');
|
||||
const soundcloud = require('./Method/Soundcloud');
|
||||
|
||||
const AllPlayers = new Map()
|
||||
|
||||
@@ -15,6 +15,7 @@ class Player {
|
||||
connection;
|
||||
player;
|
||||
guildId;
|
||||
channelId;
|
||||
queue;
|
||||
constructor(guildId) {
|
||||
if(this.guildId === null) {
|
||||
@@ -45,6 +46,8 @@ class Player {
|
||||
selfMute: false
|
||||
});
|
||||
|
||||
this.channelId = channel.id
|
||||
|
||||
this.player = createAudioPlayer()
|
||||
this.generatePlayerEvents()
|
||||
|
||||
@@ -62,6 +65,8 @@ class Player {
|
||||
|
||||
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);
|
||||
@@ -109,7 +114,9 @@ class Player {
|
||||
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
|
||||
}
|
||||
@@ -151,6 +158,7 @@ class Player {
|
||||
}
|
||||
|
||||
async leave() {
|
||||
const Activity = require('../discord/Activity');
|
||||
if(this.checkConnection()) return
|
||||
if(this.queue.current != null) {
|
||||
this.queue.addPreviousSong(this.queue.current)
|
||||
@@ -158,6 +166,9 @@ class Player {
|
||||
// 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)
|
||||
|
@@ -2,8 +2,7 @@ const {LogType} = require('loguix')
|
||||
|
||||
const clog = new LogType("Song")
|
||||
const MediaInformation = require('../media/MediaInformation')
|
||||
const YoutubeDuration = require('../utils/YoutubeDuration');
|
||||
const { getReadableDuration } = require('../utils/TimeConverter');
|
||||
const { getReadableDuration, getSecondsDuration } = require('../utils/TimeConverter');
|
||||
|
||||
class Song {
|
||||
title = "Aucun titre";
|
||||
@@ -15,7 +14,6 @@ class Song {
|
||||
duration;
|
||||
readduration;
|
||||
type;
|
||||
resource;
|
||||
|
||||
constructor(properties) {
|
||||
if(properties) {
|
||||
@@ -50,17 +48,30 @@ class Song {
|
||||
|
||||
}
|
||||
|
||||
async processYoutubeVideo(video) {
|
||||
this.title = video.snippet.title
|
||||
this.author = video.snippet.channelTitle
|
||||
this.authorId = video.snippet.channelId
|
||||
this.thumbnail = video.snippet.thumbnails.standard.url
|
||||
this.url = `https://www.youtube.com/watch?v=${video.id}`
|
||||
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 = await YoutubeDuration.getDurationVideo(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
|
||||
}
|
||||
|
@@ -18,8 +18,8 @@ function getQueryType(url) {
|
||||
|
||||
// Check if it's a Soundcloud link
|
||||
|
||||
if(Links.regex.soundcloud.track.test(url)) return QueryType.SOUNDCLOUD_TRACK
|
||||
if(Links.regex.soundcloud.playlist.test(url)) return QueryType.SOUNDCLOUD_PLAYLIST
|
||||
if(Links.regex.soundcloud.track.test(url)) return QueryType.SOUNDCLOUD_TRACK
|
||||
|
||||
|
||||
|
||||
|
@@ -29,4 +29,18 @@ function getReadableDuration(duration) {
|
||||
return max
|
||||
}
|
||||
|
||||
module.exports = {getReadableDuration}
|
||||
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}
|
@@ -1,45 +0,0 @@
|
||||
const config = require('../utils/Database/Configuration');
|
||||
const YOUTUBE_API_KEY = config.getYoutubeApiKey()
|
||||
|
||||
async function getDurationVideo(videoId) {
|
||||
const clog = require("loguix").getInstance("YoutubeInformation");
|
||||
// Check videoId if valid
|
||||
if(videoId === null && typeof videoId !== 'string') {
|
||||
clog.error("Impossible de récupérer la durée de la vidéo YouTube, car l'identifiant est nul ou n'est pas une chaîne de caractères");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch video information
|
||||
try {
|
||||
const response = await fetch(`https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=${videoId}&key=${YOUTUBE_API_KEY}`);
|
||||
const data = await response.json();
|
||||
const video = data.items[0];
|
||||
if(video) {
|
||||
|
||||
if(video.contentDetails.duration == "P0D") return "LIVE";
|
||||
const duration = video.contentDetails.duration;
|
||||
//Convert ISO 8601 duration to seconds
|
||||
return parseDuration(duration);
|
||||
|
||||
|
||||
} else {
|
||||
clog.error("Impossible de récupérer la durée de la vidéo YouTube à partir de l'identifiant");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
clog.error('Erreur lors de la recherche de la durée de la vidéo YouTube:', error);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function parseDuration(duration) {
|
||||
const match = duration.match(/PT(\d+H)?(\d+M)?(\d+S)?/);
|
||||
const hours = parseInt(match[1]) || 0;
|
||||
const minutes = parseInt(match[2]) || 0;
|
||||
const seconds = parseInt(match[3]) || 0;
|
||||
return hours * 3600 + minutes * 60 + seconds;
|
||||
}
|
||||
|
||||
module.exports = {getDurationVideo}
|
Reference in New Issue
Block a user