// code from goatstore // api: https://store.agi.bd const axios = require("axios"); const fs = require("fs-extra"); const path = require("path"); const API_URL = "https://raw.githubusercontent.com/mahmudx7/HINATA/main/baseApiUrl.json"; async function getBaseURL() { const res = await axios.get(API_URL, { timeout: 15000 }); if (!res.data || !res.data.mahmud) { throw new Error("API URL not found"); } return res.data.mahmud; } function formatNumber(num) { return String(num || 0).replace(/\B(?=(\d{3})+(?!\d))/g, ","); } function isYouTubeURL(url) { return /^(https?:\/\/)?(www\.)?(m\.)?(youtube\.com\/(watch\?v=|shorts\/|embed\/)|youtu\.be\/)[\w-]{11}/i.test(url); } function getVideoID(url) { const match = url.match( /(?:youtube\.com\/(?:watch\?v=|shorts\/|embed\/)|youtu\.be\/)([\w-]{11})/i ); return match ? match[1] : null; } module.exports = { config: { name: "ytb", aliases: ["yt", "youtube"], version: "5.0.0", author: "Mahmud x design ZH", countDown: 8, role: 0, category: "media", description: { en: "YouTube search, video, audio and information" }, guide: { en: "{pn} video \n" + "{pn} audio \n" + "{pn} info \n\n" + "Example:\n" + "{pn} video Arijit Singh\n" + "{pn} audio Alan Walker\n" + "{pn} info https://youtu.be/xxxxxxxxxxx" } }, onStart: async function ({ api, args, event, message, commandName }) { const { threadID, messageID, senderID } = event; if (!args[0]) { return message.reply( "╭━━━〔 🎬 YTB CENTER 〕━━━╮\n" + "┃\n" + "┃ 🎥 video \n" + "┃ 🎧 audio \n" + "┃ 📊 info \n" + "┃\n" + "┃ Example:\n" + "┃ ytb video Alan Walker\n" + "┃ ytb audio Arijit Singh\n" + "┃ ytb info https://youtu.be/...\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯" ); } const mode = String(args[0]).toLowerCase(); let type; if (["video", "v", "-v"].includes(mode)) { type = "video"; } else if (["audio", "a", "-a", "mp3", "sing"].includes(mode)) { type = "audio"; } else if (["info", "i", "-i", "details"].includes(mode)) { type = "info"; } else { return message.reply( "❌ Unknown mode.\n\n" + "Use:\n" + "🎥 ytb video \n" + "🎧 ytb audio \n" + "📊 ytb info " ); } const input = args.slice(1).join(" ").trim(); if (!input) { return message.reply( `❌ ${type === "video" ? "Video" : type === "audio" ? "Audio" : "Info"} এর জন্য YouTube নাম বা link দিন।` ); } try { api.setMessageReaction("🔎", messageID, () => {}, true); let videoID = null; if (isYouTubeURL(input)) { videoID = getVideoID(input); } if (videoID) { if (type === "info") { return await sendInfo( api, threadID, messageID, videoID ); } return await downloadMedia( api, threadID, messageID, videoID, type ); } const baseURL = await getBaseURL(); const search = await axios.get( `${baseURL}/api/ytb/search`, { params: { q: input }, timeout: 30000 } ); const results = search.data && Array.isArray(search.data.results) ? search.data.results.slice(0, 5) : []; if (!results.length) { api.setMessageReaction("❌", messageID, () => {}, true); return message.reply( `🔍 "${input}" এর জন্য কোনো YouTube result পাওয়া যায়নি।` ); } const cacheDir = path.join(__dirname, "cache"); await fs.ensureDir(cacheDir); let body = "╭━━━━━━〔 🎬 YTB SEARCH 〕━━━━━━╮\n" + "┃\n"; const attachments = []; for (let i = 0; i < results.length; i++) { const item = results[i]; const title = item.title || "Unknown title"; const time = item.time || "Unknown"; const channel = item.channel && typeof item.channel === "object" ? item.channel.name : item.channel || "Unknown"; body += `┃ ${i + 1}️⃣ ${title}\n` + `┃ ⏱ ${time} • 👤 ${channel}\n` + "┃\n"; if (item.thumbnail) { try { const thumb = await axios.get( item.thumbnail, { responseType: "arraybuffer", timeout: 15000 } ); const file = path.join( cacheDir, `yt_${senderID}_${Date.now()}_${i}.jpg` ); await fs.writeFile( file, Buffer.from(thumb.data) ); attachments.push( fs.createReadStream(file) ); } catch (_) { // Thumbnail failed, continue without it. } } } body += "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━━━╯\n\n" + "↳ যে ভিডিওটি চান, তার নম্বর reply করুন।\n" + "↳ অন্য কিছু লিখলে search বন্ধ হবে।"; return api.sendMessage( { body, attachment: attachments }, threadID, (err, info) => { if (!info) return; for (const stream of attachments) { try { if (stream.path && fs.existsSync(stream.path)) { fs.unlinkSync(stream.path); } } catch (_) {} } global.GoatBot.onReply.set( info.messageID, { commandName, author: senderID, results, type, messageID: info.messageID } ); }, messageID ); } catch (error) { console.error("YTB ERROR:", error); api.setMessageReaction( "❌", messageID, () => {}, true ); return message.reply( "╭━━〔 ⚠️ YTB ERROR 〕━━╮\n" + "┃ YouTube service এখন response দিচ্ছে না।\n" + "┃\n" + "┃ 🔄 কিছুক্ষণ পরে আবার চেষ্টা করুন।\n" + "╰━━━━━━━━━━━━━━━━━━━━╯" ); } }, onReply: async function ({ api, event, Reply }) { const { results, type, author, messageID } = Reply; if (String(event.senderID) !== String(author)) { return; } const choice = parseInt( String(event.body).trim() ); if ( isNaN(choice) || choice < 1 || choice > results.length ) { try { await api.unsendMessage(messageID); } catch (_) {} return; } const selected = results[choice - 1]; const videoID = selected && selected.id; if (!videoID) { return api.sendMessage( "❌ এই ভিডিওটির ID পাওয়া যায়নি।", event.threadID, event.messageID ); } try { await api.unsendMessage(messageID); } catch (_) {} api.setMessageReaction( "⏳", event.messageID, () => {}, true ); if (type === "info") { return sendInfo( api, event.threadID, event.messageID, videoID ); } return downloadMedia( api, event.threadID, event.messageID, videoID, type ); } }; async function downloadMedia( api, threadID, messageID, videoID, type ) { try { const baseURL = await getBaseURL(); const result = await axios.get( `${baseURL}/api/ytb/get`, { params: { id: videoID, type }, timeout: 60000 } ); const data = result.data && result.data.data; if (!data || !data.downloadLink) { throw new Error("Download link unavailable"); } const title = data.title || "YouTube Media"; const extension = type === "audio" ? "mp3" : "mp4"; const response = await axios.get( data.downloadLink, { responseType: "stream", timeout: 120000, maxContentLength: Infinity, maxBodyLength: Infinity } ); const fileName = `Zihad_YTB_${Date.now()}.${extension}`; response.data.path = fileName; const header = type === "audio" ? "🎧 AUDIO READY" : "🎬 VIDEO READY"; return api.sendMessage( { body: `╭━━〔 ${header} 〕━━╮\n` + `┃\n` + `┃ 📌 ${title}\n` + `┃\n` + `┃ \n` + `╰━━━━━━━━━━━━━━━━╯`, attachment: response.data }, threadID, () => { api.setMessageReaction( "✅", messageID, () => {}, true ); }, messageID ); } catch (error) { console.error( "YTB DOWNLOAD ERROR:", error.message ); return api.sendMessage( "❌ Download failed.\n" + "⚠️ YouTube server/API থেকে media পাওয়া যাচ্ছে না।\n" + "🔄 কিছুক্ষণ পরে আবার চেষ্টা করুন।", threadID, messageID ); } } async function sendInfo( api, threadID, messageID, videoID ) { try { const baseURL = await getBaseURL(); const result = await axios.get( `${baseURL}/api/ytb/details`, { params: { id: videoID }, timeout: 30000 } ); const d = result.data.details; if (!d) { throw new Error("Video information unavailable"); } const title = d.title || "Unknown"; const channel = d.channel || "Unknown"; const duration = d.duration_raw || d.duration || "Unknown"; const views = formatNumber(d.view_count); const likes = formatNumber(d.like_count); const subscribers = formatNumber(d.subCount); const upload = d.upload_date || "Unknown"; const url = d.webpage_url || `https://youtu.be/${videoID}`; let body = "╭━━━━〔 📊 YOUTUBE INFO 〕━━━━╮\n" + "┃\n" + `┃ 🎬 ${title}\n` + "┃\n" + `┃ 👤 Channel : ${channel}\n` + `┃ 👥 Subscribers : ${subscribers}\n` + `┃ ⏱ Duration : ${duration}\n` + `┃ 👀 Views : ${views}\n` + `┃ 👍 Likes : ${likes}\n` + `┃ 📅 Upload : ${upload}\n` + `┃ 🆔 ID : ${videoID}\n` + "┃\n" + `┃ 🔗 ${url}\n` + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━━━╯\n" + " ⚡ Zihad x ChatGPT"; const cacheDir = path.join(__dirname, "cache"); await fs.ensureDir(cacheDir); let attachment = null; let thumbPath = null; if (d.thumbnail) { try { const thumb = await axios.get( d.thumbnail, { responseType: "arraybuffer", timeout: 15000 } ); thumbPath = path.join( cacheDir, `info_${videoID}_${Date.now()}.jpg` ); await fs.writeFile( thumbPath, Buffer.from(thumb.data) ); attachment = fs.createReadStream(thumbPath); } catch (_) {} } return api.sendMessage( { body, ...(attachment ? { attachment } : {}) }, threadID, () => { api.setMessageReaction( "✅", messageID, () => {}, true ); if ( thumbPath && fs.existsSync(thumbPath) ) { try { fs.unlinkSync(thumbPath); } catch (_) {} } }, messageID ); } catch (error) { console.error( "YTB INFO ERROR:", error.message ); return api.sendMessage( "❌ Video information পাওয়া যায়নি।\n" + "🔄 আবার চেষ্টা করুন।", threadID, messageID ); } }