const {createAudioPlayer, createAudioResource, joinVoiceChannel, getVoiceConnection} = require("@discordjs/voice") const {dir} = require("../index") const { AudioPlayerStatus } = require("@discordjs/voice") const YTDlpWrap = require("yt-dlp-wrap").default const ytDlp = new YTDlpWrap(`${dir}/yt-dlp`) const players = {} module.exports.commands = [ { name: "play", description: "Joins your VC and plays music by youtube link.", arguments: ["link", false], // If it's not an OBJECT type, you provide it like that lol. run: async (ctx, link, loop) => { // Getting the guild member to get their voice client const author = ctx.author const member = await ctx.guild.members.fetch(author) // Initiating the channel variable (undefined) let channel; // Checking if the member is in a voice chat if (member.voice) { channel = member.voice.channel } // Returns if the user is not in vc or the bot is already in one if (!channel) { return ctx.reply("You are not in a VC right now.") } else if (ctx.guild.members.me.voice.channel && ctx.guild.members.me.voice.channel.id !== channel.id) { return ctx.reply("Already in a vc.") } let connection; // Checking if the bot is already in the vc if (!ctx.guild.members.me.voice.channel) { // Connecting to the voice channel connection = joinVoiceChannel({ channelId: channel.id, // Jetbrains is very angry over this for no reason? guildId: channel.guild.id, // Or maybe this, it shows String instead of Snowflake. adapterCreator: channel.guild.voiceAdapterCreator, }) } else { // Getting an existing connection connection = getVoiceConnection(ctx.guild.id) } const reply = await ctx.reply("Loading music data.") // Running yt-dlp (getting music title) const metadata = await ytDlp.getVideoInfo(link); await reply.edit(`Downloading ${metadata.title}`) // Running yt-dlp (downloading music) ytDlp.exec([ // Arguments to run yt-dlp with link, '-f', 'best', '-o', `${dir}/music/${metadata.title}.mp3`, '--extract-audio', '--audio-format', 'mp3' ]).on('close', () => { reply.edit(`Playing ${metadata.title}`) // Stopping old audio player if (players[ctx.guild.id]) { players[ctx.guild.id].stop() } // Checking if the bot is still in the VC and if it's the same VC. if (!ctx.guild.members.me.voice || (ctx.guild.members.me.voice && !ctx.guild.members.me.voice.channel)|| (ctx.guild.members.me.voice && ctx.guild.members.me.voice.channel && ctx.guild.members.me.voice.channel.id !== channel.id)) { return reply.edit("Not in a VC anymore, not playing.") } // Connecting audio player const player = createAudioPlayer() // Setting the global player to this players[ctx.guild.id] = player const resource = createAudioResource(`${dir}/music/${metadata.title}.mp3`) // Playing the music with the player player.play(resource) // Subscribing the connection to the player connection.subscribe(player) // Make it play again and again if it's looping if (loop) { player.on("stateChange", (oldState, newState) => { if (newState.status === AudioPlayerStatus.Idle && players[ctx.guild.id] !== undefined) { // We have to create a new resource every time const resource = createAudioResource(`${dir}/music/${metadata.title}.mp3`) // Playing the music with the player player.play(resource) } }) } }) } }, { name: "stop", description: "Stop music", run: (ctx) => { const voice = ctx.guild.members.me.voice // Checking if bot is in a voice channel if (!voice || !players[ctx.guild.id]) { return ctx.reply("Not in a voice channel.") } // Stopping the player players[ctx.guild.id].stop() // Removing the player (needed on line 100 check) players[ctx.guild.id] = undefined } }, { name: "leave", description: "Leave the channel", run: async (ctx) => { const voice = ctx.guild.members.me.voice // Checking if bot is in a voice channel if (!voice || !players[ctx.guild.id]) { return ctx.reply("Not in a voice channel.") } // Stopping the player players[ctx.guild.id].stop() // Removing the player (needed on line 100 check) players[ctx.guild.id] = undefined // Disconnecting, obviously. await voice.disconnect() } } ] module.exports.players = players // For use in events/voice.js