notmebotz-tools
Version:
Sebuah Tools yang berfungsi untuk mendownload Video atau Foto dari media sosial, serta sebagai tools yang berguna untuk aplikasi kamu seperti untuk BOT
358 lines (316 loc) • 11.8 kB
JavaScript
const axios = require("axios");
const yts = require("yt-search");
const crypto = require("crypto");
function getYouTubeVideoId(url) {
const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|v\/|embed\/|user\/[^\/\n\s]+\/)?(?:watch\?v=|v%3D|embed%2F|video%2F)?|youtu\.be\/|youtube\.com\/watch\?v=|youtube\.com\/embed\/|youtube\.com\/v\/|youtube\.com\/shorts\/|youtube\.com\/playlist\?list=)([a-zA-Z0-9_-]{11})/;
const match = url.match(regex);
return match ? match[1] : null;
}
const savetube = {
api: {
base: "https://media.savetube.me/api",
cdn: "/random-cdn",
info: "/v2/info",
download: "/download"
},
headers: {
'accept': '*/*',
'content-type': 'application/json',
'origin': 'https://yt.savetube.me',
'referer': 'https://yt.savetube.me/',
'user-agent': 'Postify/1.0.0'
},
formats: ['144', '240', '360', '480', '720', '1080', 'mp3'],
crypto: {
hexToBuffer: (hexString) => {
const matches = hexString.match(/.{1,2}/g);
return Buffer.from(matches.join(''), 'hex');
},
decrypt: async (enc) => {
try {
const secretKey = 'C5D58EF67A7584E4A29F6C35BBC4EB12';
const data = Buffer.from(enc, 'base64');
const iv = data.slice(0, 16);
const content = data.slice(16);
const key = savetube.crypto.hexToBuffer(secretKey);
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
let decrypted = decipher.update(content);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return JSON.parse(decrypted.toString());
} catch (error) {
throw new Error(`${error.message}`);
}
}
},
isUrl: (str) => {
try {
new URL(str);
return true;
} catch (_) {
return false;
}
},
youtube: (url) => {
if (!url) return null;
const a = [
/youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/,
/youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/,
/youtube\.com\/v\/([a-zA-Z0-9_-]{11})/,
/youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/,
/youtu\.be\/([a-zA-Z0-9_-]{11})/
];
for (let b of a) {
if (b.test(url)) return url.match(b)[1];
}
return null;
},
request: async (endpoint, data = {}, method = 'post') => {
try {
const { data: response } = await axios({
method,
url: `${endpoint.startsWith('http') ? '' : savetube.api.base}${endpoint}`,
data: method === 'post' ? data : undefined,
params: method === 'get' ? data : undefined,
headers: savetube.headers
});
return {
status: true,
code: 200,
data: response
};
} catch (error) {
return {
status: false,
code: error.response?.status || 500,
error: error.message
};
}
},
getCDN: async () => {
const response = await savetube.request(savetube.api.cdn, {}, 'get');
if (!response.status) return response;
return {
status: true,
code: 200,
data: response.data.cdn
};
},
download: async (link, format) => {
if (!link) {
return {
status: false,
code: 400,
error: "Linknya mana? Yakali download kagak ada linknya 🗿"
};
}
if (!savetube.isUrl(link)) {
return {
status: false,
code: 400,
error: "Lu masukin link apaan sih 🗿 Link Youtube aja bree, kan lu mau download youtube 👍🏻"
};
}
if (!format || !savetube.formats.includes(format)) {
return {
status: false,
code: 400,
error: "Format Is Unavailable",
available_fmt: savetube.formats
};
}
const id = savetube.youtube(link);
if (!id) {
return {
status: false,
code: 400,
error: "Kagak bisa ekstrak link youtubenya nih, btw link youtubenya yang bener yak.. biar kagak kejadian begini lagi 😂"
};
}
try {
const cdnx = await savetube.getCDN();
if (!cdnx.status) return cdnx;
const cdn = cdnx.data;
const result = await savetube.request(`https://${cdn}${savetube.api.info}`, {
url: `https://www.youtube.com/watch?v=${id}`
});
if (!result.status) return result;
const decrypted = await savetube.crypto.decrypt(result.data.data);
const dl = await savetube.request(`https://${cdn}${savetube.api.download}`, {
id: id,
downloadType: format === 'mp3' ? 'audio' : 'video',
quality: format === 'mp3' ? '128' : format,
key: decrypted.key
});
return {
status: true,
code: 200,
result: {
title: decrypted.title || "Gak tau 🤷🏻",
type: format === 'mp3' ? 'audio' : 'video',
format: format,
thumbnail: decrypted.thumbnail || `https://i.ytimg.com/vi/${id}/maxresdefault.jpg`,
download: dl.data.data.downloadUrl,
id: id,
key: decrypted.key,
duration: decrypted.duration,
quality: format === 'mp3' ? '128' : format,
downloaded: dl.data.data.downloaded || false
}
};
} catch (error) {
return {
status: false,
code: 500,
error: error.message
};
}
}
};
async function ytMp3(link) {
const videoId = getYouTubeVideoId(link);
if (!videoId) return { author: "Herza", status: 400, data: "Invalid YouTube URL" };
try {
const searchResult = await yts("https://youtube.com/watch?v=" + videoId);
const metadata = searchResult.all[0];
const saveTubeResult = await savetube.download(link, "mp3");
if (!saveTubeResult.status) {
return { author: "Herza", status: 400, data: saveTubeResult.error || "Failed to get audio" };
}
// Langsung menggunakan URL audio dari savetube, tidak perlu mengkonversi dengan ffmpeg
let audioUrl = saveTubeResult.result.download;
return {
author: "Herza",
status: 200,
data: {
metadata: {
title: metadata.title || saveTubeResult.result.title,
description: metadata.description || "",
duration: saveTubeResult.result.duration || metadata.duration,
views: metadata.views,
author: metadata.author?.name || "",
timestamp: metadata.timestamp,
ago: metadata.ago,
thumbnail: metadata.thumbnail || saveTubeResult.result.thumbnail
},
download: {
url: audioUrl,
filename: `${metadata.title || saveTubeResult.result.title}.mp3`
}
}
};
} catch (error) {
return { author: "Herza", status: 500, data: error.message };
}
}
async function ytMp4(link, resolution = "480") {
const videoId = getYouTubeVideoId(link);
if (!videoId) return { author: "Herza", status: 400, data: "Invalid YouTube URL" };
// Validasi resolusi
if (!savetube.formats.includes(resolution) || resolution === "mp3") {
return {
author: "Herza",
status: 400,
data: `Resolusi tidak valid. Pilih dari: ${savetube.formats.filter(f => f !== "mp3").join(", ")}`
};
}
try {
const searchResult = await yts("https://youtube.com/watch?v=" + videoId);
const metadata = searchResult.all[0];
const saveTubeResult = await savetube.download(link, resolution);
if (!saveTubeResult.status) {
return { author: "Herza", status: 400, data: saveTubeResult.error || "Failed to get video" };
}
return {
author: "Herza",
status: 200,
data: {
metadata: {
title: metadata.title || saveTubeResult.result.title,
description: metadata.description || "",
duration: saveTubeResult.result.duration || metadata.duration,
views: metadata.views,
author: metadata.author?.name || "",
timestamp: metadata.timestamp,
ago: metadata.ago,
thumbnail: metadata.thumbnail || saveTubeResult.result.thumbnail
},
download: {
url: saveTubeResult.result.download,
filename: `${metadata.title || saveTubeResult.result.title}.mp4`,
resolution: resolution
}
}
};
} catch (error) {
return { author: "Herza", status: 500, data: error.message };
}
}
async function search(teks) {
try {
let data = await yts(teks);
return { author: "Herza", status: 200, data: data.all };
} catch (error) {
return { author: "Herza", status: 500, data: error.message };
}
}
async function transcript(link) {
try {
const response = await axios.get("https://ytb2mp4.com/api/fetch-transcript", {
params: { url: link },
headers: {
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36",
"Referer": "https://ytb2mp4.com/youtube-transcript"
}
});
return { author: "Herza", status: 200, data: response.data.transcript };
} catch (error) {
return { author: "Herza", status: 500, data: error.message };
}
}
async function getAvailableResolutions(link) {
const videoId = getYouTubeVideoId(link);
if (!videoId) return { author: "Herza", status: 400, data: "Invalid YouTube URL" };
return {
author: "Herza",
status: 200,
data: {
message: "Resolusi yang tersedia",
resolutions: savetube.formats.filter(f => f !== "mp3")
}
};
}
async function ytdl(type, ...args) {
switch (type.toLowerCase()) {
case "ytmp4":
if (args.length < 1) return { author: "Herza", status: 400, data: "Missing arguments. Usage: ytdl('ytmp4', 'url', 'resolution')" };
return await ytMp4(args[0], args[1] || "480");
case "ytmp3":
if (args.length < 1) return { author: "Herza", status: 400, data: "Missing arguments. Usage: ytdl('ytmp3', 'url')" };
return await ytMp3(args[0]);
case "search":
if (args.length < 1) return { author: "Herza", status: 400, data: "Missing arguments. Usage: ytdl('search', 'query')" };
return await search(args[0]);
case "transcript":
if (args.length < 1) return { author: "Herza", status: 400, data: "Missing arguments. Usage: ytdl('transcript', 'url')" };
return await transcript(args[0]);
case "resolutions":
if (args.length < 1) return { author: "Herza", status: 400, data: "Missing arguments. Usage: ytdl('resolutions', 'url')" };
return await getAvailableResolutions(args[0]);
default:
return {
author: "Herza",
status: 400,
data: {
message: "Invalid type. Available types: ytmp4, ytmp3, search, transcript, resolutions",
usage: {
ytmp4: "ytdl('ytmp4', 'url', 'resolution')",
ytmp3: "ytdl('ytmp3', 'url')",
search: "ytdl('search', 'query')",
transcript: "ytdl('transcript', 'url')",
resolutions: "ytdl('resolutions', 'url')"
}
}
};
}
}
module.exports = { ytdl };