backend-0.2.0 => main #1

Merged
raphix merged 6 commits from backend-0.2.0 into main 2025-03-01 17:03:17 +00:00
16 changed files with 473 additions and 228 deletions
Showing only changes of commit 2a934d14ae - Show all commits

View File

@@ -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"
}
}
}
}

View File

@@ -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"
}
}

View File

@@ -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)
}
})
}
})

View File

@@ -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)
@@ -22,35 +23,23 @@ 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('**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 {
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)
}
}
embed.send(interaction)
})
}, [{type: "STRING", name: "url", description: "Recherche / Lien audio (Youtube / Soundclound / Spotify)", required: true},

View 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}

View File

@@ -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;
@@ -62,51 +112,68 @@ async function getPlaylist(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;
const tracks = info.tracks.items;
tracks.forEach(async function(track) {
var trackName = track.track.name;
var artistName = track.track.artists[0].name;
var queryForYoutube = `${trackName} - ${artistName}`;
} catch (error) {
clog.error("Impossible de récupérer les informations de l'album Spotify à partir de l'URL");
clog.error(error);
return null;
}
}
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);
async function getTracks(playlist) {
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);
const tracks = playlist.songs
playlistSongs = [];
for(const track of tracks) {
playlist.duration += track.track.duration_ms;
playlist.songs.push(song);
}
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;
}, function(err) {
clog.error('Une erreur s\'est produite lors de la récupération de la playlist');
clog.error(err);
});
}
module.exports = {getSong, getAlbum, getPlaylist}
module.exports = {getSong, getAlbum, getPlaylist, getTracks}

View File

@@ -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) {
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 video = data.items[0];
if(video) {
const songReturn = new Song()
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);
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 };

View File

@@ -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

View File

@@ -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}`)

View 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}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,20 +48,33 @@ 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
}
}
}

View File

@@ -17,10 +17,10 @@ function getQueryType(url) {
if(Links.regex.spotify.song.test(url)) return QueryType.SPOTIFY_SONG
// 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.playlist.test(url)) return QueryType.SOUNDCLOUD_PLAYLIST
if(Links.regex.soundcloud.track.test(url)) return QueryType.SOUNDCLOUD_TRACK
return QueryType.YOUTUBE_SEARCH

View File

@@ -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}

View File

@@ -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}