← back to browse
goat command

ytb

No description

1
views
0
likes
1
installs

Aliases: youtube

raw source
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);
        }
                        }

View raw file