goat
command
segx
No description
0
views
0
likes
0
installs
Aliases: neosegs, sg
raw source
const axios = require("axios");
const fs = require("fs-extra");
const cheerio = require("cheerio");
const { getStreamFromURL } = global.utils;
const API_BASE = "https://neosegs.fly.dev";
let cachedCategories = null;
async function getStreamAndSize(url, path = "") {
const response = await axios({
method: "GET",
url,
responseType: "stream",
headers: {
'Range': 'bytes=0-',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://www.xvideos.com/'
},
timeout: 60000
});
if (path)
response.data.path = path;
const totalLength = response.headers["content-length"];
return {
stream: response.data,
size: totalLength
};
}
async function apiList(params = {}) {
const response = await axios.get(`${API_BASE}/api/list`, { params });
return response.data;
}
async function apiSearchByActor(actorName, pagesize = 50) {
const response = await axios.get(`${API_BASE}/api/search-by-actor`, {
params: { actor_name: actorName, pagesize }
});
return response.data;
}
async function apiCategories() {
if (cachedCategories) return cachedCategories;
const response = await axios.get(`${API_BASE}/api/categories`);
cachedCategories = response.data.categories || {};
return cachedCategories;
}
async function getVideoById(id) {
const response = await axios.get(`${API_BASE}/api/search`, {
params: { ids: String(id) }
});
return response.data;
}
async function getDirectVideoUrl(videoName) {
try {
const searchUrl = 'https://www.xvideos.com/?k=' + encodeURIComponent(videoName);
const { data: searchData } = await axios.get(searchUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5'
},
timeout: 15000
});
const $ = cheerio.load(searchData);
let videoPageUrl = null;
$('.thumb-block').each((i, el) => {
if (videoPageUrl) return;
const link = $(el).find('a').attr('href');
if (link && link.includes('/video')) {
videoPageUrl = 'https://www.xvideos.com' + link;
}
});
if (!videoPageUrl) return null;
const { data: videoPage } = await axios.get(videoPageUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
},
timeout: 15000
});
const highMatch = videoPage.match(/setVideoUrlHigh\('([^']+)'\)/);
const lowMatch = videoPage.match(/setVideoUrlLow\('([^']+)'\)/);
if (highMatch) return { url: highMatch[1], quality: 'high' };
if (lowMatch) return { url: lowMatch[1], quality: 'low' };
return null;
} catch (err) {
console.error("Error getting direct URL:", err.message);
return null;
}
}
function parseArgs(args) {
const result = {
type: null,
keyword: [],
actor: null,
category: null,
page: 1,
year: null,
sort: null,
id: null
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case "-a":
case "--actor":
result.type = "actor";
result.actor = args.slice(i + 1).filter(a => !a.startsWith("-")).join(" ");
return result;
case "-c":
case "--category":
result.type = "category";
result.category = args[i + 1];
i++;
break;
case "-i":
case "--info":
result.type = "info";
result.id = args[i + 1];
i++;
break;
case "-p":
case "--page":
result.page = parseInt(args[i + 1]) || 1;
i++;
break;
case "-y":
case "--year":
result.year = args[i + 1];
i++;
break;
case "-s":
case "--sort":
result.sort = args[i + 1];
i++;
break;
case "list":
result.type = "list";
break;
default:
if (!arg.startsWith("-")) {
result.keyword.push(arg);
}
break;
}
}
if (!result.type && result.keyword.length > 0) {
result.type = "browse";
}
return result;
}
module.exports = {
config: {
name: "segx",
aliases: ["neosegs", "sg"],
version: "1.3.0",
author: "NeoKEX",
countDown: 5,
role: 0,
description: {
en: "Search and download videos from NeoSegs API"
},
category: "media",
guide: {
en: " {pn}: browse latest videos"
+ "\n {pn} -a <actor name>: search by actor"
+ "\n {pn} -c <category id>: browse by category"
+ "\n {pn} list: show all categories"
+ "\n {pn} -i <id>: view video information"
+ "\n {pn} -p <page>: go to page"
+ "\n {pn} -c <id> -y <year>: filter by year"
+ "\n\n Example:"
+ "\n {pn}"
+ "\n {pn} -a Jenna"
+ "\n {pn} -c 43"
+ "\n {pn} -c 43 -p 2"
+ "\n {pn} list"
}
},
langs: {
vi: {
error: "x Loi: %1",
noResult: "! Khong tim thay ket qua",
noResultActor: "! Khong tim thay dien vien: %1",
noResultCategory: "! Khong tim thay danh muc: %1",
choose: "%1\n> Reply so (1-%2) de tai video",
downloading: "> Dang tai \"%1\"...",
noVideo: "! Khong the tai video nay",
videoTooBig: "! Video qua lon (>83MB)",
info: "> %1\n\n• ID: %2\n• Danh muc: %3\n• Dien vien: %4\n• Nam: %5\n• Thoi luong: %6\n• Chat luong: %7",
categories: "> Danh sach danh muc:\n\n%1\n\n• Dung: segx -c <id> de xem",
categoryItem: "%1. %2 (ID: %3)",
searchResult: "%1. %2\n • %3 | %4 | %5 | %6",
page: "\n\n> Trang %1/%2 | Tong: %3 video"
},
en: {
error: "x Error: %1",
noResult: "! No results found",
noResultActor: "! No results for actor: %1",
noResultCategory: "! No results for category: %1",
choose: "%1\n> Reply with number (1-%2) to download",
downloading: "> Downloading \"%1\"...",
noVideo: "! Cannot download this video",
videoTooBig: "! Video too large (>83MB)",
info: "> %1\n\n• ID: %2\n• Category: %3\n• Actors: %4\n• Year: %5\n• Duration: %6\n• Quality: %7",
categories: "> Available Categories:\n\n%1\n\n• Use: segx -c <id> to browse",
categoryItem: "%1. %2 (ID: %3)",
searchResult: "%1. %2\n • %3 | %4 | %5 | %6",
page: "\n\n> Page %1/%2 | Total: %3 videos"
}
},
onStart: async function ({ args, message, event, commandName, getLang }) {
const parsed = parseArgs(args);
const maxResults = 10;
try {
switch (parsed.type) {
case "list": {
const categories = await apiCategories();
if (!categories || Object.keys(categories).length === 0) {
return message.reply(getLang("error", "No categories found"));
}
let categoryList = "";
let count = 1;
for (const [id, name] of Object.entries(categories)) {
categoryList += getLang("categoryItem", count, name, id) + "\n";
count++;
}
return message.reply(getLang("categories", categoryList));
}
case "info": {
if (!parsed.id) {
return message.SyntaxError();
}
const data = await getVideoById(parsed.id);
const videos = data?.list || [];
if (videos.length === 0) {
return message.reply(getLang("noResult"));
}
const video = videos[0];
const thumbUrl = video.poster_url || video.thumb_url;
const thumbnails = [];
if (thumbUrl) {
thumbnails.push(getStreamFromURL(thumbUrl).catch(() => null));
}
const actors = Array.isArray(video.actor) ? video.actor.join(", ") : (video.actor || "N/A");
const attachments = (await Promise.all(thumbnails)).filter(t => t !== null);
return message.reply({
body: getLang("info",
video.name || video.origin_name || "Unknown",
video.id || video.slug,
video.type_name || "N/A",
actors,
video.year || "N/A",
video.time || "N/A",
video.quality || "N/A"
),
attachment: attachments.length > 0 ? attachments : undefined
});
}
case "actor": {
if (!parsed.actor) {
return message.SyntaxError();
}
const data = await apiSearchByActor(parsed.actor, 50);
const videos = data?.list || [];
if (!videos || videos.length === 0) {
return message.reply(getLang("noResultActor", parsed.actor));
}
await displayResults(videos.slice(0, maxResults), message, event, commandName, getLang, data.total || videos.length, 1, data.pagecount || 1);
break;
}
case "category": {
if (!parsed.category) {
return message.SyntaxError();
}
const params = {
t: parseInt(parsed.category),
pg: parsed.page,
pagesize: 20
};
if (parsed.year) params.year = parsed.year;
if (parsed.sort) params.sort_direction = parsed.sort;
const data = await apiList(params);
const videos = data?.list || [];
if (!videos || videos.length === 0) {
return message.reply(getLang("noResultCategory", parsed.category));
}
await displayResults(videos.slice(0, maxResults), message, event, commandName, getLang, data.total || videos.length, parseInt(data.page) || parsed.page, parseInt(data.pagecount) || 1);
break;
}
case "browse":
default: {
const params = {
pg: parsed.page,
pagesize: 20
};
if (parsed.year) params.year = parsed.year;
if (parsed.sort) params.sort_direction = parsed.sort;
const data = await apiList(params);
const videos = data?.list || [];
if (!videos || videos.length === 0) {
return message.reply(getLang("noResult"));
}
await displayResults(videos.slice(0, maxResults), message, event, commandName, getLang, data.total || videos.length, parseInt(data.page) || parsed.page, parseInt(data.pagecount) || 1);
break;
}
}
} catch (err) {
console.error("Segx Error:", err);
return message.reply(getLang("error", err.message || "Unknown error"));
}
async function displayResults(videos, message, event, commandName, getLang, total, page, pageCount) {
let msg = "";
const thumbnails = [];
const results = [];
for (let i = 0; i < videos.length; i++) {
const video = videos[i];
const title = video.name || video.origin_name || "Unknown";
const category = video.type_name || "N/A";
const year = video.year || "N/A";
const duration = video.time || "N/A";
const quality = video.quality || "N/A";
const actors = Array.isArray(video.actor) ? video.actor.slice(0, 2).join(", ") : "";
msg += getLang("searchResult", i + 1, title, category, year, duration, quality) + "\n";
if (actors) msg += ` • Actors: ${actors}\n`;
msg += "\n";
const thumbUrl = video.poster_url || video.thumb_url;
if (thumbUrl) {
thumbnails.push(getStreamFromURL(thumbUrl).catch(() => null));
}
results.push(video);
}
msg += getLang("page", page, pageCount, total);
const attachments = (await Promise.all(thumbnails)).filter(t => t !== null);
message.reply({
body: getLang("choose", msg, results.length),
attachment: attachments.length > 0 ? attachments : undefined
}, (err, info) => {
if (err) return console.error("Reply error:", err);
global.GoatBot.onReply.set(info.messageID, {
commandName,
messageID: info.messageID,
author: event.senderID,
results
});
});
}
},
onReply: async ({ event, api, Reply, message, getLang }) => {
const { results } = Reply;
const choice = parseInt(event.body);
if (isNaN(choice) || choice < 1 || choice > results.length) {
api.unsendMessage(Reply.messageID);
return;
}
const video = results[choice - 1];
api.unsendMessage(Reply.messageID);
const title = video.name || video.origin_name || "Unknown";
const msgSend = await message.reply(getLang("downloading", title));
try {
const directUrl = await getDirectVideoUrl(title);
if (!directUrl || !directUrl.url) {
message.unsend(msgSend.messageID);
return message.reply(getLang("noVideo"));
}
const MAX_SIZE = 83 * 1024 * 1024;
const videoId = video.id || video.slug || Date.now();
const savePath = __dirname + `/tmp/segx_${videoId}_${Date.now()}.mp4`;
try {
const getStream = await getStreamAndSize(directUrl.url, `${videoId}.mp4`);
if (getStream.size && parseInt(getStream.size) > MAX_SIZE) {
message.unsend(msgSend.messageID);
return message.reply(getLang("videoTooBig"));
}
const writeStream = fs.createWriteStream(savePath);
getStream.stream.pipe(writeStream);
writeStream.on("finish", async () => {
try {
await message.reply({
body: `> ${title}`,
attachment: fs.createReadStream(savePath)
});
message.unsend(msgSend.messageID);
} catch (sendErr) {
message.reply(getLang("error", sendErr.message));
} finally {
fs.unlink(savePath, () => {});
}
});
writeStream.on("error", (err) => {
message.unsend(msgSend.messageID);
message.reply(getLang("error", err.message));
fs.unlink(savePath, () => {});
});
} catch (streamErr) {
console.error("Stream error:", streamErr.message);
message.unsend(msgSend.messageID);
return message.reply(getLang("noVideo"));
}
} catch (err) {
console.error("Download error:", err);
message.unsend(msgSend.messageID);
return message.reply(getLang("error", err.message || "Download failed"));
}
}
};