open-nami 1.2.0 → 1.2.2
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 +228 -53
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ var require_package = __commonJS({
|
|
|
9
9
|
"package.json"(exports, module) {
|
|
10
10
|
module.exports = {
|
|
11
11
|
name: "open-nami",
|
|
12
|
-
version: "1.2.
|
|
12
|
+
version: "1.2.2",
|
|
13
13
|
description: "Open Nami: terminal-first bulk social media profile downloader for Instagram, TikTok, Facebook & X.",
|
|
14
14
|
type: "module",
|
|
15
15
|
bin: {
|
|
@@ -729,6 +729,7 @@ async function clearCheckpoint(destDir) {
|
|
|
729
729
|
import path10 from "path";
|
|
730
730
|
import os5 from "os";
|
|
731
731
|
import fs10 from "fs/promises";
|
|
732
|
+
import fsSync from "fs";
|
|
732
733
|
|
|
733
734
|
// src/lib/cookies.ts
|
|
734
735
|
import fs2 from "fs";
|
|
@@ -1210,6 +1211,9 @@ var IG_QUERIES = {
|
|
|
1210
1211
|
PolarisProfilePageContentQuery: "28649589871350527"
|
|
1211
1212
|
};
|
|
1212
1213
|
var IG_POLARIS_DOC_ID = IG_QUERIES.PolarisProfilePostsTabContentQuery_connection;
|
|
1214
|
+
function isExplicitAuthError(msg) {
|
|
1215
|
+
return msg.includes("401") || msg.includes("403") || msg.includes("login_required") || msg.includes("checkpoint_required") || msg.includes("not-logged-in");
|
|
1216
|
+
}
|
|
1213
1217
|
function logExtractorDebug(endpoint, err) {
|
|
1214
1218
|
if (process.env.DEBUG || process.env.OPEN_NAMI_DEBUG) {
|
|
1215
1219
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1378,39 +1382,63 @@ var InstagramExtractor = class {
|
|
|
1378
1382
|
}
|
|
1379
1383
|
return user;
|
|
1380
1384
|
}
|
|
1381
|
-
async fetchHighlightsTray(userId, options) {
|
|
1385
|
+
async fetchHighlightsTray(userId, options, tokens) {
|
|
1386
|
+
let authError = null;
|
|
1382
1387
|
try {
|
|
1383
1388
|
const variables = JSON.stringify({ user_id: userId });
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1389
|
+
const bodyParams = new URLSearchParams({
|
|
1390
|
+
av: "17841415137994167",
|
|
1391
|
+
__d: "www",
|
|
1392
|
+
__user: "0",
|
|
1393
|
+
__a: "1",
|
|
1394
|
+
__req: "0",
|
|
1395
|
+
dpr: "1",
|
|
1396
|
+
fb_api_caller_class: "RelayModern",
|
|
1397
|
+
fb_api_req_friendly_name: "PolarisProfileStoryHighlightsTrayContentQuery",
|
|
1398
|
+
server_timestamps: "true",
|
|
1399
|
+
variables,
|
|
1400
|
+
doc_id: IG_QUERIES.PolarisProfileStoryHighlightsTrayContentQuery
|
|
1401
|
+
});
|
|
1402
|
+
if (tokens?.fbDtsg) bodyParams.set("fb_dtsg", tokens.fbDtsg);
|
|
1403
|
+
if (tokens?.lsd) bodyParams.set("lsd", tokens.lsd);
|
|
1404
|
+
const headers = {
|
|
1405
|
+
"Accept": "*/*",
|
|
1406
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1407
|
+
"X-IG-App-ID": IG_APP_ID,
|
|
1408
|
+
"X-FB-Friendly-Name": "PolarisProfileStoryHighlightsTrayContentQuery",
|
|
1409
|
+
"Referer": "https://www.instagram.com/",
|
|
1410
|
+
"Origin": "https://www.instagram.com",
|
|
1411
|
+
"X-ASBD-ID": "359341"
|
|
1412
|
+
};
|
|
1413
|
+
if (tokens?.csrfToken) headers["X-CSRFToken"] = tokens.csrfToken;
|
|
1414
|
+
if (tokens?.lsd) headers["X-FB-LSD"] = tokens.lsd;
|
|
1388
1415
|
const res = await this.httpClient.fetchJson("https://www.instagram.com/graphql/query", {
|
|
1389
1416
|
method: "POST",
|
|
1390
|
-
body,
|
|
1417
|
+
body: bodyParams.toString(),
|
|
1391
1418
|
cookiePath: options.cookiePath,
|
|
1392
1419
|
cookieDomain: "instagram.com",
|
|
1393
1420
|
signal: options.signal,
|
|
1394
|
-
headers
|
|
1395
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
1396
|
-
"X-IG-App-ID": IG_APP_ID,
|
|
1397
|
-
"X-FB-Friendly-Name": "PolarisProfileStoryHighlightsTrayContentQuery",
|
|
1398
|
-
"Referer": "https://www.instagram.com/"
|
|
1399
|
-
}
|
|
1421
|
+
headers
|
|
1400
1422
|
});
|
|
1401
1423
|
const edges = res?.data?.highlights?.edges;
|
|
1402
|
-
if (Array.isArray(edges)
|
|
1424
|
+
if (Array.isArray(edges)) {
|
|
1403
1425
|
if (res?.errors && Array.isArray(res.errors) && res.errors.length > 0) {
|
|
1404
1426
|
logExtractorDebug("PolarisProfileStoryHighlightsTrayContentQuery partial field errors", res.errors);
|
|
1405
1427
|
}
|
|
1406
|
-
return edges.map((e) => e.node);
|
|
1428
|
+
return edges.map((e) => e.node).filter(Boolean);
|
|
1407
1429
|
}
|
|
1408
1430
|
if (res?.errors && Array.isArray(res.errors) && res.errors.length > 0) {
|
|
1409
1431
|
const errMsg = res.errors.map((e) => e.message || JSON.stringify(e)).join("; ");
|
|
1410
|
-
|
|
1432
|
+
if (isExplicitAuthError(errMsg)) {
|
|
1433
|
+
authError = new Error("Instagram Highlights require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
1434
|
+
}
|
|
1411
1435
|
}
|
|
1412
1436
|
} catch (err) {
|
|
1413
1437
|
logExtractorDebug("PolarisProfileStoryHighlightsTrayContentQuery", err);
|
|
1438
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1439
|
+
if (isExplicitAuthError(msg)) {
|
|
1440
|
+
authError = new Error("Instagram Highlights require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
1441
|
+
}
|
|
1414
1442
|
}
|
|
1415
1443
|
const url = `https://www.instagram.com/api/v1/highlights/${userId}/highlights_tray/`;
|
|
1416
1444
|
try {
|
|
@@ -1419,17 +1447,84 @@ var InstagramExtractor = class {
|
|
|
1419
1447
|
cookieDomain: "instagram.com",
|
|
1420
1448
|
signal: options.signal,
|
|
1421
1449
|
headers: {
|
|
1422
|
-
"X-IG-App-ID": IG_APP_ID
|
|
1450
|
+
"X-IG-App-ID": IG_APP_ID,
|
|
1451
|
+
"Referer": "https://www.instagram.com/"
|
|
1423
1452
|
}
|
|
1424
1453
|
});
|
|
1425
|
-
|
|
1454
|
+
if (res?.message === "login_required" || res?.message === "checkpoint_required") {
|
|
1455
|
+
throw new Error("Instagram Highlights require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
1456
|
+
}
|
|
1457
|
+
return res?.tray ?? [];
|
|
1426
1458
|
} catch (err) {
|
|
1427
1459
|
logExtractorDebug("Highlights legacy REST", err);
|
|
1460
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1461
|
+
if (isExplicitAuthError(msg)) {
|
|
1462
|
+
throw new Error("Instagram Highlights require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
1463
|
+
}
|
|
1464
|
+
if (authError) throw authError;
|
|
1428
1465
|
return [];
|
|
1429
1466
|
}
|
|
1430
1467
|
}
|
|
1431
|
-
async
|
|
1468
|
+
async fetchReelsMediaGraphQL(reelIds, options, tokens) {
|
|
1432
1469
|
if (reelIds.length === 0) return [];
|
|
1470
|
+
try {
|
|
1471
|
+
const variables = JSON.stringify({
|
|
1472
|
+
initial_reel_id: reelIds[0],
|
|
1473
|
+
reel_ids: reelIds,
|
|
1474
|
+
first: 3,
|
|
1475
|
+
last: 2,
|
|
1476
|
+
__relay_internal__pv__PolarisCommunityNoteStoriesLabelEnabledrelayprovider: true
|
|
1477
|
+
});
|
|
1478
|
+
const bodyParams = new URLSearchParams({
|
|
1479
|
+
av: "17841415137994167",
|
|
1480
|
+
__d: "www",
|
|
1481
|
+
__user: "0",
|
|
1482
|
+
__a: "1",
|
|
1483
|
+
__req: "0",
|
|
1484
|
+
dpr: "1",
|
|
1485
|
+
fb_api_caller_class: "RelayModern",
|
|
1486
|
+
fb_api_req_friendly_name: "PolarisStoriesV3ReelPageGalleryQuery",
|
|
1487
|
+
server_timestamps: "true",
|
|
1488
|
+
variables,
|
|
1489
|
+
doc_id: IG_QUERIES.PolarisStoriesV3ReelPageGalleryQuery
|
|
1490
|
+
});
|
|
1491
|
+
if (tokens?.fbDtsg) bodyParams.set("fb_dtsg", tokens.fbDtsg);
|
|
1492
|
+
if (tokens?.lsd) bodyParams.set("lsd", tokens.lsd);
|
|
1493
|
+
const headers = {
|
|
1494
|
+
"Accept": "*/*",
|
|
1495
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1496
|
+
"X-IG-App-ID": IG_APP_ID,
|
|
1497
|
+
"X-FB-Friendly-Name": "PolarisStoriesV3ReelPageGalleryQuery",
|
|
1498
|
+
"Referer": "https://www.instagram.com/",
|
|
1499
|
+
"Origin": "https://www.instagram.com",
|
|
1500
|
+
"X-ASBD-ID": "359341"
|
|
1501
|
+
};
|
|
1502
|
+
if (tokens?.csrfToken) headers["X-CSRFToken"] = tokens.csrfToken;
|
|
1503
|
+
if (tokens?.lsd) headers["X-FB-LSD"] = tokens.lsd;
|
|
1504
|
+
const res = await this.httpClient.fetchJson("https://www.instagram.com/graphql/query", {
|
|
1505
|
+
method: "POST",
|
|
1506
|
+
body: bodyParams.toString(),
|
|
1507
|
+
cookiePath: options.cookiePath,
|
|
1508
|
+
cookieDomain: "instagram.com",
|
|
1509
|
+
signal: options.signal,
|
|
1510
|
+
headers
|
|
1511
|
+
});
|
|
1512
|
+
const connection = res?.data?.xdt_api__v1__feed__reels_media__connection;
|
|
1513
|
+
const edges = connection?.edges;
|
|
1514
|
+
if (Array.isArray(edges) && edges.length > 0) {
|
|
1515
|
+
return edges.map((e) => e.node).filter(Boolean);
|
|
1516
|
+
}
|
|
1517
|
+
} catch (err) {
|
|
1518
|
+
logExtractorDebug("PolarisStoriesV3ReelPageGalleryQuery", err);
|
|
1519
|
+
}
|
|
1520
|
+
return [];
|
|
1521
|
+
}
|
|
1522
|
+
async fetchReelsMedia(reelIds, options, tokens) {
|
|
1523
|
+
if (reelIds.length === 0) return [];
|
|
1524
|
+
const gqlReels = await this.fetchReelsMediaGraphQL(reelIds, options, tokens);
|
|
1525
|
+
if (gqlReels.length > 0) {
|
|
1526
|
+
return gqlReels;
|
|
1527
|
+
}
|
|
1433
1528
|
const idsParam = reelIds.join(",");
|
|
1434
1529
|
const url = `https://www.instagram.com/api/v1/feed/reels_media/?reel_ids=${encodeURIComponent(idsParam)}`;
|
|
1435
1530
|
try {
|
|
@@ -1438,13 +1533,22 @@ var InstagramExtractor = class {
|
|
|
1438
1533
|
cookieDomain: "instagram.com",
|
|
1439
1534
|
signal: options.signal,
|
|
1440
1535
|
headers: {
|
|
1441
|
-
"X-IG-App-ID": IG_APP_ID
|
|
1536
|
+
"X-IG-App-ID": IG_APP_ID,
|
|
1537
|
+
"Referer": "https://www.instagram.com/"
|
|
1442
1538
|
}
|
|
1443
1539
|
});
|
|
1444
|
-
if (res
|
|
1445
|
-
|
|
1540
|
+
if (res?.message === "login_required" || res?.message === "checkpoint_required") {
|
|
1541
|
+
throw new Error("Instagram Stories require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
1542
|
+
}
|
|
1543
|
+
if (res?.reels_media) return res.reels_media;
|
|
1544
|
+
if (res?.reels) return Object.values(res.reels);
|
|
1446
1545
|
return [];
|
|
1447
|
-
} catch {
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1548
|
+
if (isExplicitAuthError(msg)) {
|
|
1549
|
+
throw new Error("Instagram Stories require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
1550
|
+
}
|
|
1551
|
+
logExtractorDebug("Reels media legacy REST", err);
|
|
1448
1552
|
return [];
|
|
1449
1553
|
}
|
|
1450
1554
|
}
|
|
@@ -1570,16 +1674,23 @@ var InstagramExtractor = class {
|
|
|
1570
1674
|
for (const edge of connection.edges) {
|
|
1571
1675
|
const node = edge?.node;
|
|
1572
1676
|
if (node?.owner_id) {
|
|
1573
|
-
|
|
1574
|
-
|
|
1677
|
+
if (typeof node.owner_id === "object" && node.owner_id !== null) {
|
|
1678
|
+
resolvedUserId = String(node.owner_id.pk || node.owner_id.id || "");
|
|
1679
|
+
} else {
|
|
1680
|
+
resolvedUserId = String(node.owner_id);
|
|
1681
|
+
}
|
|
1682
|
+
if (resolvedUserId && resolvedUserId !== "[object Object]") break;
|
|
1575
1683
|
}
|
|
1576
1684
|
if (node?.user?.pk || node?.user?.id) {
|
|
1577
1685
|
resolvedUserId = String(node.user.pk || node.user.id);
|
|
1578
|
-
break;
|
|
1686
|
+
if (resolvedUserId && resolvedUserId !== "[object Object]") break;
|
|
1579
1687
|
}
|
|
1580
1688
|
if (typeof node?.id === "string" && node.id.includes("_")) {
|
|
1581
|
-
|
|
1582
|
-
|
|
1689
|
+
const candidate = node.id.split("_")[1];
|
|
1690
|
+
if (candidate && candidate !== "[object Object]") {
|
|
1691
|
+
resolvedUserId = candidate;
|
|
1692
|
+
break;
|
|
1693
|
+
}
|
|
1583
1694
|
}
|
|
1584
1695
|
}
|
|
1585
1696
|
return {
|
|
@@ -1739,10 +1850,10 @@ var InstagramExtractor = class {
|
|
|
1739
1850
|
logExtractorDebug("fetchWebTokens", err);
|
|
1740
1851
|
}
|
|
1741
1852
|
let userId = user?.id || webTokens.userId;
|
|
1742
|
-
if (!userId && (subDir === "Stories" || subDir === "Highlights")) {
|
|
1853
|
+
if ((!userId || userId === "[object Object]") && (subDir === "Stories" || subDir === "Highlights")) {
|
|
1743
1854
|
try {
|
|
1744
1855
|
const probe = await this.fetchPostsTabGraphQL(username, void 0, webTokens, options);
|
|
1745
|
-
if (probe.userId) {
|
|
1856
|
+
if (probe.userId && probe.userId !== "[object Object]") {
|
|
1746
1857
|
userId = probe.userId;
|
|
1747
1858
|
}
|
|
1748
1859
|
} catch (err) {
|
|
@@ -1750,44 +1861,81 @@ var InstagramExtractor = class {
|
|
|
1750
1861
|
}
|
|
1751
1862
|
}
|
|
1752
1863
|
if (subDir === "Stories") {
|
|
1753
|
-
if (!userId)
|
|
1864
|
+
if (!userId || userId === "[object Object]") {
|
|
1865
|
+
throw new Error(`Could not resolve user ID for @${username}. Profile may be private or unavailable.`);
|
|
1866
|
+
}
|
|
1867
|
+
if (user?.latest_reel_media === 0) {
|
|
1868
|
+
options.onProgress?.({
|
|
1869
|
+
stage: "Stories",
|
|
1870
|
+
found: 0,
|
|
1871
|
+
message: `Profile @${username} has no active stories in the last 24 hours.`
|
|
1872
|
+
});
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1754
1875
|
options.onProgress?.({
|
|
1755
1876
|
stage: "Stories",
|
|
1756
1877
|
found: 0,
|
|
1757
1878
|
message: `Fetching stories for @${username}...`
|
|
1758
1879
|
});
|
|
1759
|
-
const reels = await this.fetchReelsMedia([userId], options);
|
|
1880
|
+
const reels = await this.fetchReelsMedia([userId], options, webTokens);
|
|
1881
|
+
let count = 0;
|
|
1760
1882
|
for (const reel of reels) {
|
|
1761
1883
|
for (const rawItem of reel.items || []) {
|
|
1762
1884
|
const item = parseReelItemToMediaItem(rawItem, username);
|
|
1763
|
-
if (item)
|
|
1885
|
+
if (item) {
|
|
1886
|
+
count++;
|
|
1887
|
+
yield item;
|
|
1888
|
+
}
|
|
1764
1889
|
}
|
|
1765
1890
|
}
|
|
1891
|
+
if (count === 0) {
|
|
1892
|
+
options.onProgress?.({
|
|
1893
|
+
stage: "Stories",
|
|
1894
|
+
found: 0,
|
|
1895
|
+
message: `Profile @${username} has no active stories in the last 24 hours.`
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1766
1898
|
return;
|
|
1767
1899
|
}
|
|
1768
1900
|
if (subDir === "Highlights") {
|
|
1769
|
-
if (!userId)
|
|
1901
|
+
if (!userId || userId === "[object Object]") {
|
|
1902
|
+
throw new Error(`Could not resolve user ID for @${username}. Profile may be private or unavailable.`);
|
|
1903
|
+
}
|
|
1904
|
+
if (user?.highlight_reel_count === 0) {
|
|
1905
|
+
options.onProgress?.({
|
|
1906
|
+
stage: "Highlights",
|
|
1907
|
+
found: 0,
|
|
1908
|
+
message: `Profile @${username} has no available highlights.`
|
|
1909
|
+
});
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1770
1912
|
options.onProgress?.({
|
|
1771
1913
|
stage: "Highlights",
|
|
1772
1914
|
found: 0,
|
|
1773
1915
|
message: `Fetching highlights tray for @${username}...`
|
|
1774
1916
|
});
|
|
1775
|
-
const tray = await this.fetchHighlightsTray(userId, options);
|
|
1917
|
+
const tray = await this.fetchHighlightsTray(userId, options, webTokens);
|
|
1776
1918
|
const highlightIds = tray.map((t) => t.id).filter(Boolean);
|
|
1777
|
-
if (highlightIds.length
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1919
|
+
if (highlightIds.length === 0) {
|
|
1920
|
+
options.onProgress?.({
|
|
1921
|
+
stage: "Highlights",
|
|
1922
|
+
found: 0,
|
|
1923
|
+
message: `Profile @${username} has no available highlights.`
|
|
1924
|
+
});
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
const chunkSize = 5;
|
|
1928
|
+
for (let i = 0; i < highlightIds.length; i += chunkSize) {
|
|
1929
|
+
if (options.signal?.aborted) break;
|
|
1930
|
+
const chunk = highlightIds.slice(i, i + chunkSize);
|
|
1931
|
+
const reels = await this.fetchReelsMedia(chunk, options, webTokens);
|
|
1932
|
+
for (const reel of reels) {
|
|
1933
|
+
for (const rawItem of reel.items || []) {
|
|
1934
|
+
const item = parseReelItemToMediaItem(rawItem, username);
|
|
1935
|
+
if (item) yield item;
|
|
1788
1936
|
}
|
|
1789
|
-
await sleep(1e3, 1500, options.signal);
|
|
1790
1937
|
}
|
|
1938
|
+
await sleep(1e3, 1500, options.signal);
|
|
1791
1939
|
}
|
|
1792
1940
|
return;
|
|
1793
1941
|
}
|
|
@@ -2967,11 +3115,12 @@ async function downloadMediaStream(items, options) {
|
|
|
2967
3115
|
}
|
|
2968
3116
|
return arch;
|
|
2969
3117
|
};
|
|
2970
|
-
|
|
2971
|
-
if (options.destDirMap) {
|
|
3118
|
+
if (options.destDirMap && Object.values(options.destDirMap).some(Boolean)) {
|
|
2972
3119
|
for (const d of Object.values(options.destDirMap)) {
|
|
2973
3120
|
if (d) await getArchiveForDir(d);
|
|
2974
3121
|
}
|
|
3122
|
+
} else {
|
|
3123
|
+
await getArchiveForDir(destDir);
|
|
2975
3124
|
}
|
|
2976
3125
|
let downloaded = 0;
|
|
2977
3126
|
let skipped = 0;
|
|
@@ -3605,7 +3754,7 @@ function buildGalleryDlArgs(options) {
|
|
|
3605
3754
|
}
|
|
3606
3755
|
async function runGalleryDl(options) {
|
|
3607
3756
|
const { profile, targetDir, subDir, cookiePath, onProgress } = options;
|
|
3608
|
-
const destDir = path8.join(targetDir, subDir);
|
|
3757
|
+
const destDir = subDir === "All" ? targetDir : path8.join(targetDir, subDir);
|
|
3609
3758
|
await fs8.mkdir(destDir, { recursive: true });
|
|
3610
3759
|
const args = buildGalleryDlArgs({
|
|
3611
3760
|
destDir,
|
|
@@ -3909,6 +4058,30 @@ function resolveDefaultDownloadDir(platform, username, baseDir) {
|
|
|
3909
4058
|
const root = resolveDownloadBaseDir(baseDir);
|
|
3910
4059
|
return path10.join(root, platform, username);
|
|
3911
4060
|
}
|
|
4061
|
+
async function cleanupEmptyCategoryDirs(targetDir) {
|
|
4062
|
+
const candidateDirs = ["All", "Stories", "Highlights", "Photos", "Videos"];
|
|
4063
|
+
for (const dirName of candidateDirs) {
|
|
4064
|
+
const fullPath = path10.join(targetDir, dirName);
|
|
4065
|
+
try {
|
|
4066
|
+
if (!fsSync.existsSync(fullPath)) continue;
|
|
4067
|
+
const entries = await fs10.readdir(fullPath);
|
|
4068
|
+
const nonArchiveEntries = entries.filter(
|
|
4069
|
+
(e) => e !== ".open-nami-archive.json" && e !== "archive.txt" && e !== "archive_yt-dlp.txt" && e !== "archive_gallery-dl.sqlite3"
|
|
4070
|
+
);
|
|
4071
|
+
if (nonArchiveEntries.length === 0) {
|
|
4072
|
+
for (const entry of entries) {
|
|
4073
|
+
try {
|
|
4074
|
+
await fs10.unlink(path10.join(fullPath, entry));
|
|
4075
|
+
} catch {
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
await fs10.rmdir(fullPath).catch(() => {
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
} catch {
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
3912
4085
|
async function dispatchDownload(options) {
|
|
3913
4086
|
const { profile, choice, onProgress } = options;
|
|
3914
4087
|
const targetDir = options.outputDir || resolveDefaultDownloadDir(profile.platform, profile.username);
|
|
@@ -3941,7 +4114,7 @@ async function dispatchDownload(options) {
|
|
|
3941
4114
|
const savedCheckpoint = options.resume ? await loadCheckpoint(targetDir) : null;
|
|
3942
4115
|
const currentCursors = savedCheckpoint?.cursors ? { ...savedCheckpoint.cursors } : {};
|
|
3943
4116
|
const defaultRunNative = async (subDir) => {
|
|
3944
|
-
const dest = path10.join(targetDir, subDir);
|
|
4117
|
+
const dest = subDir === "All" ? targetDir : path10.join(targetDir, subDir);
|
|
3945
4118
|
const remainingLimit = globalLimit !== void 0 ? Math.max(0, globalLimit - totalDownloaded) : void 0;
|
|
3946
4119
|
if (remainingLimit === 0) {
|
|
3947
4120
|
return { success: true, downloaded: 0, skipped: 0 };
|
|
@@ -4076,7 +4249,8 @@ async function dispatchDownload(options) {
|
|
|
4076
4249
|
} else if (job.type === "photos" || job.type === "stories" || job.type === "highlights") {
|
|
4077
4250
|
const subDir = job.type === "photos" ? "Photos" : job.type === "stories" ? "Stories" : "Highlights";
|
|
4078
4251
|
let res = await runNative(subDir);
|
|
4079
|
-
|
|
4252
|
+
const isAuthError = Boolean(res.error && (res.error.includes("cookie") || res.error.includes("authenticated session") || res.error.includes("Settings (^s)")));
|
|
4253
|
+
if (!isAuthError && (!res.success || res.downloaded === 0 && res.skipped === 0) && !options.signal?.aborted) {
|
|
4080
4254
|
onProgress?.({
|
|
4081
4255
|
job: subDir,
|
|
4082
4256
|
downloadedCount: totalDownloaded,
|
|
@@ -4084,7 +4258,7 @@ async function dispatchDownload(options) {
|
|
|
4084
4258
|
statusText: "switching to gallery-dl engine\u2026"
|
|
4085
4259
|
});
|
|
4086
4260
|
const gdlRes = await runGdl(subDir);
|
|
4087
|
-
if (gdlRes.downloaded > 0 || gdlRes.skipped > 0 || gdlRes.success) {
|
|
4261
|
+
if (gdlRes.downloaded > 0 || gdlRes.skipped > 0 || gdlRes.success && !res.error) {
|
|
4088
4262
|
res = gdlRes;
|
|
4089
4263
|
}
|
|
4090
4264
|
}
|
|
@@ -4123,6 +4297,7 @@ async function dispatchDownload(options) {
|
|
|
4123
4297
|
}
|
|
4124
4298
|
}
|
|
4125
4299
|
}
|
|
4300
|
+
await cleanupEmptyCategoryDirs(targetDir);
|
|
4126
4301
|
if (errors.length === 0 && !options.signal?.aborted) {
|
|
4127
4302
|
await clearCheckpoint(targetDir);
|
|
4128
4303
|
}
|