snapdouyin
douyin downloader
20 Juni 2026742 file
douyin.jstypescript · 129 baris
const fs = require("fs");const { pipeline } = require("stream/promises");const { Readable } = require("stream");const BASE = "https://snapdouyin.app";const UA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36";function calculateHash(url, salt) { return Buffer.from(url).toString("base64") + (url.length + 1000) + Buffer.from(salt).toString("base64");}function parseCookies(setCookieHeaders) { if (!setCookieHeaders) return ""; return setCookieHeaders.map((c) => c.split(";")[0]).join("; ");}async function getSessionAndToken() { const res = await fetch(BASE + "/", { headers: { "User-Agent": UA } }); const html = await res.text(); const setCookie = res.headers.getSetCookie ? res.headers.getSetCookie() : []; const cookie = parseCookies(setCookie); const match = html.match(/id="token"[^>]*value="([^"]+)"/); if (!match) throw new Error("Token tidak ditemukan di halaman"); return { cookie, token: match[1] };}async function getVideoData(videoUrl, retries = 2) { for (let attempt = 0; attempt <= retries; attempt++) { try { const { cookie: initialCookie, token } = await getSessionAndToken(); const hash = calculateHash(videoUrl, "aio-dl"); const body = new URLSearchParams(); body.append("url", videoUrl); body.append("token", token); body.append("hash", hash); const res = await fetch(BASE + "/wp-json/mx-downloader/video-data/", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Origin: BASE, Referer: BASE + "/", Cookie: initialCookie, "User-Agent": UA, }, body, }); if (!res.ok) throw new Error(`video-data gagal dengan status ${res.status}`); const newSetCookie = res.headers.getSetCookie ? res.headers.getSetCookie() : []; const newCookie = parseCookies(newSetCookie); const cookie = newCookie || initialCookie; const data = await res.json(); if (data.error) throw new Error(data.error); data._cookie = cookie; return data; } catch (err) { if (attempt === retries) throw err; } }}async function downloadMedia(mediaUrl, cookie, outputPath) { const res = await fetch(mediaUrl + "&bandwidth_saving=1", { headers: { accept: "*/*", "accept-language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", Cookie: cookie, "User-Agent": UA, }, signal: AbortSignal.timeout(30000), }); if (!res.ok) throw new Error(`download gagal dengan status ${res.status}`); if (!res.body) throw new Error("Response tidak punya body"); await pipeline(Readable.fromWeb(res.body), fs.createWriteStream(outputPath)); return outputPath;}async function main() { const url = process.argv[2]; const qualityArg = process.argv[3]; if (!url) { console.error("Gunakan: node snapdouyin.js <url_douyin> [index_kualitas]"); process.exit(1); } const data = await getVideoData(url); if (qualityArg === undefined) { console.log(JSON.stringify(data, null, 2)); return; } const media = data.medias[Number(qualityArg)]; if (!media) throw new Error("Index kualitas tidak valid"); const filename = `douyin-${media.quality.replace(/\s/g, "")}-${Date.now()}.${media.extension}`; await downloadMedia(media.url, data._cookie, filename); console.log(JSON.stringify({ url: data.url, title: data.title, thumbnail: data.thumbnail, duration: data.duration, source: data.source, downloaded: { saved: filename, quality: media.quality, extension: media.extension, size: media.formattedSize, }, }, null, 2));}main().catch((err) => { console.error("Error:", err.message); if (err.cause) console.error("Cause:", err.cause.message || err.cause); process.exit(1);});README.mdmarkdown · 18 baris
format tersedia
| Index | Kualitas |
|---|---|
| 0 | hd ⭐ |
| 1 | hd |
| 2 | sd |
| 3 |
Komentar
Masuk untuk ikut berkomentar.
Belum ada komentar.