const { addToQueue, playNextInQueue } = require('../utils/queueManager'); const { exec } = require('child_process'); const { v4: uuidv4 } = require('uuid'); const path = require('path'); const { EmbedBuilder } = require('discord.js'); const fetch = require('node-fetch'); const fs = require('fs'); module.exports = { name: 'play', description: 'Play a song from YouTube, a URL, or an uploaded MP3 file', async execute(message, args) { const searchQuery = args.join(' '); const voiceChannel = message.member.voice.channel; if (!voiceChannel) { return message.reply('You need to be in a voice channel to play music!'); } let url, title, tempFilePath; try { if (message.attachments.size > 0) { const attachment = message.attachments.first(); if (attachment.name.endsWith('.mp3')) { title = attachment.name; tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`); const response = await fetch(attachment.url); const buffer = await response.buffer(); fs.writeFileSync(tempFilePath, buffer); const embed = new EmbedBuilder() .setColor('#0099ff') .setTitle('Now Playing') .setDescription(`**${title}**`) .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); message.channel.send({ embeds: [embed] }); addToQueue(message.guild.id, tempFilePath, title); playNextInQueue(message.guild.id, voiceChannel); return; } else { return message.reply('Only MP3 files are supported for uploads.'); } } if (isValidURL(searchQuery)) { url = searchQuery; if (url.endsWith('.mp3')) { title = path.basename(url); tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`); const response = await fetch(url); const buffer = await response.buffer(); fs.writeFileSync(tempFilePath, buffer); } else { tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`); title = await downloadWithYtDlp(url, tempFilePath); } } else { const searchUrl = `ytsearch:${searchQuery}`; const searchResult = await getYtDlpInfo(searchUrl); if (!searchResult || !searchResult.entries || searchResult.entries.length === 0) { return message.reply('No results found for your search query.'); } url = searchResult.entries[0].webpage_url; title = searchResult.entries[0].title; tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`); await downloadWithYtDlp(url, tempFilePath); } const embed = new EmbedBuilder() .setColor('#0099ff') .setTitle('Now Playing') .setDescription(`**${title}**`) .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); message.channel.send({ embeds: [embed] }); addToQueue(message.guild.id, tempFilePath, title); playNextInQueue(message.guild.id, voiceChannel); } catch (error) { console.error('yt-dlp error:', error); message.reply('Failed to retrieve or download the video. Please try again.'); } }, }; function isValidURL(string) { try { new URL(string); return true; } catch (_) { return false; } } function downloadWithYtDlp(url, output) { return new Promise((resolve, reject) => { const command = `yt-dlp --cookies ${path.join(__dirname, '../cookies.txt')} --format bestaudio --output ${output} --quiet --verbose "${url}"`; exec(command, (error, stdout, stderr) => { if (error) { reject(`yt-dlp error: ${stderr}`); } else { resolve(output); } }); }); } function getYtDlpInfo(url) { return new Promise((resolve, reject) => { const command = `yt-dlp --dump-single-json "${url}"`; exec(command, (error, stdout, stderr) => { if (error) { reject(`yt-dlp info error: ${stderr}`); } else { resolve(JSON.parse(stdout)); } }); }); }