Un bot musical avec discord js

Logo discordjs

Cet article remplace mon précédent article « Un music bot avec Discord.js en 5 minutes ! » qui utilisait un paquet npm devenu aujourd’hui obsolète.
Je vous propose donc de suivre cette nouvelle méthode afin de créer vous même votre propre bot musical.
L’avantage de cette technique est que vous contrôlez tout de A à Z, tout n’est plus fait de façon automatique comme dans le précédent tuto.

Comme mon précédent article, je vous laisse suivre le tuto « Discord Js: Créer votre propre bot » afin d’avoir une base de travail.

Sans plus attendre, initialisons notre projet !

Bob l'éponge et son bot musical
Place à la musique !

Installation des dépendances

Tout d’abords, rendons nous dans le dossier de notre bot que vous obtenez après avoir suivi le tuto « Discord Js: Créer votre propre bot« .

Une fois à l’intérieur, nous allons installer les dépendances nécessaire pour que tout fonctionne:

npm install discord.js ffmpeg fluent-ffmpeg ffmpeg-static @discordjs/opus ytdl-core --save

Configuration

Nous allons déplacer les constantes de configuration dans un fichier qui s’appellera config.json et que l’on placera à côté de notre fichier index.js.
On va donc y définir deux valeurs: prefix et token.
Prefix correspond au caractère devant préfixer les commandes du bot pour être reconnues.
Token correspond au token discord obtenu pendant la création du bot sur le site de discord.

Voici le contenu du fichier config.json:

{
  "prefix": "&",
  "token": "votre-token"
}

Place au code de notre bot musical

Tout se passe désormais dans le fichier index.js.
Dans un premier temps, importons les dépendances dont nous aurons besoin:

const Discord = require('discord.js');
/// DÉBUT NOUVEAU 
const { prefix, token} = require('./config.json');
const ytdl = require('ytdl-core');
/// FIN NOUVEAU 
const client = new Discord.Client();

Puis nous allons devoir traiter les différentes commandes de l’utilisateur, pour cela on reprend notre code (celui du ping) existant et on l’améliore:

client.on('message', msg => {
  if (msg.content === 'ping') {
    msg.reply('pong');
  }
});

Deviens donc dans un premiers temps:

client.on('message', msg => {
     if (msg.author.bot) return;
     if (!msg.content.startsWith(prefix)) return;

});

Ici on ignore le message si celui viens de notre bot ou si il ne commence pas par notre préfixe.
On créer ensuite une constante représentant la queue du serveur discord grâce à l’id de la guild que l’on récupère sur le message:

const serverQueue = queue.get(message.guild.id);

On continue en en traitant les différentes commandes de notre bot:

if (message.content.startsWith(`${prefix}play`)) {
    // Code pour la commande play
    return;
} else if (message.content.startsWith(`${prefix}skip`)) {
    // Code pour la commande skip
    return;
} else if (message.content.startsWith(`${prefix}stop`)) {
    // Code pour la commande skip
    return;
} else {
    message.channel.send("Vous avez entrez une commande invalide !");
}

Nous remplirons par la suite le code pour chaque commande, préparons avant ça la variable qui représentera la queue de musique.
On créer donc une variable queue de type map:

const Discord = require("discord.js");
const { prefix, token } = require("./config.json");
const ytdl = require("ytdl-core");

const client = new Discord.Client();

// NOUVEAU
const queue = new Map();

Pour information, un objet de type map est itérable dans l’ordre d’arrivé de ses éléments. C’est parfait dans le cas d’une queue de musique à lire !
Vous pouvez consulter la documentation à cette adresse.

Créons ensuite une fonction qui sera appelée lorsque le bot devra jouer une musique. Dans un premier temps, vérifions deux points:

  • L’utilisateur est bien dans un salon vocal
  • Le bot possède bien les permissions de se connecter au salon vocal et d’y parler

Voici ce que cela donne:

