UNPKG

snapsave-media-downloader

Version:

Download Instagram, Facebook, TikTok and Twitter (X) media using snapsave.app downloader

228 lines (223 loc) 9.29 kB
import { load } from 'cheerio'; 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/133.0.0.0 Safari/537.36"; const normalizeURL = (url) => { if (url.match(twitterRegex)) return url; 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) { 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) => { 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 response2 = await fetch("https://snaptik.app/", { headers: { "user-agent": userAgent } }); const homeHtml = await response2.text(); const $2 = load(homeHtml); const token = $2("input[name='token']").val(); formData.append("token", token); const response22 = 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": userAgent }, body: formData }); const data2 = await response22.text(); 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 response2 = await fetch("https://twitterdownloader.snapsave.app/", { headers: { "user-agent": userAgent } }); const homeHtml = await response2.text(); const $2 = load(homeHtml); const token = $2("input[name='token']").val(); formData.append("token", token); const response22 = 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": userAgent }, body: formData }); const data2 = await response22.json(); const html2 = data2?.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 response = await fetch("https://snapsave.app/action.php?lang=en", { method: "POST", headers: { "accept": "*/*", "content-type": "application/x-www-form-urlencoded", "origin": "https://snapsave.app", "referer": "https://snapsave.app/", "user-agent": userAgent }, body: formData }); const html = await response.text(); 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 url2 = itemBtn.find("a").attr("href"); const spanText = itemBtn.find("span").text().trim(); const type = spanText === "Download Photo" ? "image" : "video"; 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: "Something went wrong" }; } }; export { snapsave };