open-nami 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +39 -21
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -9,8 +9,8 @@ var require_package = __commonJS({
|
|
|
9
9
|
"package.json"(exports, module) {
|
|
10
10
|
module.exports = {
|
|
11
11
|
name: "open-nami",
|
|
12
|
-
version: "1.0.
|
|
13
|
-
description: "Open Nami
|
|
12
|
+
version: "1.0.6",
|
|
13
|
+
description: "Open Nami: terminal-first bulk social media profile downloader for Instagram, TikTok, Facebook & X.",
|
|
14
14
|
type: "module",
|
|
15
15
|
bin: {
|
|
16
16
|
"open-nami": "dist/cli.js",
|
|
@@ -2153,18 +2153,28 @@ var InstagramExtractor = class {
|
|
|
2153
2153
|
return { edges: [], hasNextPage: false };
|
|
2154
2154
|
}
|
|
2155
2155
|
async fetchNextPostsPage(userId, username, cursor, options, tokens) {
|
|
2156
|
+
try {
|
|
2157
|
+
const page = await this.fetchPostsTabGraphQL(username, cursor, tokens || {}, options);
|
|
2158
|
+
if (page.edges && page.edges.length > 0) {
|
|
2159
|
+
return page;
|
|
2160
|
+
}
|
|
2161
|
+
} catch (err) {
|
|
2162
|
+
logExtractorDebug("PolarisProfilePostsTabContentQuery_connection", err);
|
|
2163
|
+
}
|
|
2156
2164
|
if (userId) {
|
|
2157
2165
|
const variables = JSON.stringify({ id: userId, first: 12, after: cursor });
|
|
2158
2166
|
const url = `https://www.instagram.com/graphql/query/?query_hash=${IG_TIMELINE_QUERY_HASH}&variables=${encodeURIComponent(variables)}`;
|
|
2159
2167
|
try {
|
|
2168
|
+
const headers = {
|
|
2169
|
+
"X-IG-App-ID": IG_APP_ID,
|
|
2170
|
+
"Referer": `https://www.instagram.com/${username}/`
|
|
2171
|
+
};
|
|
2172
|
+
if (tokens?.csrfToken) headers["X-CSRFToken"] = tokens.csrfToken;
|
|
2160
2173
|
const res = await this.httpClient.fetchJson(url, {
|
|
2161
2174
|
cookiePath: options.cookiePath,
|
|
2162
2175
|
cookieDomain: "instagram.com",
|
|
2163
2176
|
signal: options.signal,
|
|
2164
|
-
headers
|
|
2165
|
-
"X-IG-App-ID": IG_APP_ID,
|
|
2166
|
-
"Referer": `https://www.instagram.com/${username}/`
|
|
2167
|
-
}
|
|
2177
|
+
headers
|
|
2168
2178
|
});
|
|
2169
2179
|
const data = res?.data?.user?.edge_owner_to_timeline_media;
|
|
2170
2180
|
if (data?.edges && data.edges.length > 0) {
|
|
@@ -2178,25 +2188,19 @@ var InstagramExtractor = class {
|
|
|
2178
2188
|
logExtractorDebug("edge_owner_to_timeline_media", err);
|
|
2179
2189
|
}
|
|
2180
2190
|
}
|
|
2181
|
-
try {
|
|
2182
|
-
const page = await this.fetchPostsTabGraphQL(username, cursor, tokens || {}, options);
|
|
2183
|
-
if (page.edges && page.edges.length > 0) {
|
|
2184
|
-
return page;
|
|
2185
|
-
}
|
|
2186
|
-
} catch (err) {
|
|
2187
|
-
logExtractorDebug("PolarisProfilePostsTabContentQuery_connection", err);
|
|
2188
|
-
}
|
|
2189
2191
|
if (userId) {
|
|
2190
2192
|
try {
|
|
2191
2193
|
const restUrl = `https://www.instagram.com/api/v1/feed/user/${userId}/?count=12&max_id=${encodeURIComponent(cursor)}`;
|
|
2194
|
+
const headers = {
|
|
2195
|
+
"X-IG-App-ID": IG_APP_ID,
|
|
2196
|
+
"Referer": `https://www.instagram.com/${username}/`
|
|
2197
|
+
};
|
|
2198
|
+
if (tokens?.csrfToken) headers["X-CSRFToken"] = tokens.csrfToken;
|
|
2192
2199
|
const res = await this.httpClient.fetchJson(restUrl, {
|
|
2193
2200
|
cookiePath: options.cookiePath,
|
|
2194
2201
|
cookieDomain: "instagram.com",
|
|
2195
2202
|
signal: options.signal,
|
|
2196
|
-
headers
|
|
2197
|
-
"X-IG-App-ID": IG_APP_ID,
|
|
2198
|
-
"Referer": `https://www.instagram.com/${username}/`
|
|
2199
|
-
}
|
|
2203
|
+
headers
|
|
2200
2204
|
});
|
|
2201
2205
|
if (Array.isArray(res?.items) && res.items.length > 0) {
|
|
2202
2206
|
const edges = res.items.map((item) => ({ node: item }));
|
|
@@ -2283,8 +2287,10 @@ var InstagramExtractor = class {
|
|
|
2283
2287
|
logExtractorDebug("fetchUserProfile", err);
|
|
2284
2288
|
}
|
|
2285
2289
|
let webTokens = {};
|
|
2286
|
-
|
|
2290
|
+
try {
|
|
2287
2291
|
webTokens = await this.fetchWebTokens(username, options);
|
|
2292
|
+
} catch (err) {
|
|
2293
|
+
logExtractorDebug("fetchWebTokens", err);
|
|
2288
2294
|
}
|
|
2289
2295
|
let userId = user?.id || webTokens.userId;
|
|
2290
2296
|
if (!userId && (subDir === "Stories" || subDir === "Highlights")) {
|
|
@@ -3359,6 +3365,14 @@ function sanitizeFilename(rawFilename, fallbackId, ext) {
|
|
|
3359
3365
|
}
|
|
3360
3366
|
return sanitizedBase.includes(".") ? sanitizedBase : `${sanitizedBase}.${cleanExt}`;
|
|
3361
3367
|
}
|
|
3368
|
+
function isCdnUrl(rawUrl) {
|
|
3369
|
+
try {
|
|
3370
|
+
const hostname = new URL(rawUrl).hostname.toLowerCase();
|
|
3371
|
+
return hostname.endsWith(".cdninstagram.com") || hostname.endsWith(".fbcdn.net") || hostname.endsWith(".tiktokcdn.com") || hostname.endsWith(".akamaized.net") || hostname === "pbs.twimg.com" || hostname === "video.twimg.com";
|
|
3372
|
+
} catch {
|
|
3373
|
+
return false;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3362
3376
|
async function downloadMediaStream(items, options) {
|
|
3363
3377
|
const { jobName, destDir, signal, onProgress } = options;
|
|
3364
3378
|
const httpClient = options.httpClient ?? new HttpClient();
|
|
@@ -3439,10 +3453,13 @@ async function downloadMediaStream(items, options) {
|
|
|
3439
3453
|
if (options.referer) {
|
|
3440
3454
|
reqHeaders["Referer"] = options.referer;
|
|
3441
3455
|
}
|
|
3456
|
+
const isCdn = isCdnUrl(item.url);
|
|
3442
3457
|
const response = await httpClient.request(item.url, {
|
|
3443
3458
|
signal,
|
|
3444
|
-
cookiePath: options.cookiePath,
|
|
3445
|
-
headers: reqHeaders
|
|
3459
|
+
cookiePath: isCdn ? void 0 : options.cookiePath,
|
|
3460
|
+
headers: reqHeaders,
|
|
3461
|
+
retries: 2,
|
|
3462
|
+
retryDelayMs: 1500
|
|
3446
3463
|
});
|
|
3447
3464
|
if (!response.ok || !response.body) {
|
|
3448
3465
|
try {
|
|
@@ -3463,6 +3480,7 @@ async function downloadMediaStream(items, options) {
|
|
|
3463
3480
|
await fsPromises2.rename(tmpPath, finalPath);
|
|
3464
3481
|
archive.add(item.id);
|
|
3465
3482
|
downloaded++;
|
|
3483
|
+
consecutiveErrors = 0;
|
|
3466
3484
|
onProgress?.({
|
|
3467
3485
|
job: jobName,
|
|
3468
3486
|
downloadedCount: downloaded,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-nami",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Open Nami
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Open Nami: terminal-first bulk social media profile downloader for Instagram, TikTok, Facebook & X.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"open-nami": "dist/cli.js",
|