LOADING
YouTube video dan music downloader support 4k(WEBM)
const BASE = "https://p.savenow.to";
const DEFAULT_APIKEY = "dfcb6d76f2f6a9894gjkege8a4ab232222";
const REFERER = "https://en.loader.to/";
const FORMATS = ["mp3", "m4a", "webm", "aac", "flac", "opus", "ogg", "wav", "144", "240", "360", "480", "720", "1080", "1440", "4k"];
const baseHeaders = {
"accept": "*/*",
"accept-language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "no-cache",
"pragma": "no-cache",
"Referer": REFERER
};
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function health() {
try {
await fetch(`${BASE}/api/health`, { method: "HEAD", headers: baseHeaders });
} catch (_) {}
}
async function requestDownload(sourceUrl, format, apikey, addInfo) {
const params = new URLSearchParams({ format, url: sourceUrl, apikey });
if (addInfo) params.set("add_info", "1");
const res = await fetch(`${BASE}/api/v2/download?${params.toString()}`, { headers: baseHeaders });
const data = await res.json();
if (!data.success) throw new Error(data.error || "permintaan download ditolak oleh server");
return data;
}
async function checkProgress(id) {
const res = await fetch(`${BASE}/api/progress?id=${encodeURIComponent(id)}&_=${Date.now()}`, { headers: baseHeaders });
return res.json();
}
function isFailureStatus(progress) {
if (progress.error) return true;
const text = (progress.text || "").toLowerCase();
return text.includes("fail") || text.includes("error") || text.includes("gagal");
}
async function waitForResult(id, { intervalMs = 1500, maxAttempts = 60 } = {}) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const progress = await checkProgress(id);
if (progress.download_url) return progress;
if (isFailureStatus(progress)) throw new Error(progress.text || progress.error || "proses download gagal");
await sleep(intervalMs);
}
throw new Error("timeout menunggu hasil download");
}
async function probe(url) {
try {
const res = await fetch(url, { method: "HEAD", headers: { Referer: REFERER } });
return {
ok: res.ok,
status: res.status,
contentType: res.headers.get("content-type") || null,
contentLength: res.headers.get("content-length") ? Number(res.headers.get("content-length")) : null
};
} catch (_) {
return null;
}
}
async function download(sourceUrl, format, options = {}) {
if (!sourceUrl) throw new Error("url wajib diisi");
if (!FORMATS.includes(format)) throw new Error(`format tidak dikenal, pilih salah satu: ${FORMATS.join(", ")}`);
const apikey = options.apikey || DEFAULT_APIKEY;
const addInfo = options.addInfo !== false;
await health();
const initial = await requestDownload(sourceUrl, format, apikey, addInfo);
let final = initial;
if (!initial.url && !initial.download_url) {
final = await waitForResult(initial.id, options);
}
const downloadUrl = final.download_url || final.url || initial.url;
const fileInfo = options.probe === false ? null : await probe(downloadUrl);
return {
tool: "loaderto-wrapper by ShanMolvyr",
success: true,
sourceUrl,
format,
id: initial.id,
title: initial.info ? initial.info.title || null : null,
thumbnail: initial.info ? initial.info.image || null : null,
downloadUrl,
filename: final.filename || null,
expiresIn: final.expires_in || null,
file: fileInfo
};
}
async function main() {
const [sourceUrl, format = "mp3", apikey] = process.argv.slice(2);
try {
const result = await download(sourceUrl, format, { apikey });
console.log(JSON.stringify(result, null, 2));
} catch (err) {
console.log(JSON.stringify({ tool: "loaderto-wrapper by ShanMolvyr", success: false, error: err.message }, null, 2));
process.exitCode = 1;
}
}
if (require.main === module) {
main();
}
module.exports = { download, FORMATS };bashnode loaderto.js https://youtu.be/xxxx <format>
bashnode loaderto.js https://youtu.be/xxxx <format>
| Audio | Video |
|---|---|
| MP3 | MP4 (144p) |
| M4A | MP4 (240p) |
| WEBM | MP4 (360p) |
| AAC | MP4 (480p) |
| FLAC | MP4 (720p) |
| OPUS | MP4 (1080p) |
| OGG | MP4 (1440p) |
| WAV | WEBM (4K) |