diff --git a/package.json b/package.json index 221ce5e..a7ddb95 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Kayo Souza", "name": "insta-downloader", - "version": "3.2.0", + "version": "3.3.0", "description": "An application to download content from Instagram", "main": "src/index.js", "scripts": { diff --git a/src/Downloader.js b/src/Downloader.js index a77c56b..35c4d8e 100644 --- a/src/Downloader.js +++ b/src/Downloader.js @@ -221,7 +221,8 @@ export default class Downloader { /** @type {import("axios").AxiosResponse} */ const response = await this.Request(new URL(API_FACEBOOK_ACCOUNT, BASE_URL), "POST", { headers: { Referer: BASE_URL + "/" }, - responseType: "json" + responseType: "json", + maxRedirects: 0 }) if(typeof response?.data === "object" && "status" in response.data){ @@ -256,6 +257,7 @@ export default class Downloader { * @param {string} username */ async GetHighlights(user_id, username){ + const { config } = this const url = new URL(API_QUERY, BASE_URL) /** @type {import("axios").AxiosResponse} */ @@ -271,8 +273,23 @@ export default class Downloader { }) try{ - return response.data.data.highlights.edges.map(({ node }) => node) + const { cookie: { sessionid } } = config + const { vary } = response.headers + + if( + !vary || + !sessionid || + sessionid === '""' || + !vary.includes("Cookie") || + !vary.includes("Accept-Encoding") + ) throw "Login session expired" + + const { data: { highlights } } = response.data + + return highlights.edges.map(({ node }) => node) }catch(error){ + if(typeof error === "string") throw new Error(error) + throw new Error(`Failed to get user (${username || user_id}) highlights`, { cause: /** @type {Error} */ (error).message.replace(/\[?Error\]?:? ?/, "") }) @@ -288,7 +305,12 @@ export default class Downloader { first ??= this.queue?.limit ?? 10 - /** @type {import("axios").AxiosResponse} */ + /** + * @type {import("axios").AxiosResponse< + * | import("./typings/api.js").HighlightsAPIResponse + * | import("./typings/api.js").GraphAPIResponseError + * > + * } */ const response = await this.Request(url, "POST", { data: new URLSearchParams({ variables: JSON.stringify({ @@ -307,7 +329,15 @@ export default class Downloader { responseType: "json" }) - return response.data.data.xdt_api__v1__feed__reels_media__connection.edges.map(({ node }) => node) + const { xdt_api__v1__feed__reels_media__connection: feed } = response.data.data || {} + + if(!feed){ + const { errors } = /** @type {import("./typings/api.js").GraphAPIResponseError} */ (response.data) + const error = errors[0] + throw `Error downloading highlights (${error.severity}): ${error.message}` + } + + return feed.edges.map(({ node }) => node) } /** * @param {`${number}`} userId @@ -462,6 +492,9 @@ export default class Downloader { if(typeof response?.data === "object"){ const { more_available, num_results, next_max_id, items } = response.data + if(!Array.isArray(items)) throw new Error(`Couldn't get user timeline, user: ${username}`) + if(!items.length) throw new Error(`No items found in timeline, user: ${username}`) + if(num_results === 0) break if(first){ @@ -667,7 +700,7 @@ export default class Downloader { } } - throw error instanceof Error ? new Error(error.name, { cause: error.message }) : error + throw error instanceof Error ? new Error(error.name.replace(/\[?Error\]?:? ?/, ""), { cause: error.message }) : error } } async CheckServerConfig(){ diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts index 144fe03..20b97ef 100644 --- a/src/typings/api.d.ts +++ b/src/typings/api.d.ts @@ -1253,3 +1253,13 @@ export interface StoriesAPIResponse { reels_media: ReelMedia[] status: APIStatus } + +export interface GraphAPIResponseError { + errors: { + message: string + severity: string + extensions: any + }[] + data: null + status: "ok" +}