(sock, message, args, context)
| 30 | description: 'Download song from YouTube (MP3)', |
| 31 | usage: '.song <song name | youtube link>', |
| 32 | async handler(sock, message, args, context) { |
| 33 | const chatId = context.chatId || message.key.remoteJid; |
| 34 | const query = args.join(' ').trim(); |
| 35 | if (!query) |
| 36 | return sock.sendMessage(chatId, { text: '🎵 *Song Downloader*\n\nUsage:\n.song <song name | YouTube link>' }, { quoted: message }); |
| 37 | try { |
| 38 | let video; |
| 39 | if (query.includes('youtube.com') || query.includes('youtu.be')) { |
| 40 | video = { url: query }; |
| 41 | } |
| 42 | else { |
| 43 | const { videos } = await yts(query); |
| 44 | if (!videos?.length) |
| 45 | return sock.sendMessage(chatId, { text: '❌ No results found.' }, { quoted: message }); |
| 46 | video = videos[0]; |
| 47 | } |
| 48 | if (video.thumbnail) { |
| 49 | await sock.sendMessage(chatId, { |
| 50 | image: { url: video.thumbnail }, |
| 51 | caption: `🎶 *${video.title || query}*\n⏱ ${video.timestamp || ''}\n\n⏳ Downloading... *(may take up to 30s)*` |
| 52 | }, { quoted: message }); |
| 53 | } |
| 54 | const audio = await downloadWithRetry(video.url); |
| 55 | await sock.sendMessage(chatId, { |
| 56 | audio: { url: audio.downloadUrl }, |
| 57 | mimetype: 'audio/mpeg', |
| 58 | fileName: `${audio.title || video.title || 'song'}.mp3`, |
| 59 | ptt: false |
| 60 | }, { quoted: message }); |
| 61 | } |
| 62 | catch (err) { |
| 63 | console.error('Song plugin error:', err.message); |
| 64 | const reason = err.response?.status === 408 |
| 65 | ? 'Download timed out. Try again.' |
| 66 | : err.message; |
| 67 | await sock.sendMessage(chatId, { text: `❌ Failed: ${reason}` }, { quoted: message }); |
| 68 | } |
| 69 | } |
| 70 | }; |
nothing calls this directly
no test coverage detected