async function execute(message, serverQueue) {
  const args = message.content.split(" "); // On récupère les arguments dans le message pour la suite

  const voiceChannel = message.member.voice.channel;
  if (!voiceChannel) // Si l'utilisateur n'est pas dans un salon vocal
    return message.channel.send(
      "Vous devez être dans un salon vocal!"
    );
  const permissions = voiceChannel.permissionsFor(message.client.user); // On récupère les permissions du bot pour le salon vocal
  if (!permissions.has("CONNECT") || !permissions.has("SPEAK")) { // Si le bot n'a pas les permissions
    return message.channel.send(
      "J'ai besoin des permissions pour rejoindre le salon et pour y jouer de la musique!"
    );
  }
}

On continue, toujours dans la fonction execute, on récupère les informations de la musique passé en argument dans le message.
On utilise le paquet ytdl pour récupérer les infos de youtube.
Seuls le titre et l’url de la vidéo nous intéresse, on les stock donc dans une variable song:

const songInfo = await ytdl.getInfo(args[1]);
const song = {
 title: songInfo.videoDetails.title,
 url: songInfo.videoDetails.video_url,
};

Nous ajoutons ensuite la musique à notre queue de musique, voyons d’abord le cas où serverQueue existe:

if (!serverQueue) {

}else {
 serverQueue.songs.push(song);
 console.log(serverQueue.songs);
 return message.channel.send(`${song.title} has been added to the queue!`);
}

Maintenant si serverQueue n’existe pas, on le créer, on l’ajoute à la queue global et on push la musique dedans.
Ensuite on connecte le bot au salon vocal et on lance la musique:

const queueConstruct = {
 textChannel: message.channel,
 voiceChannel: voiceChannel,
 connection: null,
 songs: [],
 volume: 1,
 playing: true,
};

// On ajoute la queue du serveur dans la queue globale:
queue.set(message.guild.id, queueConstruct);
// On y ajoute la musique
queueContruct.songs.push(song);

try {
 // On connecte le bot au salon vocal et on sauvegarde l'objet connection
 var connection = await voiceChannel.join();
 queueContruct.connection = connection;
 // On lance la musique
 play(message.guild, queueContruct.songs[0]);
} catch (err) {
 //On affiche les messages d'erreur si le bot ne réussi pas à se connecter, on supprime également la queue de lecture
 console.log(err);
 queue.delete(message.guild.id);
 return message.channel.send(err);
}

Jouer une musique

On va implémenter la fonction appelé lorsque l’utilisateur tape la commande play.
Tout d’abord on va compléter le passage où l’on surveille les différentes commandes tapées par l’utilisateur en y rajoutant nos appels de fonction:

if (message.content.startsWith(`${prefix}play`)) {
    execute(message, serverQueue); // On appel execute qui soit initialise et lance la musique soit ajoute à la queue la musique
    return;
  } else if (message.content.startsWith(`${prefix}skip`)) {
    skip(message, serverQueue); // Permettra de passer à la musique suivante
    return;
  } else if (message.content.startsWith(`${prefix}stop`)) {
    stop(message, serverQueue); // Permettra de stopper la lecture
    return;
  } else {
    message.channel.send("You need to enter a valid command!");
  }

Voici le contenu de la fonction play:

function play(guild, song) {
  const serverQueue = queue.get(guild.id); // On récupère la queue de lecture
  if (!song) { // Si la musique que l'utilisateur veux lancer n'existe pas on annule tout et on supprime la queue de lecture
    serverQueue.voiceChannel.leave();
    queue.delete(guild.id);
    return;
  }

// On lance la musique 
  const dispatcher = serverQueue.connection
    .play(ytdl(song.url, { filter: 'audioonly' }))
    .on("finish", () => { // On écoute l'événement de fin de musique
      serverQueue.songs.shift(); // On passe à la musique suivante quand la courante se termine 
      play(guild, serverQueue.songs[0]);
    })
    .on("error", error => console.error(error));
  dispatcher.setVolume(serverQueue.volume); // On définie le volume
  serverQueue.textChannel.send(`Démarrage de la musique: **${song.title}**`);
}

