Instagram Downloader
Udah Gitu Aja
#!/usr/bin/env node
/**
* Powered FazzCodeID
* Base Url : 9xbuddy.site
*
*
*/
import fs from "node:fs";
import path from "node:path";
import https from "node:https";
import http from "node:http";
import crypto from "node:crypto";
import { pathToFileURL } from "node:url";
const CONFIG = {
SITE: "https://9xbuddy.site",
LOCALE: process.env.LOCALE || "id",
TIMEOUT_MS: 60_000,
DOWNLOAD_TIMEOUT_MS: 180_000,
UA: "Mozilla/5.0 (Android 15; Mobile; rv:155.0) Gecko/155.0 Firefox/155.0",
SIG_SUFFIX: "E",
DECODE_PREFIX: "SORRY_MATE",
};
const C = { reset: "\x1b[0m", dim: "\x1b[2m", green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m" };
const log = {
info: (t, m) => console.error(`${C.dim}[${t}]${C.reset} ${m}`),
ok: (t, m) => console.error(`${C.green}[${t}]${C.reset} ${m}`),
warn: (t, m) => console.error(`${C.yellow}[${t}]${C.reset} ${m}`),
err: (t, m) => console.error(`${C.red}[${t}]${C.reset} ${m}`),
hit: (t, m) => console.error(`${C.cyan}[${t}]${C.reset} ${m}`),
};
class N {
static decode64(e) {
e = e.replace(/\s/g, "");
if (!/^[a-z0-9+/]+\={0,2}$/i.test(e) || e.length % 4 > 0) return null;
try { return Buffer.from(e, "base64").toString("binary"); } catch { return null; }
}
static encode64(e) {
const buf = Buffer.alloc(e.length);
for (let i = 0; i < e.length; i++) buf[i] = e.charCodeAt(i) & 0xff;
return buf.toString("base64");
}
static encrypt(pt, key) {
if (!key || key.length === 0) return null;
let out = "";
for (let r = 0; r < pt.length; r++) {
const p = pt.charCodeAt(r);
const ki = (r % key.length) - 1;
const kc = key.charCodeAt(ki < 0 ? key.length - 1 : ki);
out += String.fromCharCode((p + kc) & 0xff);
}
return this.encode64(out);
}
static decrypt(ct, key) {
const d = this.decode64(ct);
if (!d) return null;
let out = "";
for (let r = 0; r < d.length; r++) {
const c = d.charCodeAt(r);
const ki = (r % key.length) - 1;
const kc = key.charCodeAt(ki < 0 ? key.length - 1 : ki);
out += String.fromCharCode((c - kc) & 0xff);
}
return out;
}
static hex2bin(hex) {
const out = [];
for (let i = 0; i < hex.length; i += 2) {
const hi = parseInt(hex[i], 16);
const lo = parseInt(hex[i + 1], 16);
if (Number.isNaN(hi) || Number.isNaN(lo)) return null;
out.push((hi << 4) | lo);
}
return String.fromCharCode(...out);
}
}
const SECRET = "SORRY_MATE_IM_NOT_GONNA_TELL_YOU";
const cookieJar = new Map();
function captureCookies(headers) {
for (const c of headers["set-cookie"] || []) {
const [pair] = c.split(";");
const eq = pair.indexOf("=");
if (eq > 0) cookieJar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
function cookieHeader() {
return [...cookieJar.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
}
function request(url, opts = {}) {
const { method = "GET", headers = {}, body = null, timeout = CONFIG.TIMEOUT_MS, redirects = 5 } = opts;
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const lib = parsed.protocol === "https:" ? https : http;
const h = { "User-Agent": CONFIG.UA, "Accept-Language": "en-US,en;q=0.9,id;q=0.8", ...headers };
const cookie = cookieHeader();
if (cookie) h.Cookie = cookie;
const req = lib.request(
{ method, hostname: parsed.hostname, port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
path: parsed.pathname + parsed.search, headers: h },
(res) => {
captureCookies(res.headers);
if (redirects > 0 && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
res.resume();
return request(new URL(res.headers.location, url).toString(), { ...opts, redirects: redirects - 1 }).then(resolve, reject);
}
const chunks = [];
res.on("data", (c) => chunks.push(c));
res.on("end", () => resolve({ status: res.statusCode, headers: res.headers, body: Buffer.concat(chunks).toString("utf-8"), buffer: Buffer.concat(chunks) }));
}
);
req.on("error", reject);
req.setTimeout(timeout, () => req.destroy(new Error(`Timeout ${timeout}ms`)));
if (body) req.write(body);
req.end();
});
}
function extractInit(html) {
const start = html.indexOf("window.__INIT__");
if (start === -1) throw new Error("__INIT__ tidak ditemukan");
const eq = html.indexOf("=", start);
let i = eq + 1;
while (i < html.length && /\s/.test(html[i])) i++;
if (html[i] !== "{") throw new Error("Format __INIT__ salah");
let depth = 0, inStr = false, strChar = "", esc = false;
const startJson = i;
for (; i < html.length; i++) {
const ch = html[i];
if (esc) { esc = false; continue; }
if (ch === "\\") { esc = true; continue; }
if (inStr) { if (ch === strChar) inStr = false; continue; }
if (ch === '"' || ch === "'") { inStr = true; strChar = ch; continue; }
if (ch === "{") depth++;
else if (ch === "}") { depth--; if (depth === 0) return JSON.parse(html.slice(startJson, i + 1)); }
}
throw new Error("Parse __INIT__ gagal");
}
function parseInstagramUrl(rawUrl) {
const clean = rawUrl.replace(/\\([?=&])/g, "$1").trim();
const m = clean.match(/instagram\.com\/(?:reel|reels|p|tv)\/([A-Za-z0-9_-]+)/);
return m ? { cleanUrl: clean, shortcode: m[1] } : null;
}
function generateAuthToken(init, cssHash) {
const cssRev = cssHash.split("").reverse().join("");
const uaRev = (init.ua || "").split("").reverse().join("").substr(0, 10);
const hostname = new URL(CONFIG.SITE).hostname;
const appVer = init.appVersion;
const c = `xbuddy123sudo-${appVer}`;
const payload = hostname + cssRev + uaRev + SECRET + c + appVer;
return { authToken: N.encrypt(payload, cssRev), cssRev, hostname, appVersion: appVer };
}
async function fetchAccessToken(init, authToken, hostname) {
const res = await request(`${init.apiBase}/token`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
Accept: "application/json, text/plain, */*",
"X-Requested-With": "xmlhttprequest",
"x-auth-token": authToken,
"x-requested-domain": hostname,
Origin: CONFIG.SITE, Referer: `${CONFIG.SITE}/`,
},
body: "{}",
});
const data = JSON.parse(res.body);
if (!data.access_token) throw new Error(`Token gagal: ${res.body.slice(0, 150)}`);
return data.access_token;
}
function generateSig(urlEncoded, authToken) {
return N.encrypt(urlEncoded, authToken + CONFIG.SIG_SUFFIX);
}
async function callExtract(init, authToken, accessToken, url, opts = {}) {
const hostname = new URL(CONFIG.SITE).hostname;
const urlEncoded = encodeURIComponent(url);
const sig = generateSig(urlEncoded, authToken);
const body = {
url: urlEncoded,
_sig: sig,
searchEngine: opts.searchEngine ?? "yt",
skipCache: opts.skipCache ?? false,
extractionId: opts.extractionId ?? crypto.randomUUID(),
};
const res = await request(`${init.apiBase}/extract`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
Accept: "application/json, text/plain, */*",
"X-Requested-With": "xmlhttprequest",
"x-auth-token": authToken,
"x-access-token": accessToken,
"x-requested-domain": hostname,
Origin: CONFIG.SITE, Referer: `${CONFIG.SITE}/`,
},
body: JSON.stringify(body),
});
let parsed = null;
try { parsed = JSON.parse(res.body); } catch {}
return { status: res.status, data: parsed, raw: res.body };
}
function decodeUrl(hexUrl, cssHash, token, hostname) {
if (!hexUrl || !token) return null;
if (/^https?:\/\//i.test(hexUrl)) return hexUrl;
const binStr = N.hex2bin(hexUrl);
if (!binStr) return null;
const reversed = binStr.split("").reverse().join("");
const prefix = CONFIG.DECODE_PREFIX;
const hostLen = String(hostname.length);
const key = prefix + hostLen + cssHash + token;
log.info("DECODE", `key_len=${key.length} prefix=${prefix} hostLen=${hostLen} cssHash=${cssHash.slice(0, 8)}...`);
const result = N.decrypt(reversed, key);
if (typeof result !== "string") return null;
if (!/^[\x20-\x7e]+$/.test(result)) return null;
return result;
}
export async function scrape(rawUrl, options = {}) {
const parsed = parseInstagramUrl(rawUrl);
if (!parsed) return { success: false, error: "URL Instagram tidak valid", formats: [] };
const { cleanUrl } = parsed;
const result = {
success: false, input_url: rawUrl, clean_url: cleanUrl,
title: null, thumbnail: null, uploader: null,
formats: [], errors: [],
};
try {
const pageUrl = `${CONFIG.SITE}/${CONFIG.LOCALE}/process?url=${encodeURIComponent(cleanUrl)}`;
log.info("GET", pageUrl);
const pageRes = await request(pageUrl, {
headers: {
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
},
});
if (pageRes.status !== 200) throw new Error(`Page HTTP ${pageRes.status}`);
const init = extractInit(pageRes.body);
const cssMatch = pageRes.body.match(/\/build\/(?:assets\/)?main\.([^"']+?)\.css/);
if (!cssMatch) throw new Error("CSS hash tidak ditemukan");
const cssHash = cssMatch[1];
log.ok("INIT", `appVer=${init.appVersion} cssHash=${cssHash}`);
const { authToken, hostname } = generateAuthToken(init, cssHash);
const accessToken = await fetchAccessToken(init, authToken, hostname);
log.ok("TOKEN", `auth=${authToken.length}c access=${accessToken.length}c`);
const extraction = await callExtract(init, authToken, accessToken, cleanUrl, {
searchEngine: options.searchEngine ?? "yt",
});
if (!extraction.data || extraction.data.status !== "1" || !extraction.data.response) {
const msg = extraction.data?.message || "unknown";
result.errors.push({ stage: "extract", message: msg });
log.err("EXTRACT", msg);
return result;
}
const resp = extraction.data.response;
result.title = resp.title || null;
result.thumbnail = resp.thumbnail || null;
result.uploader = resp.uploader || null;
const responseToken = resp.token || accessToken;
log.info("TOKEN", `response.token=${responseToken.slice(0, 20)}... (len=${responseToken.length})`);
for (const f of resp.formats || []) {
let url = null;
try {
url = decodeUrl(f.url, cssHash, responseToken, hostname);
} catch (e) {
log.warn("DECODE", `Format ${f.quality}: ${e.message}`);
}
result.formats.push({
quality: f.quality || null,
type: f.type || null,
ext: f.ext || null,
width: f.width || null,
height: f.height || null,
size: f.size || null,
url,
});
}
result.success = result.formats.some((f) => f.url);
log.hit("DONE", `${result.formats.filter((f) => f.url).length}/${result.formats.length} URL decoded`);
} catch (err) {
result.errors.push({ stage: "fatal", message: err.message });
log.err("ERROR", err.message);
}
return result;
}
export async function download(url, outPath) {
const res = await request(url, {
timeout: CONFIG.DOWNLOAD_TIMEOUT_MS,
headers: { Referer: "https://www.instagram.com/" },
});
if (res.status !== 200) throw new Error(`HTTP ${res.status}`);
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, res.buffer);
return { path: outPath, size: res.buffer.length };
}
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: node ig.js <instagram-url> [-d] [-o dir]");
process.exit(1);
}
const url = args.find((a) => !a.startsWith("-"));
const flags = {
download: args.includes("-d") || args.includes("--download"),
outDir: args.includes("-o") ? args[args.indexOf("-o") + 1] : "./downloads",
};
const result = await scrape(url);
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
if (result.success && flags.download) {
console.error("");
const shortcode = (url.match(/\/(?:reel|reels|p|tv)\/([A-Za-z0-9_-]+)/) || [])[1] || "ig";
for (const f of result.formats) {
if (!f.url) continue;
const suffix = (f.quality || "file").replace(/[^a-z0-9_]/gi, "_");
const filename = `${shortcode}_${suffix}.${f.ext || "mp4"}`;
const outPath = path.join(flags.outDir, filename);
try {
const { size } = await download(f.url, outPath);
log.ok("DL", `${filename} (${(size / 1024).toFixed(1)} KB)`);
} catch (e) {
log.err("DL", `${filename}: ${e.message}`);
}
}
}
process.exit(result.success ? 0 : 1);
}
const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
if (isMain) main().catch((e) => { console.error("Fatal:", e); process.exit(1); });
Discussion
Thread
Belum ada komentarJadilah yang pertama membuka diskusi.