const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); const { useMainPlayer, useQueue } = require('discord-player'); module.exports = { data: new SlashCommandBuilder() .setName('music') .setDescription('Music player controls') .addSubcommand(sub => sub.setName('play') .setDescription('Play a song or playlist') .addStringOption(opt => opt.setName('query') .setDescription('Song name, URL, or playlist link') .setRequired(true))) .addSubcommand(sub => sub.setName('skip') .setDescription('Skip the current track')) .addSubcommand(sub => sub.setName('stop') .setDescription('Stop playback and clear the queue')) .addSubcommand(sub => sub.setName('pause') .setDescription('Pause the current track')) .addSubcommand(sub => sub.setName('resume') .setDescription('Resume playback')) .addSubcommand(sub => sub.setName('queue') .setDescription('View the current queue')) .addSubcommand(sub => sub.setName('nowplaying') .setDescription('Show the currently playing track')) .addSubcommand(sub => sub.setName('volume') .setDescription('Adjust the volume') .addIntegerOption(opt => opt.setName('level') .setDescription('Volume level (0-100)') .setRequired(true) .setMinValue(0) .setMaxValue(100))) .addSubcommand(sub => sub.setName('shuffle') .setDescription('Shuffle the queue')) .addSubcommand(sub => sub.setName('loop') .setDescription('Set loop mode') .addStringOption(opt => opt.setName('mode') .setDescription('Loop mode') .setRequired(true) .addChoices( { name: 'Off', value: 'off' }, { name: 'Track', value: 'track' }, { name: 'Queue', value: 'queue' } ))), async execute(interaction, client, supabase) { const subcommand = interaction.options.getSubcommand(); const player = useMainPlayer(); const memberVoice = interaction.member.voice.channel; const botVoice = interaction.guild.members.me.voice.channel; if (subcommand === 'play') { if (!memberVoice) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('You need to be in a voice channel to play music.')], ephemeral: true }); } if (botVoice && botVoice.id !== memberVoice.id) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription(`I'm already playing music in <#${botVoice.id}>. Join that channel or wait until I'm done.`)], ephemeral: true }); } const query = interaction.options.getString('query'); await interaction.deferReply(); try { const result = await player.search(query, { requestedBy: interaction.user }); if (!result.hasTracks()) { return interaction.editReply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription(`No results found for: **${query}**`)] }); } const { track, searchResult } = await player.play(memberVoice, result, { nodeOptions: { metadata: { channel: interaction.channel, client: interaction.guild.members.me, requestedBy: interaction.user }, volume: 50, leaveOnEmpty: true, leaveOnEmptyCooldown: 300000, leaveOnEnd: true, leaveOnEndCooldown: 300000 } }); const embed = new EmbedBuilder() .setColor(0x5865f2) .setAuthor({ name: 'Added to Queue', iconURL: interaction.user.displayAvatarURL() }); if (searchResult.playlist) { embed.setTitle(searchResult.playlist.title) .setURL(searchResult.playlist.url) .setDescription(`Added **${searchResult.tracks.length}** tracks from playlist`) .setThumbnail(searchResult.playlist.thumbnail || track.thumbnail); } else { embed.setTitle(track.title) .setURL(track.url) .setDescription(`**Duration:** ${track.duration}\n**Author:** ${track.author}`) .setThumbnail(track.thumbnail); } return interaction.editReply({ embeds: [embed] }); } catch (error) { console.error('Music play error:', error); let errorMessage = 'Something went wrong while trying to play music.'; if (error.message?.includes('Sign in')) { errorMessage = 'This track requires age verification or is restricted.'; } else if (error.message?.includes('private')) { errorMessage = 'This playlist or video is private.'; } else if (error.message?.includes('unavailable')) { errorMessage = 'This content is unavailable in the current region.'; } else if (error.message?.includes('Could not extract')) { errorMessage = 'Could not load this track. Try a different search term.'; } return interaction.editReply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription(errorMessage)] }); } } const queue = useQueue(interaction.guildId); if (subcommand === 'nowplaying') { if (!queue || !queue.isPlaying()) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('Nothing is currently playing.')], ephemeral: true }); } const track = queue.currentTrack; const progress = queue.node.createProgressBar(); const embed = new EmbedBuilder() .setColor(0x5865f2) .setTitle('Now Playing') .setDescription(`**[${track.title}](${track.url})**\n\n${progress}`) .setThumbnail(track.thumbnail) .addFields( { name: 'Author', value: track.author, inline: true }, { name: 'Duration', value: track.duration, inline: true }, { name: 'Requested By', value: track.requestedBy?.username || 'Unknown', inline: true } ) .setFooter({ text: `Volume: ${queue.node.volume}%` }); return interaction.reply({ embeds: [embed] }); } if (subcommand === 'queue') { if (!queue || !queue.isPlaying()) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('The queue is empty.')], ephemeral: true }); } const current = queue.currentTrack; const tracks = queue.tracks.toArray().slice(0, 10); let description = `**Now Playing:**\n[${current.title}](${current.url}) - \`${current.duration}\`\n\n`; if (tracks.length > 0) { description += '**Up Next:**\n'; description += tracks.map((track, i) => `**${i + 1}.** [${track.title}](${track.url}) - \`${track.duration}\`` ).join('\n'); if (queue.tracks.size > 10) { description += `\n\n*...and ${queue.tracks.size - 10} more tracks*`; } } else { description += '*No more tracks in queue*'; } const embed = new EmbedBuilder() .setColor(0x5865f2) .setTitle(`Queue for ${interaction.guild.name}`) .setDescription(description) .setFooter({ text: `Total: ${queue.tracks.size + 1} tracks | Volume: ${queue.node.volume}%` }); return interaction.reply({ embeds: [embed] }); } if (!queue || !queue.isPlaying()) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('No music is currently playing.')], ephemeral: true }); } if (memberVoice?.id !== botVoice?.id) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('You need to be in the same voice channel as the bot.')], ephemeral: true }); } switch (subcommand) { case 'skip': { const current = queue.currentTrack; queue.node.skip(); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription(`Skipped **${current.title}**`)] }); } case 'stop': { queue.delete(); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription('Stopped playback and cleared the queue.')] }); } case 'pause': { if (queue.node.isPaused()) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('The player is already paused.')], ephemeral: true }); } queue.node.pause(); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription('Paused the player.')] }); } case 'resume': { if (!queue.node.isPaused()) { return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0xff0000) .setDescription('The player is not paused.')], ephemeral: true }); } queue.node.resume(); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription('Resumed playback.')] }); } case 'volume': { const level = interaction.options.getInteger('level'); queue.node.setVolume(level); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription(`Volume set to **${level}%**`)] }); } case 'shuffle': { queue.tracks.shuffle(); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription('Shuffled the queue.')] }); } case 'loop': { const mode = interaction.options.getString('mode'); const { QueueRepeatMode } = require('discord-player'); const modes = { off: QueueRepeatMode.OFF, track: QueueRepeatMode.TRACK, queue: QueueRepeatMode.QUEUE }; queue.setRepeatMode(modes[mode]); return interaction.reply({ embeds: [new EmbedBuilder() .setColor(0x5865f2) .setDescription(`Loop mode set to **${mode}**`)] }); } } } };