Passer une musique

C’est maintenant le tour de la fonction skip:

function skip(message, serverQueue) {
  if (!message.member.voice.channel) // on vérifie que l'utilisateur est bien dans un salon vocal pour skip
    return message.channel.send(
      "Vous devez être dans un salon vocal pour passer une musique!"
    );
  if (!serverQueue) // On vérifie si une musique est en cours
    return message.channel.send("Aucune lecture de musique en cours !");
  serverQueue.connection.dispatcher.end(); // On termine la musique courante, ce qui lance la suivante grâce à l'écoute d'événement finish
}

Stopper la lecture

La fonction stop enfin ! Elle est quasiment la même que celle pour passer une musique.
On vide juste la liste des musique avant de terminer la courante:

function stop(message, serverQueue) {
 if (!message.member.voice.channel) // on vérifie que l'utilisateur est bien dans un salon vocal pour skip
    return message.channel.send(
      "Vous devez être dans un salon vocal pour stopper la lecture!"
    );
  if (!serverQueue) // On vérifie si une musique est en cours
    return message.channel.send("Aucune lecture de musique en cours !");
    serverQueue.songs = [];
    serverQueue.connection.dispatcher.end();
}

Conclusion

Au final voici le code que l’on obtiens pour notre bot musical:

const Discord         = require("discord.js");
const {prefix, token} = require("./config.json");
const ytdl            = require("ytdl-core");

const client = new Discord.Client();

const queue = new Map();

client.once("ready", () => {
        console.log("Ready!");
});

client.once("reconnecting", () => {
        console.log("Reconnecting!");
});

client.once("disconnect", () => {
        console.log("Disconnect!");
});

client.on("message", async message => {
        if (message.author.bot) {
                return;
        }
        if (!message.content.startsWith(prefix)) {
                return;
        }

        const serverQueue = queue.get(message.guild.id);

        if (message.content.startsWith(`${prefix}play`)) {
                execute(message, serverQueue); // On appel execute qui soit initialise et lance la musique soit ajoute à la queue la musique
                return;
        }
        else if (message.content.startsWith(`${prefix}skip`)) {
                skip(message, serverQueue); // Permettra de passer à la musique suivante
                return;
        }
        else if (message.content.startsWith(`${prefix}stop`)) {
                stop(message, serverQueue); // Permettra de stopper la lecture
                return;
        }
        else {
                message.channel.send("You need to enter a valid command!");
        }

});

async function execute(message, serverQueue) {
        const args = message.content.split(" "); // On récupère les arguments dans le message pour la suite

        const voiceChannel = message.member.voice.channel;
        if (!voiceChannel) // Si l'utilisateur n'est pas dans un salon vocal
        {
                return message.channel.send(
                    "Vous devez être dans un salon vocal!"
                );
        }
        const permissions = voiceChannel.permissionsFor(message.client.user); // On récupère les permissions du bot pour le salon vocal
        if (!permissions.has("CONNECT") || !permissions.has("SPEAK")) { // Si le bot n'a pas les permissions
                return message.channel.send(
                    "J'ai besoin des permissions pour rejoindre le salon et pour y jouer de la musique!"
                );
        }

        const songInfo = await ytdl.getInfo(args[1]);
        const song     = {
                title: songInfo.videoDetails.title,
                url  : songInfo.videoDetails.video_url,
        };

        if (!serverQueue) {
                const queueConstruct = {
                        textChannel : message.channel,
                        voiceChannel: voiceChannel,
                        connection  : null,
                        songs       : [],
                        volume      : 1,
                        playing     : true,
                };

                // On ajoute la queue du serveur dans la queue globale:
                queue.set(message.guild.id, queueConstruct);
                // On y ajoute la musique
                queueConstruct.songs.push(song);

                try {
                        // On connecte le bot au salon vocal et on sauvegarde l'objet connection
                        var connection           = await voiceChannel.join();
                        queueConstruct.connection = connection;
                        // On lance la musique
                        play(message.guild, queueConstruct.songs[0]);
                }
                catch (err) {
                        //On affiche les messages d'erreur si le bot ne réussi pas à se connecter, on supprime également la queue de lecture
                        console.log(err);
                        queue.delete(message.guild.id);
                        return message.channel.send(err);
                }
        }
        else {
                serverQueue.songs.push(song);
                console.log(serverQueue.songs);
                return message.channel.send(`${song.title} has been added to the queue!`);
        }

}

