const axios = require("axios"); const yts = require("yt-search"); const fs = require("fs"); const path = require("path"); const { alldown } = require("shaon-video-downloader"); const CACHE_DIR = path.join(__dirname, "cache"); function tempFilePath(ext) { if (!fs.existsSync(CACHE_DIR)) { fs.mkdirSync(CACHE_DIR, { recursive: true }); } return path.join( CACHE_DIR, `sing_${Date.now()}_${Math.floor(Math.random() * 10000)}.${ext}` ); } function react(api, messageID, emoji) { try { api.setMessageReaction( emoji, messageID, () => {}, true ); } catch (_) {} } function cleanFileName(name) { return String(name || "song") .replace(/[\/:*?"<>|]/g, "") .replace(/\s+/g, " ") .trim() .slice(0, 100) || "song"; } async function getDownloadLink(videoUrl) { const data = await alldown(videoUrl); if (!data || data.status !== true) { throw new Error( data?.message || data?.error || "Download link paoa jayni" ); } /* shaon-video-downloader response e audio object thakle audio.url use korbo. */ let downloadUrl = null; if (data.audio?.url) { downloadUrl = data.audio.url; } // fallback if (!downloadUrl && data.url) { downloadUrl = data.url; } if (!downloadUrl) { throw new Error("MP3 download link paoa jayni"); } return { downloadUrl, title: data.title || "Unknown Song", author: data.author || "", source: data.source || "YouTube" }; } async function downloadMP3(downloadUrl, filePath) { const response = await axios.get(downloadUrl, { responseType: "stream", timeout: 300000, maxContentLength: Infinity, maxBodyLength: Infinity, headers: { "User-Agent": "Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 Chrome/120.0 Mobile Safari/537.36" } }); const writer = fs.createWriteStream(filePath); await new Promise((resolve, reject) => { let finished = false; const fail = (err) => { if (finished) return; finished = true; try { writer.destroy(); } catch (_) {} try { fs.unlinkSync(filePath); } catch (_) {} reject(err); }; response.data.on("error", fail); writer.on("error", fail); writer.on("finish", () => { if (finished) return; finished = true; resolve(); }); response.data.pipe(writer); }); if (!fs.existsSync(filePath)) { throw new Error("MP3 file create hoyni"); } const stats = fs.statSync(filePath); if (stats.size < 1024) { try { fs.unlinkSync(filePath); } catch (_) {} throw new Error( `Downloaded file too small (${stats.size} bytes)` ); } } async function sendWithRetry(message, msg, retries = 2) { for (let i = 0; i <= retries; i++) { try { return await message.reply(msg); } catch (err) { const errorText = String( err?.message || err || "" ); const is408 = err?.error === 408 || errorText.includes("408") || errorText.toLowerCase().includes("timeout"); if (is408 && i < retries) { console.warn( `[sing] Upload timeout, retrying (${i + 1}/${retries})...` ); await new Promise(resolve => setTimeout(resolve, 2000) ); continue; } throw err; } } } function extractError(err) { if (!err) return "Unknown error"; if (typeof err === "string") { return err; } if (err.message) { return err.message; } try { return JSON.stringify(err); } catch (_) { return "Unknown error"; } } module.exports.config = { name: "sing", aliases: ["song"], version: "2.0.0", author: "SUJON - BOSS", countDown: 5, role: 0, shortDescription: "YouTube theke gaan download", longDescription: "Song name diye YouTube theke MP3 download kore Messenger e pathay", category: "media", guide: { en: "{pn} \n" + "Example: {pn} mann mera" } }; module.exports.onStart = async function ({ api, event, args, message }) { const { messageID } = event; const query = args .join(" ") .trim(); if (!query) { return message.reply( "❌ Song name den.\n\n" + "Example:\n" + "sing mann mera" ); } react(api, messageID, "⏳"); let file = null; try { console.log( `[sing] Searching YouTube: ${query}` ); // YouTube search const search = await yts(query); const video = search.videos?.[0]; if (!video) { react(api, messageID, "❌"); return message.reply( `❌ "${query}" er kono YouTube result paoa jayni.` ); } console.log( `[sing] Found: ${video.title}` ); react(api, messageID, "🔎"); // shaon-video-downloader API/package const info = await getDownloadLink( video.url ); if (!info.downloadUrl) { throw new Error( "MP3 download URL paoa jayni" ); } file = tempFilePath("mp3"); react(api, messageID, "⬇️"); console.log( "[sing] Downloading MP3..." ); await downloadMP3( info.downloadUrl, file ); console.log( "[sing] MP3 download complete" ); const fileName = cleanFileName( info.title || video.title ); react(api, messageID, "📤"); await sendWithRetry(message, { body: `🎵 ${fileName}\n\n` + `👤 ${info.author || video.author?.name || "Unknown"}\n` + `🕒 ${video.timestamp || "Unknown"}\n` + `📺 YouTube`, attachment: fs.createReadStream(file) }); react(api, messageID, "✅"); console.log( `[sing] Sent successfully: ${fileName}` ); } catch (err) { console.error( "[sing ERROR]", err ); react(api, messageID, "❌"); const errorMessage = extractError(err); return message.reply( "❌ Song download failed!\n\n" + `📌 ${errorMessage}` ); } finally { // Cache cleanup if (file) { try { if (fs.existsSync(file)) { fs.unlinkSync(file); console.log( "[sing] Cache deleted" ); } } catch (e) { console.error( "[sing] Cleanup error:", e.message ); } } } };