const axios = require("axios"); const fs = require('fs-extra'); const path = require('path'); const baseApiUrl = async () => { const base = await axios.get(`https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json`); return base.data.mahmud; }; module.exports = { config: { name: "ytb", aliases: ["youtube"], version: "1.7", author: "乛 SIYAM ゎ", countDown: 5, role: 0, description: { bn: "ইউটিউব থেকে ভিডিও, অডিও ডাউনলোড বা তথ্য দেখুন", en: "Download video, audio or view video information from YouTube", vi: "Tải video, audio hoặc xem thông tin video trên YouTube" }, category: "media", guide: { bn: ' {pn} video [নাম/লিঙ্ক]: ভিডিও ডাউনলোড করতে\n {pn} audio [নাম/লিঙ্ক]: অডিও ডাউনলোড করতে\n {pn} info [নাম/লিঙ্ক]: ভিডিওর তথ্য দেখতে\n উদাহরণ:\n {pn} -v Mood Lofi\n {pn} -a Mood Lofi', en: ' {pn} [video|-v] [name|link]: download video\n {pn} [audio|-a] [name|link]: download audio\n {pn} [info|-i] [name|link]: view details', vi: ' {pn} [video|-v] [tên|link]: tải video\n {pn} [audio|-a] [tên|link]: tải audio\n {pn} [info|-i] [tên|link]: xem thông tin' } }, langs: { bn: { error: "❌ সমস্যা হয়েছে: %1", noResult: "⭕ দুঃখিত বেবি, \"%1\" এর জন্য কিছু খুঁজে পাইনি।", choose: "%1যা ডাউনলোড করতে চান তার নাম্বার লিখে রিপ্লাই দিন।", video: "ভিডিও", audio: "অডিও", downloading: "⬇️ আপনার কাঙ্ক্ষিত %1 \"%2\" ডাউনলোড হচ্ছে...", info: "💠 শিরোনাম: %1\n🏪 চ্যানেল: %2\n👨‍👩‍👧‍👦 সাবস্ক্রাইবার: %3\n⏱ সময়কাল: %4\n👀 ভিউ: %5\n👍 লাইক: %6\n🆙 আপলোড: %7\n🔠 আইডি: %8\n🔗 লিঙ্ক: %9" }, en: { error: "❌ An error occurred: %1", noResult: "⭕ No search results match the keyword %1", choose: "%1Reply with a number to choose or anything else to cancel.", video: "video", audio: "audio", downloading: "⬇️ Downloading %1 \"%2\"", info: "💠 Title: %1\n🏪 Channel: %2\n👨‍👩‍👧‍👦 Subscriber: %3\n⏱ Duration: %4\n👀 Views: %5\n👍 Likes: %6\n🆙 Upload date: %7\n🔠 ID: %8\n🔗 Link: %9" }, vi: { error: "❌ Đã xảy ra lỗi: %1", noResult: "⭕ Không có kết quả tìm kiếm cho %1", choose: "%1Reply tin nhắn với số để chọn hoặc nội dung bất kì để gỡ", video: "video", audio: "âm thanh", downloading: "⬇️ Đang tải xuống %1 \"%2\"", info: "💠 Tiêu đề: %1\n🏪 Channel: %2\n👨‍👩‍👧‍👦 Subscriber: %3\n⏱ Thời gian: %4\n👀 Lượt xem: %5\n👍 Lượt thích: %6\n🆙 Ngày tải: %7\n🔠 ID: %8\n🔗 Link: %9" } }, onStart: async function ({ api, args, message, event, commandName, getLang }) { const { threadID, messageID, senderID } = event; let type; switch (args[0]) { case "-v": case "video": type = "video"; break; case "-a": case "-s": case "audio": case "sing": type = "audio"; break; case "-i": case "info": type = "info"; break; default: return message.reply(`• Usage: ${this.config.guide[getLang.name]}`); } const input = args.slice(1).join(" "); if (!input) return api.sendMessage("• Please provide a song name or link baby! 😘", threadID, messageID); const apiUrl = await baseApiUrl(); const checkurl = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=|shorts\/))((\w|-){11})(?:\S+)?$/; if (checkurl.test(input)) { const videoID = input.match(checkurl)[1]; api.setMessageReaction("⌛", messageID, () => {}, true); if (type === 'info') return fetchInfo(api, threadID, messageID, videoID, apiUrl, getLang); return handleDownload(api, threadID, messageID, videoID, type, apiUrl, getLang); } try { api.setMessageReaction("🥀", messageID, () => {}, true); const res = await axios.get(`${apiUrl}/api/ytb/search?q=${encodeURIComponent(input)}`); const results = res.data.results.slice(0, 6); if (!results || results.length === 0) return api.sendMessage(getLang("noResult", input), threadID, messageID); let msg = ""; const attachments = []; const cacheDir = path.join(__dirname, 'cache'); if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true }); for (let i = 0; i < results.length; i++) { msg += `${i + 1}. ${results[i].title}\nTime: ${results[i].time}\n\n`; const thumbPath = path.join(cacheDir, `thumb_${senderID}_${Date.now()}_${i}.jpg`); const thumbRes = await axios.get(results[i].thumbnail, { responseType: 'arraybuffer' }); fs.writeFileSync(thumbPath, Buffer.from(thumbRes.data)); attachments.push(fs.createReadStream(thumbPath)); } return api.sendMessage({ body: getLang("choose", msg), attachment: attachments }, threadID, (err, info) => { attachments.forEach(stream => { if (fs.existsSync(stream.path)) fs.unlinkSync(stream.path); }); global.GoatBot.onReply.set(info.messageID, { commandName, author: senderID, results, type, apiUrl }); }, messageID); } catch (e) { return api.sendMessage(getLang("error", e.message), threadID, messageID); } }, onReply: async function ({ event, api, Reply, getLang }) { const { results, type, apiUrl, author } = Reply; if (event.senderID !== author) return; const choice = parseInt(event.body); if (isNaN(choice) || choice <= 0 || choice > results.length) return api.unsendMessage(Reply.messageID); const videoID = results[choice - 1].id; api.unsendMessage(Reply.messageID); api.setMessageReaction("⌛", event.messageID, () => {}, true); if (type === 'info') return fetchInfo(api, event.threadID, event.messageID, videoID, apiUrl, getLang); await handleDownload(api, event.threadID, event.messageID, videoID, type, apiUrl, getLang); } }; async function handleDownload(api, threadID, messageID, videoID, type, apiUrl, getLang) { const format = type === 'audio' ? 'mp3' : 'mp4'; const filePath = path.join(__dirname, 'cache', `yt_${Date.now()}.${format}`); try { const res = await axios.get(`${apiUrl}/api/ytb/get?id=${videoID}&type=${type}`); const { title, downloadLink } = res.data.data; api.sendMessage(getLang("downloading", getLang(type), title), threadID, messageID); const response = await axios({ url: downloadLink, method: 'GET', responseType: 'stream' }); const writer = fs.createWriteStream(filePath); response.data.pipe(writer); writer.on('finish', () => { api.sendMessage({ body: `✅ Successfully Downloaded: ${title}`, attachment: fs.createReadStream(filePath) }, threadID, () => { api.setMessageReaction("✅", messageID, () => {}, true); if (fs.existsSync(filePath)) fs.unlinkSync(filePath); }, messageID); }); } catch (e) { api.sendMessage(getLang("error", "Download failed baby! 🥺"), threadID, messageID); } } async function fetchInfo(api, threadID, messageID, videoID, apiUrl, getLang) { try { const res = await axios.get(`${apiUrl}/api/ytb/details?id=${videoID}`); const d = res.data.details; const msg = getLang("info", d.title, d.channel, d.subCount || 'N/A', d.duration_raw || d.duration, d.view_count, d.like_count || 'N/A', d.upload_date || 'N/A', videoID, d.webpage_url ); const thumbPath = path.join(__dirname, 'cache', `info_${videoID}.jpg`); const thumbRes = await axios.get(d.thumbnail, { responseType: 'arraybuffer' }); fs.writeFileSync(thumbPath, Buffer.from(thumbRes.data)); api.sendMessage({ body: msg, attachment: fs.createReadStream(thumbPath) }, threadID, () => { api.setMessageReaction("✅", messageID, () => {}, true); if (fs.existsSync(thumbPath)) fs.unlinkSync(thumbPath); }, messageID); } catch (e) { api.sendMessage(getLang("error", "Could not find details."), threadID, messageID); } }