function skip(message, serverQueue) {
        if (!message.member.voice.channel) // on vérifie que l'utilisateur est bien dans un salon vocal pour skip
        {
                return message.channel.send(
                    "Vous devez être dans un salon vocal pour passer une musique!"
                );
        }
        if (!serverQueue) // On vérifie si une musique est en cours
        {
                return message.channel.send("Aucune lecture de musique en cours !");
        }
        serverQueue.connection.dispatcher.end(); // On termine la musique courante, ce qui lance la suivante grâce à l'écoute d'événement
                                                 // finish
}

function stop(message, serverQueue) {
        if (!message.member.voice.channel) // on vérifie que l'utilisateur est bien dans un salon vocal pour skip
        {
                return message.channel.send(
                    "Vous devez être dans un salon vocal pour stopper la lecture!"
                );
        }
        if (!serverQueue) // On vérifie si une musique est en cours
        {
                return message.channel.send("Aucune lecture de musique en cours !");
        }
        serverQueue.songs = [];
        serverQueue.connection.dispatcher.end();
}

function play(guild, song) {
        console.log(song);
        const serverQueue = queue.get(guild.id); // On récupère la queue de lecture
        if (!song) { // Si la musique que l'utilisateur veux lancer n'existe pas on annule tout et on supprime la queue de lecture
                serverQueue.voiceChannel.leave();
                queue.delete(guild.id);
                return;
        }
        // On lance la musique
        const dispatcher = serverQueue.connection
            .play(ytdl(song.url, { filter: 'audioonly' }))
            .on("finish", () => { // On écoute l'événement de fin de musique
                    serverQueue.songs.shift(); // On passe à la musique suivante quand la courante se termine
                    play(guild, serverQueue.songs[0]);
            })
            .on("error", error => console.error(error));
        dispatcher.setVolume(1); // On définie le volume
        serverQueue.textChannel.send(`Démarrage de la musique: **${song.title}**`);
}

client.login(token);

Sources


Articles récents

Un bot musical avec discord js

