snapsave-media-downloader
Version:
Download Instagram, Facebook, TikTok and Twitter (X) media using snapsave.app downloader
243 lines (238 loc) • 10.1 kB
JavaScript
import { load } from 'cheerio';
import { $fetch } from 'ofetch';
import { ProxyAgent } from 'undici';
const facebookRegex = /^https?:\/\/(?:www\.|web\.|m\.)?facebook\.com\/(watch(\?v=|\/\?v=)[0-9]+(?!\/)|reel\/[0-9]+|[a-zA-Z0-9.\-_]+\/(videos|posts)\/[0-9]+|[0-9]+\/(videos|posts)\/[0-9]+|[a-zA-Z0-9]+\/(videos|posts)\/[0-9]+|share\/(v|r)\/[a-zA-Z0-9]+\/?)([^/?#&]+).*$|^https:\/\/fb\.watch\/[a-zA-Z0-9]+$/g;
const instagramRegex = /^https?:\/\/(?:www\.)?instagram\.com\/(?:[^/]+\/)?(?:p|reel|reels|tv|stories|share)\/([^/?#&]+).*/g;
const tiktokRegex = /^https?:\/\/(?:www\.|m\.|vm\.|vt\.)?tiktok\.com\/(?:@[^/]+\/(?:video|photo)\/\d+|v\/\d+|t\/[\w]+|[\w]+)\/?/g;
const twitterRegex = /^https:\/\/(?:x|twitter)\.com(?:\/(?:i\/web|[^/]+)\/status\/(\d+)(?:.*)?)?$/g;
const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36";
const normalizeURL = (url) => {
if (url.match(twitterRegex)) return url;
if (url.match(instagramRegex)) url = url.replace(/\?.*$/, "");
return /^(https?:\/\/)(?!www\.)[a-z0-9]+/i.test(url) ? url.replace(/^(https?:\/\/)([^./]+\.[^./]+)(\/.*)?$/, "$1www.$2$3") : url;
};
const fixThumbnail = (url) => {
const toReplace = "https://snapinsta.app/photo.php?photo=";
return url.includes(toReplace) ? decodeURIComponent(url.replace(toReplace, "")) : url;
};
function decodeSnapApp(args) {
let [h, u, n, t, e, r] = args;
const tNum = Number(t);
const eNum = Number(e);
function decode(d, e2, f) {
const g = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/".split("");
const hArr = g.slice(0, e2);
const iArr = g.slice(0, f);
let j = d.split("").reverse().reduce((a, b, c) => {
const idx = hArr.indexOf(b);
if (idx !== -1) return a + idx * Math.pow(e2, c);
return a;
}, 0);
let k = "";
while (j > 0) {
k = iArr[j % f] + k;
j = Math.floor(j / f);
}
return k || "0";
}
let result = "";
for (let i = 0, len = h.length; i < len; ) {
let s = "";
while (i < len && h[i] !== n[eNum]) {
s += h[i];
i++;
}
i++;
for (let j = 0; j < n.length; j++)
s = s.replace(new RegExp(n[j], "g"), j.toString());
result += String.fromCharCode(Number(decode(s, eNum, 10)) - tNum);
}
const fixEncoding = (str) => {
try {
const bytes = new Uint8Array(str.split("").map((char) => char.charCodeAt(0)));
return new TextDecoder("utf-8").decode(bytes);
} catch (e2) {
return str;
}
};
return fixEncoding(result);
}
function getEncodedSnapApp(data) {
return data.split("decodeURIComponent(escape(r))}(")[1].split("))")[0].split(",").map((v) => v.replace(/"/g, "").trim());
}
function getDecodedSnapSave(data) {
const errorMessage = data?.split('document.querySelector("#alert").innerHTML = "')?.[1]?.split('";')?.[0]?.trim();
if (errorMessage) throw new Error(errorMessage);
return data.split('getElementById("download-section").innerHTML = "')[1].split('"; document.getElementById("inputData").remove(); ')[0].replace(/\\(\\)?/g, "");
}
function getDecodedSnaptik(data) {
return data.split('$("#download").innerHTML = "')[1].split('"; document.getElementById("inputData").remove(); ')[0].replace(/\\(\\)?/g, "");
}
function decryptSnapSave(data) {
return getDecodedSnapSave(decodeSnapApp(getEncodedSnapApp(data)));
}
function decryptSnaptik(data) {
return getDecodedSnaptik(decodeSnapApp(getEncodedSnapApp(data)));
}
const snapsave = async (url, options) => {
const retry = { retry: options?.retry || 1, retryDelay: options?.retryDelay || 500 };
const UA = options?.userAgent || userAgent;
const dispatcher = options?.proxy ? new ProxyAgent(options.proxy) : void 0;
try {
const regexList = [facebookRegex, instagramRegex, twitterRegex, tiktokRegex];
const isValid = regexList.some((regex) => url.match(regex));
if (!isValid) return { success: false, message: "Invalid URL" };
const isTwitter = url.match(twitterRegex);
const isTiktok = url.match(tiktokRegex);
const formData = new URLSearchParams();
formData.append("url", normalizeURL(url));
if (isTiktok) {
const homeHtml = await $fetch("https://snaptik.app/", {
headers: { "user-agent": UA },
responseType: "text",
...retry,
...dispatcher ? { dispatcher } : {}
});
const $2 = load(homeHtml);
const token = $2("input[name='token']").val();
formData.append("token", token);
const data2 = await $fetch("https://snaptik.app/abc2.php", {
method: "POST",
headers: {
"accept": "*/*",
"content-type": "application/x-www-form-urlencoded",
"origin": "https://snaptik.app",
"referer": "https://snaptik.app/",
"user-agent": UA
},
body: formData,
responseType: "text",
...retry,
...dispatcher ? { dispatcher } : {}
});
const decode2 = decryptSnaptik(data2);
const $3 = load(decode2);
const _url = $3(".download-box > .video-links > a").attr("href");
const description = $3(".video-title").text().trim();
const preview = $3("#thumbnail").attr("src");
const spanText = $3(".video-links > a.button.download-file").text().trim();
const type = spanText === "Download photo" ? "image" : "video";
return { success: true, data: { description, preview, media: [{ url: _url, type }] } };
}
if (isTwitter) {
const homeHtml = await $fetch("https://twitterdownloader.snapsave.app/", {
headers: { "user-agent": UA },
responseType: "text",
...dispatcher ? { dispatcher } : {}
});
const $2 = load(homeHtml);
const token = $2("input[name='token']").val();
formData.append("token", token);
const response2 = await $fetch("https://twitterdownloader.snapsave.app/action.php", {
method: "POST",
headers: {
"accept": "*/*",
"content-type": "application/x-www-form-urlencoded",
"origin": "https://twitterdownloader.snapsave.app",
"referer": "https://twitterdownloader.snapsave.app/",
"user-agent": UA
},
responseType: "json",
body: formData,
...retry,
...dispatcher ? { dispatcher } : {}
});
const html2 = response2.data;
const $22 = load(html2);
const _url = $22("#download-block > .abuttons > a").attr("href");
const description = $22(".videotikmate-middle > p > span").text().trim();
const preview = $22(".videotikmate-left > img").attr("src");
const spanText = $22("#download-block > .abuttons > a > span > span").text().trim();
const type = spanText === "Download photo" ? "image" : "video";
return { success: true, data: { description, preview, media: [{ url: _url, type }] } };
}
const html = await $fetch("https://snapsave.app/action.php", {
method: "POST",
query: { lang: "en" },
headers: {
"accept": "*/*",
"content-type": "application/x-www-form-urlencoded",
"origin": "https://snapsave.app",
"referer": "https://snapsave.app/",
"user-agent": UA
},
body: formData,
responseType: "text",
...retry,
...dispatcher ? { dispatcher } : {}
});
const decode = decryptSnapSave(html);
const $ = load(decode);
const data = {};
const media = [];
if ($("table.table").length || $("article.media > figure").length) {
const description = $("span.video-des").text().trim();
const preview = $("article.media > figure").find("img").attr("src");
data.description = description;
data.preview = preview;
if ($("table.table").length) {
$("tbody > tr").each((_, el) => {
const $el = $(el);
const $td = $el.find("td");
const resolution = $td.eq(0).text();
let _url = $td.eq(2).find("a").attr("href") || $td.eq(2).find("button").attr("onclick");
const shouldRender = /get_progressApi/ig.test(_url || "");
if (shouldRender) {
_url = "https://snapsave.app" + /get_progressApi\('(.*?)'\)/.exec(_url || "")?.[1] || _url;
}
media.push({
resolution,
...shouldRender ? { shouldRender } : {},
url: _url,
type: resolution ? "video" : "image"
});
});
} else if ($("div.card").length) {
$("div.card").each((_, el) => {
const cardBody = $(el).find("div.card-body");
const aText = cardBody.find("a").text().trim();
const url2 = cardBody.find("a").attr("href");
const type = aText === "Download Photo" ? "image" : "video";
media.push({
url: url2,
type
});
});
} else {
const url2 = $("a").attr("href") || $("button").attr("onclick");
const aText = $("a").text().trim();
const type = aText === "Download Photo" ? "image" : "video";
media.push({
url: url2,
type
});
}
} else if ($("div.download-items").length) {
$("div.download-items").each((_, el) => {
const itemThumbnail = $(el).find("div.download-items__thumb > img").attr("src");
const itemBtn = $(el).find("div.download-items__btn");
const downloadUrl = itemBtn.find("a").attr("href");
const spanText = itemBtn.find("span").text().trim();
const type = spanText === "Download Photo" ? "image" : "video";
const url2 = type === "image" ? itemThumbnail : downloadUrl;
media.push({
url: url2,
...type === "video" ? {
thumbnail: itemThumbnail ? fixThumbnail(itemThumbnail) : void 0
} : {},
type
});
});
}
if (!media.length) return { success: false, message: "Blank data" };
return { success: true, data: { ...data, media } };
} catch (e) {
return { success: false, message: e instanceof Error ? e.message : "Something went wrong" };
}
};
export { snapsave };