open-nami 1.2.2 → 1.2.4
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 +363 -153
- 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.4",
|
|
13
13
|
description: "Open Nami: terminal-first bulk social media profile downloader for Instagram, TikTok, Facebook & X.",
|
|
14
14
|
type: "module",
|
|
15
15
|
bin: {
|
|
@@ -287,15 +287,17 @@ function FramedInput({
|
|
|
287
287
|
children
|
|
288
288
|
}) {
|
|
289
289
|
const theme = useTheme();
|
|
290
|
-
const inner = width - 2;
|
|
291
|
-
const
|
|
290
|
+
const inner = Math.max(0, width - 2);
|
|
291
|
+
const maxTitleLen = Math.max(0, inner - 4);
|
|
292
|
+
const displayTitle = title.length > maxTitleLen ? title.slice(0, Math.max(0, maxTitleLen - 1)) + "\u2026" : title;
|
|
293
|
+
const tail = Math.max(0, inner - displayTitle.length - 3);
|
|
292
294
|
const buttonW = button ? frameButtonWidth(button) : 0;
|
|
293
295
|
const fillColor = buttonDim ? theme.gray : theme.primary;
|
|
294
296
|
return /* @__PURE__ */ jsxs2(Box3, { width: width + buttonW, children: [
|
|
295
297
|
/* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", width, children: [
|
|
296
|
-
/* @__PURE__ */ jsxs2(Text3, { children: [
|
|
298
|
+
/* @__PURE__ */ jsxs2(Text3, { wrap: "truncate-end", children: [
|
|
297
299
|
/* @__PURE__ */ jsx4(Text3, { color: theme.gray, dimColor: theme.dimSecondary, children: "\u256D\u2500 " }),
|
|
298
|
-
/* @__PURE__ */ jsx4(Text3, { color: theme.primary, children:
|
|
300
|
+
/* @__PURE__ */ jsx4(Text3, { color: theme.primary, children: displayTitle }),
|
|
299
301
|
/* @__PURE__ */ jsx4(Text3, { color: theme.gray, dimColor: theme.dimSecondary, children: ` ${"\u2500".repeat(tail)}${button ? "\u2500" : "\u256E"}` })
|
|
300
302
|
] }),
|
|
301
303
|
/* @__PURE__ */ jsxs2(Box3, { width, height: 1, overflow: "hidden", children: [
|
|
@@ -661,12 +663,14 @@ import { Box as Box5, Text as Text6 } from "ink";
|
|
|
661
663
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
662
664
|
function Panel({ title, width, children }) {
|
|
663
665
|
const theme = useTheme();
|
|
664
|
-
const inner = width - 2;
|
|
665
|
-
const
|
|
666
|
+
const inner = Math.max(0, width - 2);
|
|
667
|
+
const maxTitleLen = Math.max(0, inner - 4);
|
|
668
|
+
const displayTitle = title.length > maxTitleLen ? title.slice(0, Math.max(0, maxTitleLen - 1)) + "\u2026" : title;
|
|
669
|
+
const tail = Math.max(0, inner - displayTitle.length - 3);
|
|
666
670
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", width, children: [
|
|
667
|
-
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
671
|
+
/* @__PURE__ */ jsxs5(Text6, { wrap: "truncate-end", children: [
|
|
668
672
|
/* @__PURE__ */ jsx7(Text6, { color: theme.gray, dimColor: theme.dimSecondary, children: "\u256D\u2500 " }),
|
|
669
|
-
/* @__PURE__ */ jsx7(Text6, { color: theme.primary, children:
|
|
673
|
+
/* @__PURE__ */ jsx7(Text6, { color: theme.primary, children: displayTitle }),
|
|
670
674
|
/* @__PURE__ */ jsx7(Text6, { color: theme.gray, dimColor: theme.dimSecondary, children: ` ${"\u2500".repeat(tail)}\u256E` })
|
|
671
675
|
] }),
|
|
672
676
|
/* @__PURE__ */ jsx7(
|
|
@@ -1205,7 +1209,8 @@ var IG_QUERIES = {
|
|
|
1205
1209
|
PolarisProfilePostsTabContentQuery_connection: "39229011733413759",
|
|
1206
1210
|
PolarisProfileReelsTabContentQuery_connection: "28989741540630190",
|
|
1207
1211
|
PolarisProfileTaggedTabContentQuery_connection: "28794602806842110",
|
|
1208
|
-
|
|
1212
|
+
ProfileStoryHighlightsTrayContentQuery_connection: "38168009559465141",
|
|
1213
|
+
PolarisProfileStoryHighlightsTrayContentQuery: "38168009559465141",
|
|
1209
1214
|
PolarisStoriesV3ReelPageGalleryQuery: "28262315486766731",
|
|
1210
1215
|
PolarisStoriesV3TrayContainerQuery: "27703822975903310",
|
|
1211
1216
|
PolarisProfilePageContentQuery: "28649589871350527"
|
|
@@ -1392,12 +1397,18 @@ var InstagramExtractor = class {
|
|
|
1392
1397
|
__user: "0",
|
|
1393
1398
|
__a: "1",
|
|
1394
1399
|
__req: "0",
|
|
1400
|
+
__hs: "20731.HYP:instagram_web_pkg.2.1...0",
|
|
1395
1401
|
dpr: "1",
|
|
1402
|
+
__ccg: "EXCELLENT",
|
|
1403
|
+
__rev: "1049285836",
|
|
1404
|
+
__hsi: "7693193318680449885",
|
|
1405
|
+
__comet_req: "7",
|
|
1406
|
+
jazoest: "26461",
|
|
1396
1407
|
fb_api_caller_class: "RelayModern",
|
|
1397
|
-
fb_api_req_friendly_name: "
|
|
1408
|
+
fb_api_req_friendly_name: "ProfileStoryHighlightsTrayContentQuery_connection",
|
|
1398
1409
|
server_timestamps: "true",
|
|
1399
1410
|
variables,
|
|
1400
|
-
doc_id: IG_QUERIES.
|
|
1411
|
+
doc_id: IG_QUERIES.ProfileStoryHighlightsTrayContentQuery_connection
|
|
1401
1412
|
});
|
|
1402
1413
|
if (tokens?.fbDtsg) bodyParams.set("fb_dtsg", tokens.fbDtsg);
|
|
1403
1414
|
if (tokens?.lsd) bodyParams.set("lsd", tokens.lsd);
|
|
@@ -1405,14 +1416,18 @@ var InstagramExtractor = class {
|
|
|
1405
1416
|
"Accept": "*/*",
|
|
1406
1417
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
1407
1418
|
"X-IG-App-ID": IG_APP_ID,
|
|
1408
|
-
"X-
|
|
1419
|
+
"X-IG-Max-Touch-Points": "0",
|
|
1420
|
+
"X-FB-Friendly-Name": "ProfileStoryHighlightsTrayContentQuery_connection",
|
|
1409
1421
|
"Referer": "https://www.instagram.com/",
|
|
1410
1422
|
"Origin": "https://www.instagram.com",
|
|
1411
|
-
"X-ASBD-ID": "359341"
|
|
1423
|
+
"X-ASBD-ID": "359341",
|
|
1424
|
+
"Sec-Fetch-Dest": "empty",
|
|
1425
|
+
"Sec-Fetch-Mode": "cors",
|
|
1426
|
+
"Sec-Fetch-Site": "same-origin"
|
|
1412
1427
|
};
|
|
1413
1428
|
if (tokens?.csrfToken) headers["X-CSRFToken"] = tokens.csrfToken;
|
|
1414
1429
|
if (tokens?.lsd) headers["X-FB-LSD"] = tokens.lsd;
|
|
1415
|
-
const res = await this.httpClient.fetchJson("https://www.instagram.com/graphql
|
|
1430
|
+
const res = await this.httpClient.fetchJson("https://www.instagram.com/api/graphql", {
|
|
1416
1431
|
method: "POST",
|
|
1417
1432
|
body: bodyParams.toString(),
|
|
1418
1433
|
cookiePath: options.cookiePath,
|
|
@@ -1423,7 +1438,7 @@ var InstagramExtractor = class {
|
|
|
1423
1438
|
const edges = res?.data?.highlights?.edges;
|
|
1424
1439
|
if (Array.isArray(edges)) {
|
|
1425
1440
|
if (res?.errors && Array.isArray(res.errors) && res.errors.length > 0) {
|
|
1426
|
-
logExtractorDebug("
|
|
1441
|
+
logExtractorDebug("ProfileStoryHighlightsTrayContentQuery_connection partial field errors", res.errors);
|
|
1427
1442
|
}
|
|
1428
1443
|
return edges.map((e) => e.node).filter(Boolean);
|
|
1429
1444
|
}
|
|
@@ -1434,7 +1449,7 @@ var InstagramExtractor = class {
|
|
|
1434
1449
|
}
|
|
1435
1450
|
}
|
|
1436
1451
|
} catch (err) {
|
|
1437
|
-
logExtractorDebug("
|
|
1452
|
+
logExtractorDebug("ProfileStoryHighlightsTrayContentQuery_connection", err);
|
|
1438
1453
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1439
1454
|
if (isExplicitAuthError(msg)) {
|
|
1440
1455
|
authError = new Error("Instagram Highlights require an active authenticated session. Your session cookie is missing or expired. Update your cookies in Settings (^s).");
|
|
@@ -1481,7 +1496,13 @@ var InstagramExtractor = class {
|
|
|
1481
1496
|
__user: "0",
|
|
1482
1497
|
__a: "1",
|
|
1483
1498
|
__req: "0",
|
|
1499
|
+
__hs: "20731.HYP:instagram_web_pkg.2.1...0",
|
|
1484
1500
|
dpr: "1",
|
|
1501
|
+
__ccg: "EXCELLENT",
|
|
1502
|
+
__rev: "1049285836",
|
|
1503
|
+
__hsi: "7693193318680449885",
|
|
1504
|
+
__comet_req: "7",
|
|
1505
|
+
jazoest: "26461",
|
|
1485
1506
|
fb_api_caller_class: "RelayModern",
|
|
1486
1507
|
fb_api_req_friendly_name: "PolarisStoriesV3ReelPageGalleryQuery",
|
|
1487
1508
|
server_timestamps: "true",
|
|
@@ -1494,14 +1515,18 @@ var InstagramExtractor = class {
|
|
|
1494
1515
|
"Accept": "*/*",
|
|
1495
1516
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
1496
1517
|
"X-IG-App-ID": IG_APP_ID,
|
|
1518
|
+
"X-IG-Max-Touch-Points": "0",
|
|
1497
1519
|
"X-FB-Friendly-Name": "PolarisStoriesV3ReelPageGalleryQuery",
|
|
1498
1520
|
"Referer": "https://www.instagram.com/",
|
|
1499
1521
|
"Origin": "https://www.instagram.com",
|
|
1500
|
-
"X-ASBD-ID": "359341"
|
|
1522
|
+
"X-ASBD-ID": "359341",
|
|
1523
|
+
"Sec-Fetch-Dest": "empty",
|
|
1524
|
+
"Sec-Fetch-Mode": "cors",
|
|
1525
|
+
"Sec-Fetch-Site": "same-origin"
|
|
1501
1526
|
};
|
|
1502
1527
|
if (tokens?.csrfToken) headers["X-CSRFToken"] = tokens.csrfToken;
|
|
1503
1528
|
if (tokens?.lsd) headers["X-FB-LSD"] = tokens.lsd;
|
|
1504
|
-
const res = await this.httpClient.fetchJson("https://www.instagram.com/graphql
|
|
1529
|
+
const res = await this.httpClient.fetchJson("https://www.instagram.com/api/graphql", {
|
|
1505
1530
|
method: "POST",
|
|
1506
1531
|
body: bodyParams.toString(),
|
|
1507
1532
|
cookiePath: options.cookiePath,
|
|
@@ -1864,14 +1889,6 @@ var InstagramExtractor = class {
|
|
|
1864
1889
|
if (!userId || userId === "[object Object]") {
|
|
1865
1890
|
throw new Error(`Could not resolve user ID for @${username}. Profile may be private or unavailable.`);
|
|
1866
1891
|
}
|
|
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
|
-
}
|
|
1875
1892
|
options.onProgress?.({
|
|
1876
1893
|
stage: "Stories",
|
|
1877
1894
|
found: 0,
|
|
@@ -1901,14 +1918,6 @@ var InstagramExtractor = class {
|
|
|
1901
1918
|
if (!userId || userId === "[object Object]") {
|
|
1902
1919
|
throw new Error(`Could not resolve user ID for @${username}. Profile may be private or unavailable.`);
|
|
1903
1920
|
}
|
|
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
|
-
}
|
|
1912
1921
|
options.onProgress?.({
|
|
1913
1922
|
stage: "Highlights",
|
|
1914
1923
|
found: 0,
|
|
@@ -3318,6 +3327,15 @@ function parseDateBoundary(input, boundary) {
|
|
|
3318
3327
|
return nowSeconds - offsetSeconds;
|
|
3319
3328
|
}
|
|
3320
3329
|
if (/^\d+$/.test(trimmed)) {
|
|
3330
|
+
if (trimmed.length === 4) {
|
|
3331
|
+
const year = parseInt(trimmed, 10);
|
|
3332
|
+
if (year >= 1990 && year <= 2100) {
|
|
3333
|
+
const date = boundary === "until" ? new Date(Date.UTC(year, 11, 31, 23, 59, 59, 999)) : new Date(Date.UTC(year, 0, 1, 0, 0, 0, 0));
|
|
3334
|
+
if (!isNaN(date.getTime())) {
|
|
3335
|
+
return Math.floor(date.getTime() / 1e3);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3321
3339
|
if (trimmed.length === 8) {
|
|
3322
3340
|
const year = parseInt(trimmed.slice(0, 4), 10);
|
|
3323
3341
|
const month = parseInt(trimmed.slice(4, 6), 10) - 1;
|
|
@@ -3333,6 +3351,14 @@ function parseDateBoundary(input, boundary) {
|
|
|
3333
3351
|
}
|
|
3334
3352
|
return num;
|
|
3335
3353
|
}
|
|
3354
|
+
if (/^\d{4}-\d{2}$/.test(trimmed)) {
|
|
3355
|
+
const year = parseInt(trimmed.slice(0, 4), 10);
|
|
3356
|
+
const month = parseInt(trimmed.slice(5, 7), 10);
|
|
3357
|
+
const date = boundary === "until" ? new Date(Date.UTC(year, month, 0, 23, 59, 59, 999)) : new Date(Date.UTC(year, month - 1, 1, 0, 0, 0, 0));
|
|
3358
|
+
if (!isNaN(date.getTime())) {
|
|
3359
|
+
return Math.floor(date.getTime() / 1e3);
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3336
3362
|
if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
|
3337
3363
|
const timeStr = boundary === "until" ? "T23:59:59.999Z" : "T00:00:00.000Z";
|
|
3338
3364
|
const parsed2 = /* @__PURE__ */ new Date(`${trimmed}${timeStr}`);
|
|
@@ -3346,8 +3372,34 @@ function parseDateBoundary(input, boundary) {
|
|
|
3346
3372
|
}
|
|
3347
3373
|
return void 0;
|
|
3348
3374
|
}
|
|
3375
|
+
function resolveDateFilter(input) {
|
|
3376
|
+
if (!input) return {};
|
|
3377
|
+
const trimmed = input.trim();
|
|
3378
|
+
if (!trimmed || trimmed.toLowerCase() === "all" || trimmed.toLowerCase() === "none") return {};
|
|
3379
|
+
if (trimmed.includes("..") || /\s+-\s+/.test(trimmed)) {
|
|
3380
|
+
const parts = trimmed.split(/\.\.|\s+-\s+/);
|
|
3381
|
+
return {
|
|
3382
|
+
since: parts[0]?.trim(),
|
|
3383
|
+
until: parts[1]?.trim()
|
|
3384
|
+
};
|
|
3385
|
+
}
|
|
3386
|
+
if (/^\d{4}$/.test(trimmed) || /^\d{4}-\d{2}$/.test(trimmed)) {
|
|
3387
|
+
return {
|
|
3388
|
+
since: trimmed,
|
|
3389
|
+
until: trimmed
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
return {
|
|
3393
|
+
since: trimmed,
|
|
3394
|
+
until: void 0
|
|
3395
|
+
};
|
|
3396
|
+
}
|
|
3349
3397
|
function parseLimit(input) {
|
|
3350
3398
|
if (input === void 0 || input === null) return void 0;
|
|
3399
|
+
if (typeof input === "string") {
|
|
3400
|
+
const trimmed = input.trim().toLowerCase();
|
|
3401
|
+
if (!trimmed || trimmed === "all" || trimmed === "none" || trimmed === "0") return void 0;
|
|
3402
|
+
}
|
|
3351
3403
|
const num = typeof input === "number" ? input : parseInt(String(input).trim(), 10);
|
|
3352
3404
|
if (isNaN(num) || num <= 0) return void 0;
|
|
3353
3405
|
return Math.floor(num);
|
|
@@ -4316,7 +4368,7 @@ async function dispatchDownload(options) {
|
|
|
4316
4368
|
}
|
|
4317
4369
|
|
|
4318
4370
|
// src/components/views/picking-view.tsx
|
|
4319
|
-
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4371
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4320
4372
|
function ChoiceIndicator({ isSelected }) {
|
|
4321
4373
|
const theme = useTheme();
|
|
4322
4374
|
return /* @__PURE__ */ jsx8(Box6, { marginRight: 1, children: /* @__PURE__ */ jsx8(Text7, { color: theme.primary, children: isSelected ? "\u276F" : " " }) });
|
|
@@ -4340,6 +4392,8 @@ function getPickingOptions(platform) {
|
|
|
4340
4392
|
});
|
|
4341
4393
|
return items;
|
|
4342
4394
|
}
|
|
4395
|
+
var DATE_PRESETS = ["all", "24h", "7d", "30d", "2024", "2023", "2022", "2021", "custom"];
|
|
4396
|
+
var LIMIT_PRESETS = [void 0, 10, 24, 50, 100, "custom"];
|
|
4343
4397
|
function PickingView({
|
|
4344
4398
|
contentWidth,
|
|
4345
4399
|
profile,
|
|
@@ -4357,14 +4411,17 @@ function PickingView({
|
|
|
4357
4411
|
const items = getPickingOptions(profile.platform);
|
|
4358
4412
|
const [limit, setLimit] = useState4(initialLimit);
|
|
4359
4413
|
const [dateFilter, setDateFilter] = useState4(() => {
|
|
4360
|
-
if (typeof initialSince === "string" && (
|
|
4361
|
-
return initialSince;
|
|
4414
|
+
if (typeof initialSince === "string" && initialSince.trim()) {
|
|
4415
|
+
return initialSince.trim();
|
|
4362
4416
|
}
|
|
4363
4417
|
return "all";
|
|
4364
4418
|
});
|
|
4365
4419
|
const [writeMetadata, setWriteMetadata] = useState4(initialWriteMetadata);
|
|
4366
4420
|
const [resume, setResume] = useState4(initialResume);
|
|
4367
4421
|
const [hasCheckpoint, setHasCheckpoint] = useState4(false);
|
|
4422
|
+
const [editingModal, setEditingModal] = useState4(null);
|
|
4423
|
+
const [customLimitText, setCustomLimitText] = useState4("");
|
|
4424
|
+
const [customDateText, setCustomDateText] = useState4("");
|
|
4368
4425
|
const targetDir = resolveDefaultDownloadDir(profile.platform, profile.username, outputDir);
|
|
4369
4426
|
useEffect4(() => {
|
|
4370
4427
|
let isMounted = true;
|
|
@@ -4380,29 +4437,40 @@ function PickingView({
|
|
|
4380
4437
|
};
|
|
4381
4438
|
}, [targetDir]);
|
|
4382
4439
|
const cycleLimit = useCallback(() => {
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
}
|
|
4390
|
-
|
|
4440
|
+
const curIdx = LIMIT_PRESETS.findIndex((p) => p === limit);
|
|
4441
|
+
const nextIdx = (curIdx + 1) % LIMIT_PRESETS.length;
|
|
4442
|
+
const nextVal = LIMIT_PRESETS[nextIdx];
|
|
4443
|
+
if (nextVal === "custom") {
|
|
4444
|
+
setCustomLimitText(limit ? String(limit) : "24");
|
|
4445
|
+
setEditingModal("limit");
|
|
4446
|
+
} else {
|
|
4447
|
+
setLimit(nextVal);
|
|
4448
|
+
}
|
|
4449
|
+
}, [limit]);
|
|
4391
4450
|
const cycleDate = useCallback(() => {
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4451
|
+
const curIdx = DATE_PRESETS.indexOf(dateFilter);
|
|
4452
|
+
const nextIdx = (curIdx + 1) % DATE_PRESETS.length;
|
|
4453
|
+
const nextVal = DATE_PRESETS[nextIdx];
|
|
4454
|
+
if (nextVal === "custom") {
|
|
4455
|
+
setCustomDateText(dateFilter !== "all" ? dateFilter : "2021");
|
|
4456
|
+
setEditingModal("date");
|
|
4457
|
+
} else {
|
|
4458
|
+
setDateFilter(nextVal);
|
|
4459
|
+
}
|
|
4460
|
+
}, [dateFilter]);
|
|
4399
4461
|
const toggleMetadata = useCallback(() => {
|
|
4400
4462
|
setWriteMetadata((prev) => !prev);
|
|
4401
4463
|
}, []);
|
|
4402
4464
|
const toggleResume = useCallback(() => {
|
|
4403
4465
|
setResume((prev) => !prev);
|
|
4404
4466
|
}, []);
|
|
4405
|
-
useInput2((input) => {
|
|
4467
|
+
useInput2((input, key) => {
|
|
4468
|
+
if (editingModal) {
|
|
4469
|
+
if (key.escape) {
|
|
4470
|
+
setEditingModal(null);
|
|
4471
|
+
}
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
4406
4474
|
if (input === "l" || input === "L") {
|
|
4407
4475
|
cycleLimit();
|
|
4408
4476
|
return;
|
|
@@ -4421,10 +4489,13 @@ function PickingView({
|
|
|
4421
4489
|
}
|
|
4422
4490
|
}, { isActive: Boolean(process.stdin.isTTY) });
|
|
4423
4491
|
const handleItemSelect = (selectedChoice) => {
|
|
4492
|
+
if (editingModal) return;
|
|
4493
|
+
const dateRange = resolveDateFilter(dateFilter);
|
|
4424
4494
|
onSelect({
|
|
4425
4495
|
choice: selectedChoice,
|
|
4426
4496
|
limit,
|
|
4427
|
-
since:
|
|
4497
|
+
since: dateRange.since,
|
|
4498
|
+
until: dateRange.until,
|
|
4428
4499
|
writeMetadata,
|
|
4429
4500
|
resume
|
|
4430
4501
|
});
|
|
@@ -4442,12 +4513,11 @@ function PickingView({
|
|
|
4442
4513
|
profile.cleanUrl ? ` \xB7 ${profile.cleanUrl}` : ""
|
|
4443
4514
|
] }),
|
|
4444
4515
|
/* @__PURE__ */ jsx8(Gap2, {}),
|
|
4445
|
-
/* @__PURE__ */ jsx8(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: "
|
|
4446
|
-
/* @__PURE__ */ jsx8(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: "Use hotkeys [l], [d], [m], [r] to adjust options." }),
|
|
4516
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: "Select media type. Hotkeys [l], [d], [m], [r] adjust options." }),
|
|
4447
4517
|
hasCheckpoint ? /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { color: theme.primary, bold: true, children: "\u26A1 Checkpoint found (cursor saved)" }) }) : null
|
|
4448
4518
|
] }),
|
|
4449
4519
|
/* @__PURE__ */ jsxs6(Panel, { title: "Download", width: 38, children: [
|
|
4450
|
-
/* @__PURE__ */ jsx8(
|
|
4520
|
+
!editingModal && /* @__PURE__ */ jsx8(
|
|
4451
4521
|
SelectInput,
|
|
4452
4522
|
{
|
|
4453
4523
|
indicatorComponent: ChoiceIndicator,
|
|
@@ -4461,29 +4531,61 @@ function PickingView({
|
|
|
4461
4531
|
onHighlight: (item) => onHighlight?.(item.value)
|
|
4462
4532
|
}
|
|
4463
4533
|
),
|
|
4464
|
-
/* @__PURE__ */
|
|
4534
|
+
/* @__PURE__ */ jsx8(
|
|
4465
4535
|
Box6,
|
|
4466
4536
|
{
|
|
4467
4537
|
flexDirection: "column",
|
|
4468
4538
|
marginTop: 1,
|
|
4469
4539
|
borderStyle: "round",
|
|
4470
|
-
borderColor: theme.gray,
|
|
4471
|
-
borderDimColor:
|
|
4540
|
+
borderColor: editingModal ? theme.primary : theme.gray,
|
|
4541
|
+
borderDimColor: !editingModal,
|
|
4472
4542
|
paddingX: 1,
|
|
4473
|
-
children: [
|
|
4543
|
+
children: editingModal === "limit" ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
4544
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: theme.primary, children: "Set post limit (e.g. 24, all):" }),
|
|
4545
|
+
/* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(
|
|
4546
|
+
TextInput,
|
|
4547
|
+
{
|
|
4548
|
+
value: customLimitText,
|
|
4549
|
+
onChange: setCustomLimitText,
|
|
4550
|
+
onSubmit: (val) => {
|
|
4551
|
+
const parsed = parseLimit(val);
|
|
4552
|
+
setLimit(parsed);
|
|
4553
|
+
setEditingModal(null);
|
|
4554
|
+
},
|
|
4555
|
+
width: 24
|
|
4556
|
+
}
|
|
4557
|
+
) }),
|
|
4558
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: "\u21B5 confirm \xB7 esc cancel" })
|
|
4559
|
+
] }) : editingModal === "date" ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
4560
|
+
/* @__PURE__ */ jsx8(Text7, { bold: true, color: theme.primary, children: "Set date or year (e.g. 2021, 7d):" }),
|
|
4561
|
+
/* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(
|
|
4562
|
+
TextInput,
|
|
4563
|
+
{
|
|
4564
|
+
value: customDateText,
|
|
4565
|
+
onChange: setCustomDateText,
|
|
4566
|
+
onSubmit: (val) => {
|
|
4567
|
+
const trimmed = val.trim();
|
|
4568
|
+
setDateFilter(trimmed ? trimmed : "all");
|
|
4569
|
+
setEditingModal(null);
|
|
4570
|
+
},
|
|
4571
|
+
width: 24
|
|
4572
|
+
}
|
|
4573
|
+
) }),
|
|
4574
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: "\u21B5 confirm \xB7 esc cancel" })
|
|
4575
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
4474
4576
|
/* @__PURE__ */ jsxs6(Box6, { justifyContent: "space-between", children: [
|
|
4475
4577
|
/* @__PURE__ */ jsxs6(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
4476
|
-
"[l]
|
|
4578
|
+
"[l] Posts: ",
|
|
4477
4579
|
/* @__PURE__ */ jsx8(Text7, { color: theme.primary, bold: true, children: limit ? `${limit}` : "ALL" })
|
|
4478
4580
|
] }),
|
|
4479
4581
|
/* @__PURE__ */ jsxs6(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
4480
4582
|
"[d] Date: ",
|
|
4481
|
-
/* @__PURE__ */ jsx8(Text7, { color: theme.primary, bold: true, children: dateFilter
|
|
4583
|
+
/* @__PURE__ */ jsx8(Text7, { color: theme.primary, bold: true, children: dateFilter.toUpperCase() })
|
|
4482
4584
|
] })
|
|
4483
4585
|
] }),
|
|
4484
4586
|
/* @__PURE__ */ jsxs6(Box6, { justifyContent: "space-between", children: [
|
|
4485
4587
|
/* @__PURE__ */ jsxs6(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
4486
|
-
"[m]
|
|
4588
|
+
"[m] Meta: ",
|
|
4487
4589
|
/* @__PURE__ */ jsx8(Text7, { color: theme.primary, bold: true, children: writeMetadata ? "ON" : "OFF" })
|
|
4488
4590
|
] }),
|
|
4489
4591
|
/* @__PURE__ */ jsxs6(Text7, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
@@ -4491,7 +4593,7 @@ function PickingView({
|
|
|
4491
4593
|
/* @__PURE__ */ jsx8(Text7, { color: theme.primary, bold: true, children: resume ? "ON" : "OFF" })
|
|
4492
4594
|
] })
|
|
4493
4595
|
] })
|
|
4494
|
-
]
|
|
4596
|
+
] })
|
|
4495
4597
|
}
|
|
4496
4598
|
)
|
|
4497
4599
|
] })
|
|
@@ -4706,7 +4808,7 @@ function DownloadingView({
|
|
|
4706
4808
|
// src/components/views/completion-view.tsx
|
|
4707
4809
|
import os7 from "os";
|
|
4708
4810
|
import { Box as Box10, Text as Text12 } from "ink";
|
|
4709
|
-
import { Fragment as
|
|
4811
|
+
import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4710
4812
|
var DONE_LABEL = "\u21B5 download another";
|
|
4711
4813
|
function CompletionView({
|
|
4712
4814
|
profile,
|
|
@@ -4718,10 +4820,10 @@ function CompletionView({
|
|
|
4718
4820
|
const plural = result.downloadedCount === 1 ? "" : "s";
|
|
4719
4821
|
const skipText = result.skippedCount > 0 ? ` (${result.skippedCount} archived)` : "";
|
|
4720
4822
|
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", alignItems: "center", children: [
|
|
4721
|
-
/* @__PURE__ */ jsx13(Text12, { children: isZero ? /* @__PURE__ */ jsxs10(
|
|
4823
|
+
/* @__PURE__ */ jsx13(Text12, { children: isZero ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
4722
4824
|
/* @__PURE__ */ jsx13(Text12, { bold: true, color: theme.primary, children: "\u2713 profile scanned " }),
|
|
4723
4825
|
/* @__PURE__ */ jsx13(Text12, { color: theme.primary, children: "\u2014 0 media items found" })
|
|
4724
|
-
] }) : /* @__PURE__ */ jsxs10(
|
|
4826
|
+
] }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
4725
4827
|
/* @__PURE__ */ jsx13(Text12, { bold: true, color: theme.primary, children: "\u2713 profile downloaded! " }),
|
|
4726
4828
|
/* @__PURE__ */ jsx13(Text12, { color: theme.primary, children: "saved to:" })
|
|
4727
4829
|
] }) }),
|
|
@@ -5641,7 +5743,9 @@ async function runBatchQueue(options) {
|
|
|
5641
5743
|
}
|
|
5642
5744
|
|
|
5643
5745
|
// src/components/views/batch-view.tsx
|
|
5644
|
-
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5746
|
+
import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5747
|
+
var DATE_PRESETS2 = ["all", "24h", "7d", "30d", "2024", "2023", "2022", "2021", "custom"];
|
|
5748
|
+
var LIMIT_PRESETS2 = [void 0, 10, 24, 50, 100, "custom"];
|
|
5645
5749
|
function BatchView({
|
|
5646
5750
|
onBack,
|
|
5647
5751
|
outputDir,
|
|
@@ -5657,6 +5761,9 @@ function BatchView({
|
|
|
5657
5761
|
const [rawText, setRawText] = useState8("");
|
|
5658
5762
|
const [filePath, setFilePath] = useState8("");
|
|
5659
5763
|
const [errorMessage, setErrorMessage] = useState8(null);
|
|
5764
|
+
const [focusSection, setFocusSection] = useState8("input");
|
|
5765
|
+
const [customLimitText, setCustomLimitText] = useState8("");
|
|
5766
|
+
const [customDateText, setCustomDateText] = useState8("");
|
|
5660
5767
|
const [choice, setChoice] = useState8(defaultChoice);
|
|
5661
5768
|
const [limit, setLimit] = useState8(defaultLimit);
|
|
5662
5769
|
const [dateFilter, setDateFilter] = useState8("all");
|
|
@@ -5676,22 +5783,27 @@ function BatchView({
|
|
|
5676
5783
|
});
|
|
5677
5784
|
}, []);
|
|
5678
5785
|
const cycleLimit = useCallback4(() => {
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
}
|
|
5686
|
-
|
|
5786
|
+
const curIdx = LIMIT_PRESETS2.findIndex((p) => p === limit);
|
|
5787
|
+
const nextIdx = (curIdx + 1) % LIMIT_PRESETS2.length;
|
|
5788
|
+
const nextVal = LIMIT_PRESETS2[nextIdx];
|
|
5789
|
+
if (nextVal === "custom") {
|
|
5790
|
+
setCustomLimitText(limit ? String(limit) : "24");
|
|
5791
|
+
setFocusSection("custom-limit");
|
|
5792
|
+
} else {
|
|
5793
|
+
setLimit(nextVal);
|
|
5794
|
+
}
|
|
5795
|
+
}, [limit]);
|
|
5687
5796
|
const cycleDate = useCallback4(() => {
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5797
|
+
const curIdx = DATE_PRESETS2.indexOf(dateFilter);
|
|
5798
|
+
const nextIdx = (curIdx + 1) % DATE_PRESETS2.length;
|
|
5799
|
+
const nextVal = DATE_PRESETS2[nextIdx];
|
|
5800
|
+
if (nextVal === "custom") {
|
|
5801
|
+
setCustomDateText(dateFilter !== "all" ? dateFilter : "2021");
|
|
5802
|
+
setFocusSection("custom-date");
|
|
5803
|
+
} else {
|
|
5804
|
+
setDateFilter(nextVal);
|
|
5805
|
+
}
|
|
5806
|
+
}, [dateFilter]);
|
|
5695
5807
|
const toggleMetadata = useCallback4(() => {
|
|
5696
5808
|
setWriteMetadata((prev) => !prev);
|
|
5697
5809
|
}, []);
|
|
@@ -5722,12 +5834,14 @@ function BatchView({
|
|
|
5722
5834
|
abortControllerRef.current = controller;
|
|
5723
5835
|
setPhase("running");
|
|
5724
5836
|
try {
|
|
5837
|
+
const dateRange = resolveDateFilter(dateFilter);
|
|
5725
5838
|
const queueRes = await runBatchQueue({
|
|
5726
5839
|
filePath: inputField === "file" ? filePath.trim() : void 0,
|
|
5727
5840
|
lines: inputField === "text" ? lines : void 0,
|
|
5728
5841
|
choice,
|
|
5729
5842
|
outputDir,
|
|
5730
|
-
since:
|
|
5843
|
+
since: dateRange.since,
|
|
5844
|
+
until: dateRange.until,
|
|
5731
5845
|
limit,
|
|
5732
5846
|
resume,
|
|
5733
5847
|
writeMetadata,
|
|
@@ -5743,7 +5857,7 @@ function BatchView({
|
|
|
5743
5857
|
return next;
|
|
5744
5858
|
});
|
|
5745
5859
|
},
|
|
5746
|
-
onAccountProgress: (
|
|
5860
|
+
onAccountProgress: (_item, progress) => {
|
|
5747
5861
|
setCurrentProgressText(progress.statusText);
|
|
5748
5862
|
},
|
|
5749
5863
|
onAccountComplete: (item, idx) => {
|
|
@@ -5780,6 +5894,14 @@ function BatchView({
|
|
|
5780
5894
|
}, [inputField, filePath, rawText, choice, outputDir, dateFilter, limit, resume, writeMetadata]);
|
|
5781
5895
|
useInput5((input, key) => {
|
|
5782
5896
|
if (key.escape) {
|
|
5897
|
+
if (focusSection === "custom-limit" || focusSection === "custom-date") {
|
|
5898
|
+
setFocusSection("options");
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
if (focusSection === "options") {
|
|
5902
|
+
setFocusSection("input");
|
|
5903
|
+
return;
|
|
5904
|
+
}
|
|
5783
5905
|
if (phase === "running") {
|
|
5784
5906
|
abortControllerRef.current?.abort();
|
|
5785
5907
|
setPhase("input");
|
|
@@ -5801,25 +5923,67 @@ function BatchView({
|
|
|
5801
5923
|
return;
|
|
5802
5924
|
}
|
|
5803
5925
|
if (phase === "input") {
|
|
5804
|
-
if (
|
|
5805
|
-
cycleChoice();
|
|
5806
|
-
return;
|
|
5807
|
-
}
|
|
5808
|
-
if (input === "l" || input === "L") {
|
|
5809
|
-
cycleLimit();
|
|
5926
|
+
if (focusSection === "custom-limit" || focusSection === "custom-date") {
|
|
5810
5927
|
return;
|
|
5811
5928
|
}
|
|
5812
|
-
if (
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5929
|
+
if (key.ctrl) {
|
|
5930
|
+
if (input === "l" || input === "L") {
|
|
5931
|
+
setCustomLimitText(limit ? String(limit) : "24");
|
|
5932
|
+
setFocusSection("custom-limit");
|
|
5933
|
+
return;
|
|
5934
|
+
}
|
|
5935
|
+
if (input === "d" || input === "D") {
|
|
5936
|
+
setCustomDateText(dateFilter !== "all" ? dateFilter : "2021");
|
|
5937
|
+
setFocusSection("custom-date");
|
|
5938
|
+
return;
|
|
5939
|
+
}
|
|
5940
|
+
if (input === "c" || input === "C") {
|
|
5941
|
+
cycleChoice();
|
|
5942
|
+
return;
|
|
5943
|
+
}
|
|
5944
|
+
if (input === "m" || input === "M") {
|
|
5945
|
+
toggleMetadata();
|
|
5946
|
+
return;
|
|
5947
|
+
}
|
|
5948
|
+
if (input === "r" || input === "R") {
|
|
5949
|
+
toggleResume();
|
|
5950
|
+
return;
|
|
5951
|
+
}
|
|
5819
5952
|
}
|
|
5820
|
-
if (
|
|
5821
|
-
|
|
5822
|
-
|
|
5953
|
+
if (focusSection === "input") {
|
|
5954
|
+
if (key.downArrow) {
|
|
5955
|
+
setFocusSection("options");
|
|
5956
|
+
return;
|
|
5957
|
+
}
|
|
5958
|
+
} else if (focusSection === "options") {
|
|
5959
|
+
if (key.upArrow) {
|
|
5960
|
+
setFocusSection("input");
|
|
5961
|
+
return;
|
|
5962
|
+
}
|
|
5963
|
+
if (key.return) {
|
|
5964
|
+
startQueue();
|
|
5965
|
+
return;
|
|
5966
|
+
}
|
|
5967
|
+
if (input === "c" || input === "C") {
|
|
5968
|
+
cycleChoice();
|
|
5969
|
+
return;
|
|
5970
|
+
}
|
|
5971
|
+
if (input === "l" || input === "L") {
|
|
5972
|
+
cycleLimit();
|
|
5973
|
+
return;
|
|
5974
|
+
}
|
|
5975
|
+
if (input === "d" || input === "D") {
|
|
5976
|
+
cycleDate();
|
|
5977
|
+
return;
|
|
5978
|
+
}
|
|
5979
|
+
if (input === "m" || input === "M") {
|
|
5980
|
+
toggleMetadata();
|
|
5981
|
+
return;
|
|
5982
|
+
}
|
|
5983
|
+
if (input === "r" || input === "R") {
|
|
5984
|
+
toggleResume();
|
|
5985
|
+
return;
|
|
5986
|
+
}
|
|
5823
5987
|
}
|
|
5824
5988
|
}
|
|
5825
5989
|
}, { isActive: Boolean(process.stdin.isTTY) });
|
|
@@ -5828,70 +5992,114 @@ function BatchView({
|
|
|
5828
5992
|
const totalCount = queueItems.length > 0 ? queueItems.length : 1;
|
|
5829
5993
|
const percent = Math.min(100, Math.floor(completedCount / totalCount * 100));
|
|
5830
5994
|
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", alignItems: "center", children: [
|
|
5831
|
-
phase === "input" && /* @__PURE__ */ jsx17(Panel, { title: "Batch
|
|
5832
|
-
/* @__PURE__ */
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
/* @__PURE__ */ jsx17(Text16, { bold: true, color: inputField === "
|
|
5836
|
-
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: " (Tab to switch) " }),
|
|
5837
|
-
/* @__PURE__ */ jsx17(Text16, { bold: true, color: inputField === "file" ? theme.primary : theme.gray, children: inputField === "file" ? "\u25CF Load Batch File (.txt)" : "\u25CB Load Batch File (.txt)" })
|
|
5995
|
+
phase === "input" && /* @__PURE__ */ jsx17(Panel, { title: "Batch Queue", width: Math.max(60, boxWidth), children: /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingY: 1, children: [
|
|
5996
|
+
/* @__PURE__ */ jsxs14(Box14, { marginBottom: 1, justifyContent: "center", children: [
|
|
5997
|
+
/* @__PURE__ */ jsx17(Text16, { bold: true, color: inputField === "text" ? theme.primary : theme.gray, children: inputField === "text" ? "\u25CF Links / Handles" : "\u25CB Links / Handles" }),
|
|
5998
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: " \xB7 " }),
|
|
5999
|
+
/* @__PURE__ */ jsx17(Text16, { bold: true, color: inputField === "file" ? theme.primary : theme.gray, children: inputField === "file" ? "\u25CF File (.txt)" : "\u25CB File (.txt)" })
|
|
5838
6000
|
] }),
|
|
5839
|
-
inputField === "text" ? /* @__PURE__ */ jsx17(FramedInput, { title: "
|
|
6001
|
+
inputField === "text" ? /* @__PURE__ */ jsx17(FramedInput, { title: "Links or @handles", width: Math.max(56, boxWidth - 4), children: /* @__PURE__ */ jsx17(
|
|
5840
6002
|
TextInput,
|
|
5841
6003
|
{
|
|
5842
6004
|
value: rawText,
|
|
5843
6005
|
onChange: setRawText,
|
|
5844
6006
|
onSubmit: startQueue,
|
|
5845
|
-
placeholder: "
|
|
5846
|
-
width: Math.max(
|
|
6007
|
+
placeholder: "@user1, @user2, tiktok.com/@user3\u2026",
|
|
6008
|
+
width: Math.max(50, boxWidth - 10),
|
|
6009
|
+
focus: focusSection === "input",
|
|
5847
6010
|
onTab: () => setInputField("file"),
|
|
5848
|
-
onArrowDown: () =>
|
|
6011
|
+
onArrowDown: () => setFocusSection("options")
|
|
5849
6012
|
}
|
|
5850
|
-
) }) : /* @__PURE__ */ jsx17(FramedInput, { title: "
|
|
6013
|
+
) }) : /* @__PURE__ */ jsx17(FramedInput, { title: "Batch file (.txt)", width: Math.max(56, boxWidth - 4), children: /* @__PURE__ */ jsx17(
|
|
5851
6014
|
TextInput,
|
|
5852
6015
|
{
|
|
5853
6016
|
value: filePath,
|
|
5854
6017
|
onChange: setFilePath,
|
|
5855
6018
|
onSubmit: startQueue,
|
|
5856
|
-
placeholder: "e.g.
|
|
5857
|
-
width: Math.max(
|
|
6019
|
+
placeholder: "e.g. creators.txt or C:\\path\\creators.txt",
|
|
6020
|
+
width: Math.max(50, boxWidth - 10),
|
|
6021
|
+
focus: focusSection === "input",
|
|
5858
6022
|
onTab: () => setInputField("text"),
|
|
5859
|
-
|
|
6023
|
+
onArrowDown: () => setFocusSection("options")
|
|
5860
6024
|
}
|
|
5861
6025
|
) }),
|
|
5862
6026
|
errorMessage ? /* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text16, { color: "red", children: [
|
|
5863
6027
|
"\u2717 ",
|
|
5864
6028
|
errorMessage
|
|
5865
6029
|
] }) }) : null,
|
|
5866
|
-
/* @__PURE__ */ jsx17(
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
/* @__PURE__ */
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
6030
|
+
/* @__PURE__ */ jsx17(
|
|
6031
|
+
Box14,
|
|
6032
|
+
{
|
|
6033
|
+
flexDirection: "column",
|
|
6034
|
+
marginTop: 1,
|
|
6035
|
+
borderStyle: "round",
|
|
6036
|
+
borderColor: focusSection === "options" || focusSection === "custom-limit" || focusSection === "custom-date" ? theme.primary : theme.gray,
|
|
6037
|
+
borderDimColor: focusSection === "input",
|
|
6038
|
+
paddingX: 1,
|
|
6039
|
+
children: focusSection === "custom-limit" ? /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
6040
|
+
/* @__PURE__ */ jsx17(Text16, { bold: true, color: theme.primary, children: "Set post limit (e.g. 24, all):" }),
|
|
6041
|
+
/* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(
|
|
6042
|
+
TextInput,
|
|
6043
|
+
{
|
|
6044
|
+
value: customLimitText,
|
|
6045
|
+
onChange: setCustomLimitText,
|
|
6046
|
+
onSubmit: (val) => {
|
|
6047
|
+
const parsed = parseLimit(val);
|
|
6048
|
+
setLimit(parsed);
|
|
6049
|
+
setFocusSection("options");
|
|
6050
|
+
},
|
|
6051
|
+
width: 24
|
|
6052
|
+
}
|
|
6053
|
+
) }),
|
|
6054
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: "\u21B5 confirm \xB7 esc cancel" })
|
|
6055
|
+
] }) : focusSection === "custom-date" ? /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
6056
|
+
/* @__PURE__ */ jsx17(Text16, { bold: true, color: theme.primary, children: "Set date or year (e.g. 2021, 2022, 7d):" }),
|
|
6057
|
+
/* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(
|
|
6058
|
+
TextInput,
|
|
6059
|
+
{
|
|
6060
|
+
value: customDateText,
|
|
6061
|
+
onChange: setCustomDateText,
|
|
6062
|
+
onSubmit: (val) => {
|
|
6063
|
+
const trimmed = val.trim();
|
|
6064
|
+
setDateFilter(trimmed ? trimmed : "all");
|
|
6065
|
+
setFocusSection("options");
|
|
6066
|
+
},
|
|
6067
|
+
width: 24
|
|
6068
|
+
}
|
|
6069
|
+
) }),
|
|
6070
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: "\u21B5 confirm \xB7 esc cancel" })
|
|
6071
|
+
] }) : /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
6072
|
+
/* @__PURE__ */ jsxs14(Box14, { justifyContent: "space-between", children: [
|
|
6073
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
6074
|
+
"[c] Media: ",
|
|
6075
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.primary, bold: true, children: choice.toUpperCase() })
|
|
6076
|
+
] }),
|
|
6077
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
6078
|
+
"[l] Posts: ",
|
|
6079
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.primary, bold: true, children: limit ? `${limit}` : "ALL" })
|
|
6080
|
+
] }),
|
|
6081
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
6082
|
+
"[d] Date: ",
|
|
6083
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.primary, bold: true, children: dateFilter.toUpperCase() })
|
|
6084
|
+
] })
|
|
6085
|
+
] }),
|
|
6086
|
+
/* @__PURE__ */ jsxs14(Box14, { justifyContent: "space-between", marginTop: 1, children: [
|
|
6087
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
6088
|
+
"[m] Meta: ",
|
|
6089
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.primary, bold: true, children: writeMetadata ? "ON" : "OFF" })
|
|
6090
|
+
] }),
|
|
6091
|
+
/* @__PURE__ */ jsxs14(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
6092
|
+
"[r] Resume: ",
|
|
6093
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.primary, bold: true, children: resume ? "ON" : "OFF" })
|
|
6094
|
+
] }),
|
|
6095
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.primary, bold: true, children: "[\u21B5] Start Queue" })
|
|
6096
|
+
] })
|
|
6097
|
+
] })
|
|
6098
|
+
}
|
|
6099
|
+
),
|
|
6100
|
+
/* @__PURE__ */ jsx17(Box14, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: focusSection === "input" ? "Press \u2193 to adjust options \xB7 \u21B5 to start queue" : "Hotkeys [c] [l] [d] [m] [r] \xB7 \u2191 to edit text \xB7 \u21B5 to start" }) })
|
|
5893
6101
|
] }) }),
|
|
5894
|
-
phase === "running" && /* @__PURE__ */ jsx17(Panel, { title: "Batch Queue Running
|
|
6102
|
+
phase === "running" && /* @__PURE__ */ jsx17(Panel, { title: "Batch Queue Running", width: Math.max(60, boxWidth), children: /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingY: 1, children: [
|
|
5895
6103
|
/* @__PURE__ */ jsxs14(Box14, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
5896
6104
|
/* @__PURE__ */ jsxs14(Text16, { color: theme.primary, bold: true, children: [
|
|
5897
6105
|
"Account ",
|
|
@@ -5902,9 +6110,9 @@ function BatchView({
|
|
|
5902
6110
|
percent,
|
|
5903
6111
|
"%)"
|
|
5904
6112
|
] }),
|
|
5905
|
-
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: isCooldown ? "\u23F3
|
|
6113
|
+
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: isCooldown ? "\u23F3 Cooldown active" : "\u25B6 Downloading" })
|
|
5906
6114
|
] }),
|
|
5907
|
-
/* @__PURE__ */ jsx17(ProgressBar, { percent, width:
|
|
6115
|
+
/* @__PURE__ */ jsx17(ProgressBar, { percent, width: 54 }),
|
|
5908
6116
|
/* @__PURE__ */ jsx17(Gap2, {}),
|
|
5909
6117
|
currentItem ? /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: theme.primary, paddingX: 1, paddingY: 1, children: [
|
|
5910
6118
|
/* @__PURE__ */ jsxs14(Box14, { children: [
|
|
@@ -5926,7 +6134,7 @@ function BatchView({
|
|
|
5926
6134
|
/* @__PURE__ */ jsx17(Gap2, {}),
|
|
5927
6135
|
/* @__PURE__ */ jsx17(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: "[esc] Stop queue and return" })
|
|
5928
6136
|
] }) }),
|
|
5929
|
-
phase === "done" && result && /* @__PURE__ */ jsx17(Panel, { title: "Batch Queue Completed", width: Math.max(
|
|
6137
|
+
phase === "done" && result && /* @__PURE__ */ jsx17(Panel, { title: "Batch Queue Completed", width: Math.max(60, boxWidth), children: /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingY: 1, children: [
|
|
5930
6138
|
/* @__PURE__ */ jsxs14(Text16, { bold: true, color: theme.primary, children: [
|
|
5931
6139
|
"\u2713 Successfully processed ",
|
|
5932
6140
|
result.total,
|
|
@@ -5946,7 +6154,7 @@ function BatchView({
|
|
|
5946
6154
|
result.totalDownloaded
|
|
5947
6155
|
] }),
|
|
5948
6156
|
/* @__PURE__ */ jsxs14(Text16, { color: theme.gray, dimColor: theme.dimSecondary, children: [
|
|
5949
|
-
"\u2022 Total items skipped
|
|
6157
|
+
"\u2022 Total items skipped: ",
|
|
5950
6158
|
result.totalSkipped
|
|
5951
6159
|
] }),
|
|
5952
6160
|
/* @__PURE__ */ jsx17(Gap2, {}),
|
|
@@ -6010,7 +6218,7 @@ function frameRowSpan(row) {
|
|
|
6010
6218
|
}
|
|
6011
6219
|
|
|
6012
6220
|
// src/app.tsx
|
|
6013
|
-
import { Fragment as
|
|
6221
|
+
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
6014
6222
|
var TAGLINE = "grab any profile. paste. download. done.";
|
|
6015
6223
|
var DOWNLOAD_BUTTON = "download";
|
|
6016
6224
|
var Gap = ({ lines = 1 }) => /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", flexShrink: 0, children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx18(Text17, { children: " " }, i)) });
|
|
@@ -6182,6 +6390,7 @@ function InnerApp({
|
|
|
6182
6390
|
const targetChoice = typeof choiceOrOptions === "string" ? choiceOrOptions : choiceOrOptions.choice;
|
|
6183
6391
|
const targetLimit = typeof choiceOrOptions === "string" ? effectiveLimit : choiceOrOptions.limit ?? effectiveLimit;
|
|
6184
6392
|
const targetSince = typeof choiceOrOptions === "string" ? since : choiceOrOptions.since ?? since;
|
|
6393
|
+
const targetUntil = typeof choiceOrOptions === "string" ? until : choiceOrOptions.until ?? until;
|
|
6185
6394
|
const targetResume = typeof choiceOrOptions === "string" ? resume : choiceOrOptions.resume ?? resume;
|
|
6186
6395
|
const targetWriteMetadata = typeof choiceOrOptions === "string" ? effectiveWriteMetadata : choiceOrOptions.writeMetadata ?? effectiveWriteMetadata;
|
|
6187
6396
|
setPhase({ name: "downloading", profile, choice: targetChoice });
|
|
@@ -6192,7 +6401,7 @@ function InnerApp({
|
|
|
6192
6401
|
choice: targetChoice,
|
|
6193
6402
|
outputDir: targetDir,
|
|
6194
6403
|
since: targetSince,
|
|
6195
|
-
until,
|
|
6404
|
+
until: targetUntil,
|
|
6196
6405
|
limit: targetLimit,
|
|
6197
6406
|
resume: targetResume,
|
|
6198
6407
|
writeMetadata: targetWriteMetadata,
|
|
@@ -6478,6 +6687,7 @@ function InnerApp({
|
|
|
6478
6687
|
outputDir,
|
|
6479
6688
|
initialLimit: effectiveLimit,
|
|
6480
6689
|
initialSince: since,
|
|
6690
|
+
initialUntil: until,
|
|
6481
6691
|
initialWriteMetadata: effectiveWriteMetadata,
|
|
6482
6692
|
initialResume: resume,
|
|
6483
6693
|
onSelect: (choiceOrOptions) => handleModeSelect(choiceOrOptions, phase.profile),
|
|
@@ -6554,7 +6764,7 @@ function InnerApp({
|
|
|
6554
6764
|
defaultWriteMetadata: effectiveWriteMetadata
|
|
6555
6765
|
}
|
|
6556
6766
|
),
|
|
6557
|
-
hints.length > 0 ? /* @__PURE__ */ jsxs15(
|
|
6767
|
+
hints.length > 0 ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
6558
6768
|
/* @__PURE__ */ jsx18(Gap, { lines: 2 }),
|
|
6559
6769
|
/* @__PURE__ */ jsx18(
|
|
6560
6770
|
Shortcuts,
|