Loading snippets...
/**
*
* base: https://retatube.com
* Creator: ShanMolvyr
* reupload/modif cantumkan sumber ini woii parah
*
* Note: cek https://snippet.vyr.my.id/shanmolvyr/retatube/README.md
* Sumber Scraper: https://whatsapp.com/channel/0029VbB4Kw8EFeXfeExaXc3Q
* "Kalau kamu benar seorang developer, kamu pasti paham bahwa credit bukan beban. Modifikasi sesukamu, jadikan API sesukamu, reupload pun silakan. Tapi jangan hilangkan sumber. Karena menghargai karya orang lain adalah etika, bukan kelemahan."
*/
const axios = require("axios");
const { parse } = require("node-html-parser");
const BASE_URL = "https://retatube.com";
const PREFIX = "retatube.com";
const HEADERS = {
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Mobile Safari/537.36",
Referer: "https://retatube.com/",
Origin: "https://retatube.com",
};
async function fetchInfo(url, { exclude = "", format = "" } = {}) {
const res = await axios.post(
`${BASE_URL}/api/v1/aio/html`,
{ vid: url, prefix: PREFIX, ex: exclude, format },
{ headers: HEADERS }
);
return parseHtml(res.data, url);
}
function parseHtml(html, originalUrl) {
const root = parse(html);
const section = root.querySelector(".download-section");
if (!section) throw new Error("No download results found");
const title =
section.querySelector("h3")?.text?.trim() ||
section.querySelector(".wrap-break-word")?.text?.trim() ||
null;
const owner =
section.querySelector("p strong")?.parentNode?.text
?.replace("Owner:", "")
?.trim() || null;
const thumbnail = section.querySelector("img")?.getAttribute("src") || null;
const links = [];
section.querySelectorAll("a.download-btn").forEach((a) => {
const href = a.getAttribute("href");
const label = a.text.trim();
if (href && !href.startsWith("#")) links.push({ label, url: href });
});
return { credit: "ShanMolvyr", source: originalUrl, title, owner, thumbnail, downloads: links };
}
async function fetchFormats(url, format, { exclude = "" } = {}) {
const res = await axios.post(
`${BASE_URL}/api/v1/aio/search`,
{ vid: url, prefix: PREFIX, ex: exclude, format },
{ headers: HEADERS }
);
const json = res.data;
if (!json || json.code !== 0) throw new Error(json?.message || "Format fetch failed");
return { credit: "ShanMolvyr", source: url, format, data: json.data };
}
async function checkProgress(loaderId, videoUrl) {
const res = await axios.get(`${BASE_URL}/api/v1/loader/progress`, {
params: { id: loaderId, prefix: PREFIX, v: Date.now(), s: videoUrl },
headers: HEADERS,
});
return { credit: "ShanMolvyr", loaderId, ...res.data };
}
module.exports = { fetchInfo, fetchFormats, checkProgress };
if (require.main === module) {
const args = process.argv.slice(2);
const url = args[0];
const format = args[1] || "";
if (!url) {
console.error("Usage: node retatube.js <url> [format]");
process.exit(1);
}
(async () => {
try {
const result = format ? await fetchFormats(url, format) : await fetchInfo(url);
console.log(JSON.stringify(result, null, 2));
} catch (err) {
console.error(JSON.stringify({ credit: "ShanMolvyr", error: err.message }));
process.exit(1);
}
})();
}
bashnpm install axios node-html-parser
bashconst { fetchInfo, fetchFormats, checkProgress } = require("./retatube"); const result = await fetchInfo("https://vt.tiktok.com/xxx");
bashnode retatube.js https://vt.tiktok.com/xxx node retatube.js https://youtu.be/xxx 720p