30 commentaires sur “Un bot musical avec discord js

    1. Je viens de rajouter une partie configuration vers le début de l’article afin d’expliquer comment rajouter le fichier config.json avec le prefix à l’intérieur 🙂
      Passe une très bonne journée et reviens vers moi si tu as le moindre soucis !

    1. Bonjour !
      Peux tu me dire les erreurs que tu as avec ton bot ?
      Car je viens d’essayer et il fonctionne toujours pour moi, on va essayer de comprendre ensemble pourquoi cela ne fonctionne plus chez toi 🙂

      1. TypeError [ERR_INVALID_ARG_TYPE]: The « url » argument must be of type string. Received undefined
        at validateString (internal/validators.js:120:11)
        at Url.parse (url.js:159:3)
        at Object.urlParse [as parse] (url.js:154:13)
        at Object.exports.getURLVideoID (/home/container/node_modules/ytdl-core/lib/url-utils.js:30:22)
        at Object.exports.getVideoID (/home/container/node_modules/ytdl-core/lib/url-utils.js:63:20)
        at Function.exports. [as getInfo] (/home/container/node_modules/ytdl-core/lib/info.js:312:23)
        at ytdl (/home/container/node_modules/ytdl-core/lib/index.js:19:8)
        at play (/home/container/index.js:645:11)
        at execute (/home/container/index.js:567:7)
        at processTicksAndRejections (internal/process/task_queues.js:97:5) {
        code: ‘ERR_INVALID_ARG_TYPE’
        }
        (node:13) UnhandledPromiseRejectionWarning: DiscordAPIError: Cannot send an empty message
        at RequestHandler.execute (/home/container/node_modules/discord.js/src/rest/RequestHandler.js:170:25)
        at processTicksAndRejections (internal/process/task_queues.js:97:5)
        (node:13) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `–unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
        (node:13) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

      2. Nan c’est bon j’ai trouver le probleme

        Probleme :
        const songInfo = await ytdl.getInfo(args[1]);
        const song = {
        title: songInfo.title,
        url: songInfo.video_url
        };

        Resolution :
        const songInfo = await ytdl.getInfo(args[1]);
        const song = {
        title: songInfo.videoDetails.title,
        url : songInfo.videoDetails.video_url,
        };

  1. Nan c’est bon j’ai trouver le probleme

    Probleme :
    const songInfo = await ytdl.getInfo(args[1]);
    const song = {
    title: songInfo.title,
    url: songInfo.video_url
    };

    Resolution :
    const songInfo = await ytdl.getInfo(args[1]);
    const song = {
    title: songInfo.videoDetails.title,
    url : songInfo.videoDetails.video_url,
    };

  2. Bonjour Sa serais possible de faire un system de recherche en gros !play rechercheyoutube
    est sa met la musique ? si possible je vous remercie

    1. C’est possible mais il faut tout d’abord utiliser l’API de youtube afin de faire des recherches: https://developers.google.com/youtube/v3/docs/search/list
      Après en fonction des résultats on peux imaginer proposer les résultats à choisir à l’utilisateur ou bien lancer le résultat le plus pertinent 🙂
      Je vais voir à l’avenir à en faire un autre article mais en attendant je suis disponible si jamais tu as des questions !

  3. Bonjour est ce que cette commande et d’actualité et deuxiement est ce que on peux faire exemple !play (search) car sur tout les tuto y’avais des commande de musique que avec des URL

    1. Bonjour Abey !
      Pour le moment la commande search n’est pas disponible mais je vais écrire un nouvel article pour ça courant janvier
      Je te laisse venir checker ça dans une dizaine de jours 🙂

  4. Bonjour j’obtiens sa comme erreur quand je veux joué une musique

    (node:19024) UnhandledPromiseRejectionWarning: TypeError: Cannot read property ‘channel’ of undefined
    at execute (c:\Users\camba\OneDrive\Bureau\bot\index.js:201:45)
    at Client. (c:\Users\camba\OneDrive\Bureau\bot\index.js:181:11)
    at Client.emit (events.js:327:22)
    at MessageCreateHandler.handle (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\packets\handlers\MessageCreate.js:9:34)
    at WebSocketPacketManager.handle (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\packets\WebSocketPacketManager.js:108:65)
    at WebSocketConnection.onPacket (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:336:35)
    at WebSocketConnection.onMessage (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:299:17)
    at WebSocket.onMessage (c:\Users\camba\OneDrive\Bureau\bot\node_modules\ws\lib\event-target.js:120:16)
    at WebSocket.emit (events.js:315:20)
    at Receiver.receiverOnMessage (c:\Users\camba\OneDrive\Bureau\bot\node_modules\ws\lib\websocket.js:789:20)
    /internal/process/warning.js:32
    (node:19024) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `–unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
    /internal/process/warning.js:32
    (node:19024) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
    /internal/process/warning.js:32
    Error: Error code: 403 | Response from Google: {
    « error »: {
    « code »: 403,
    « message »: « The request cannot be completed because you have exceeded your \u003ca href=\ »/youtube/v3/getting-started#quota\ »\u003equota\u003c/a\u003e. »,
    « errors »: [
    {
    « message »: « The request cannot be completed because you have exceeded your \u003ca href=\ »/youtube/v3/getting-started#quota\ »\u003equota\u003c/a\u003e. »,
    « domain »: « youtube.quota »,
    « reason »: « quotaExceeded »
    }
    ]
    }
    }

    at IncomingMessage. (c:\Users\camba\OneDrive\Bureau\bot\node_modules\ytsearcher\lib\struct\YTSearch.js:103:36)
    at IncomingMessage.emit (/events.js:327:22)
    at endReadableNT (/_stream_readable.js:1201:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {stack: ‘Error: Error code: 403 | Response from Google…tions (internal/process/task_queues.js:84:21)’, message: ‘Error code: 403 | Response from Google: {
    …eason »: « quotaExceeded »
    }
    ]
    }
    }
    ‘}

    1. Bonjour Raeve !
      Je vous plusieurs soucis avec ce que tu me montres, mais tout d’abord il semblerait que tu ai épuisé ton quota de recherche youtube.
      As tu bien généré une clé spécifique à ce bot ?

  5. (node:17160) UnhandledPromiseRejectionWarning: TypeError: Cannot read property ‘channel’ of undefined
    at execute (c:\Users\camba\OneDrive\Bureau\bot\index.js:200:45)
    at Client. (c:\Users\camba\OneDrive\Bureau\bot\index.js:180:11)
    at Client.emit (events.js:327:22)
    at MessageCreateHandler.handle (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\packets\handlers\MessageCreate.js:9:34)
    at WebSocketPacketManager.handle (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\packets\WebSocketPacketManager.js:108:65)
    at WebSocketConnection.onPacket (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:336:35)
    at WebSocketConnection.onMessage (c:\Users\camba\OneDrive\Bureau\bot\node_modules\discord.js\src\client\websocket\WebSocketConnection.js:299:17)
    at WebSocket.onMessage (c:\Users\camba\OneDrive\Bureau\bot\node_modules\ws\lib\event-target.js:120:16)
    at WebSocket.emit (events.js:315:20)
    at Receiver.receiverOnMessage (c:\Users\camba\OneDrive\Bureau\bot\node_modules\ws\lib\websocket.js:789:20)
    (node:17160) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `–unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
    /internal/process/warning.js:32
    (node:17160) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

    Error: Error code: 403 | Response from Google: {
    « error »: {
    « code »: 403,
    « message »: « The request cannot be completed because you have exceeded your \u003ca href=\ »/youtube/v3/getting-started#quota\ »\u003equota\u003c/a\u003e. »,
    « errors »: [
    {
    « message »: « The request cannot be completed because you have exceeded your \u003ca href=\ »/youtube/v3/getting-started#quota\ »\u003equota\u003c/a\u003e. »,
    « domain »: « youtube.quota »,
    « reason »: « quotaExceeded »
    }
    ]
    }
    }

    at IncomingMessage. (c:\Users\camba\OneDrive\Bureau\bot\node_modules\ytsearcher\lib\struct\YTSearch.js:103:36)
    at IncomingMessage.emit (/events.js:327:22)
    at endReadableNT (/_stream_readable.js:1201:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {stack: ‘Error: Error code: 403 | Response from Google…tions (internal/process/task_queues.js:84:21)’, message: ‘Error code: 403 | Response from Google: {
    …eason »: « quotaExceeded »
    }
    ]
    }
    }
    ‘}

  6. bonsoir j’ai un probleme

    Uncaught c:\Users\camba\OneDrive\Bureau\bot\index.js:243
    var connection = await voiceChannel.join();
    ^^^^^

    SyntaxError: await is only valid in async function

    1. Bonjour !
      Aucune clé API Youtube n’est nécessaire avec ytdl-core, il va juste venir récupérer les informations de la vidéo Youtube sans passer par l’api Youtube 🙂

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Retour en haut