instagram-url-direct
Version:
Get direct url to download from Instagram media links
199 lines (197 loc) • 6.95 kB
JavaScript
;
var __importDefault = (undefined && undefined.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.instagramGetUrl = instagramGetUrl;
const axios_1 = __importDefault(require("axios"));
const qs_1 = __importDefault(require("qs"));
//Main function
async function instagramGetUrl(url_media, config = { retries: 5, delay: 1000 }) {
return new Promise(async (resolve, reject) => {
try {
url_media = await checkRedirect(url_media);
const SHORTCODE = getShortcode(url_media);
const INSTAGRAM_REQUEST = await instagramRequest(SHORTCODE, config.retries, config.delay);
const OUTPUT_DATA = createOutputData(INSTAGRAM_REQUEST);
resolve(OUTPUT_DATA);
}
catch (err) {
reject(err);
}
});
}
//Utilities
async function checkRedirect(url) {
let split_url = url.split("/");
if (split_url.includes("share")) {
let res = await axios_1.default.get(url);
return res.request.path;
}
return url;
}
function formatPostInfo(requestData) {
try {
let mediaCapt = requestData.edge_media_to_caption.edges;
const capt = (mediaCapt.length === 0) ? "" : mediaCapt[0].node.text;
return {
owner_username: requestData.owner.username,
owner_fullname: requestData.owner.full_name,
is_verified: requestData.owner.is_verified,
is_private: requestData.owner.is_private,
likes: requestData.edge_media_preview_like.count,
is_ad: requestData.is_ad,
caption: capt
};
}
catch (err) {
throw new Error(`Failed to format post info: ${err.message}`);
}
}
function formatMediaDetails(mediaData) {
try {
if (mediaData.is_video) {
return {
type: "video",
dimensions: mediaData.dimensions,
video_view_count: mediaData.video_view_count,
url: mediaData.video_url,
thumbnail: mediaData.display_url
};
}
else {
return {
type: "image",
dimensions: mediaData.dimensions,
url: mediaData.display_url
};
}
}
catch (err) {
throw new Error(`Failed to format media details: ${err.message}`);
}
}
function getShortcode(url) {
try {
let split_url = url.split("/");
let post_tags = ["p", "reel", "tv", "reels"];
let index_shortcode = split_url.findIndex(item => post_tags.includes(item)) + 1;
let shortcode = split_url[index_shortcode];
return shortcode;
}
catch (err) {
throw new Error(`Failed to obtain shortcode: ${err.message}`);
}
}
async function getCSRFToken() {
try {
let config = {
method: 'GET',
url: 'https://www.instagram.com/',
};
const token = await new Promise((resolve, reject) => {
axios_1.default.request(config).then((response) => {
if (!response.headers['set-cookie']) {
reject(new Error('CSRF token not found in response headers.'));
}
else {
const csrfCookie = response.headers['set-cookie'][0];
const csrfToken = csrfCookie.split(";")[0].replace("csrftoken=", '');
resolve(csrfToken);
}
}).catch((err) => {
reject(err);
});
});
return token;
}
catch (err) {
throw new Error(`Failed to obtain CSRF: ${err.message}`);
}
}
function isSidecar(requestData) {
try {
return requestData["__typename"] == "XDTGraphSidecar";
}
catch (err) {
throw new Error(`Failed sidecar verification: ${err.message}`);
}
}
async function instagramRequest(shortcode, retries, delay) {
var _a;
try {
const BASE_URL = "https://www.instagram.com/graphql/query";
const INSTAGRAM_DOCUMENT_ID = "9510064595728286";
let dataBody = qs_1.default.stringify({
'variables': JSON.stringify({
'shortcode': shortcode,
'fetch_tagged_user_count': null,
'hoisted_comment_id': null,
'hoisted_reply_id': null
}),
'doc_id': INSTAGRAM_DOCUMENT_ID
});
const token = await getCSRFToken();
let config = {
method: 'post',
maxBodyLength: Infinity,
url: BASE_URL,
headers: {
'X-CSRFToken': token,
'Content-Type': 'application/x-www-form-urlencoded',
},
data: dataBody
};
const { data } = await axios_1.default.request(config);
if (!((_a = data.data) === null || _a === void 0 ? void 0 : _a.xdt_shortcode_media))
throw new Error("Only posts/reels supported, check if your link is valid.");
return data.data.xdt_shortcode_media;
}
catch (err) {
const errorCodes = [429, 403];
if (err.response && errorCodes.includes(err.response.status) && retries > 0) {
const retryAfter = err.response.headers['retry-after'];
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : delay;
await new Promise(res => setTimeout(res, waitTime));
return instagramRequest(shortcode, retries - 1, delay * 2);
}
throw new Error(`Failed instagram request: ${err.message}`);
}
}
function createOutputData(requestData) {
try {
let url_list = [], media_details = [];
const IS_SIDECAR = isSidecar(requestData);
if (IS_SIDECAR) {
//Post with sidecar
requestData.edge_sidecar_to_children.edges.forEach((media) => {
media_details.push(formatMediaDetails(media.node));
if (media.node.is_video) { //Sidecar video item
url_list.push(media.node.video_url);
}
else { //Sidecar image item
url_list.push(media.node.display_url);
}
});
}
else {
//Post without sidecar
media_details.push(formatMediaDetails(requestData));
if (requestData.is_video) { // Video media
url_list.push(requestData.video_url);
}
else { //Image media
url_list.push(requestData.display_url);
}
}
return {
results_number: url_list.length,
url_list,
post_info: formatPostInfo(requestData),
media_details
};
}
catch (err) {
throw new Error(`Failed to create output data: ${err.message}`);
}
}