manhuagui-cli 1.0.0

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.
Files changed (52) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +131 -0
  3. package/README_EN.md +131 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +5 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/lib/chapter.d.ts +6 -0
  9. package/dist/lib/chapter.d.ts.map +1 -0
  10. package/dist/lib/chapter.js +142 -0
  11. package/dist/lib/chapter.js.map +1 -0
  12. package/dist/lib/cli.d.ts +30 -0
  13. package/dist/lib/cli.d.ts.map +1 -0
  14. package/dist/lib/cli.js +272 -0
  15. package/dist/lib/cli.js.map +1 -0
  16. package/dist/lib/comic.d.ts +6 -0
  17. package/dist/lib/comic.d.ts.map +1 -0
  18. package/dist/lib/comic.js +61 -0
  19. package/dist/lib/comic.js.map +1 -0
  20. package/dist/lib/config.d.ts +21 -0
  21. package/dist/lib/config.d.ts.map +1 -0
  22. package/dist/lib/config.js +42 -0
  23. package/dist/lib/config.js.map +1 -0
  24. package/dist/lib/download.d.ts +2 -0
  25. package/dist/lib/download.d.ts.map +1 -0
  26. package/dist/lib/download.js +14 -0
  27. package/dist/lib/download.js.map +1 -0
  28. package/dist/lib/errors.d.ts +4 -0
  29. package/dist/lib/errors.d.ts.map +1 -0
  30. package/dist/lib/errors.js +7 -0
  31. package/dist/lib/errors.js.map +1 -0
  32. package/dist/lib/logger.d.ts +7 -0
  33. package/dist/lib/logger.d.ts.map +1 -0
  34. package/dist/lib/logger.js +17 -0
  35. package/dist/lib/logger.js.map +1 -0
  36. package/dist/lib/progress.d.ts +22 -0
  37. package/dist/lib/progress.d.ts.map +1 -0
  38. package/dist/lib/progress.js +51 -0
  39. package/dist/lib/progress.js.map +1 -0
  40. package/dist/lib/prompts.d.ts +6 -0
  41. package/dist/lib/prompts.d.ts.map +1 -0
  42. package/dist/lib/prompts.js +56 -0
  43. package/dist/lib/prompts.js.map +1 -0
  44. package/dist/lib/types.d.ts +15 -0
  45. package/dist/lib/types.d.ts.map +1 -0
  46. package/dist/lib/types.js +2 -0
  47. package/dist/lib/types.js.map +1 -0
  48. package/dist/lib/utils.d.ts +8 -0
  49. package/dist/lib/utils.d.ts.map +1 -0
  50. package/dist/lib/utils.js +31 -0
  51. package/dist/lib/utils.js.map +1 -0
  52. package/package.json +85 -0
@@ -0,0 +1,272 @@
1
+ import { join } from "node:path";
2
+ import { intro, isCancel, outro, spinner } from "@clack/prompts";
3
+ import { defineCommand } from "citty";
4
+ import { Listr } from "listr2";
5
+ import { chromium } from "playwright";
6
+ import { extractChapterImages } from "./chapter.js";
7
+ import { parseComicPage } from "./comic.js";
8
+ import { CHAPTER_DELAY_MAX, CHAPTER_DELAY_MIN, OUTPUT_BASE, pickUserAgent, VIEWPORT_MAX_HEIGHT, VIEWPORT_MAX_WIDTH, VIEWPORT_MIN_HEIGHT, VIEWPORT_MIN_WIDTH, } from "./config.js";
9
+ import { CancelledError } from "./errors.js";
10
+ import { logger } from "./logger.js";
11
+ import { chapterKey, createProgress, filterPending, loadProgress, markChapter, saveProgress, } from "./progress.js";
12
+ import { promptConfirm, promptResume, promptSections, promptUrl } from "./prompts.js";
13
+ import { atomicSaveJSON, humanDelay, randInt, slugify } from "./utils.js";
14
+ async function launchBrowser() {
15
+ return chromium.launch({ headless: true });
16
+ }
17
+ async function parseComicWithSpinner(browser, url) {
18
+ const ctx = await browser.newContext({
19
+ userAgent: pickUserAgent(),
20
+ viewport: {
21
+ width: randInt(VIEWPORT_MIN_WIDTH, VIEWPORT_MAX_WIDTH),
22
+ height: randInt(VIEWPORT_MIN_HEIGHT, VIEWPORT_MAX_HEIGHT),
23
+ },
24
+ });
25
+ const page = await ctx.newPage();
26
+ const s = spinner();
27
+ s.start("Parsing comic page");
28
+ const comic = await parseComicPage(page, url);
29
+ await ctx.close();
30
+ s.stop(comic.title);
31
+ return comic;
32
+ }
33
+ function applyFilters(sections, sectionFilter, chapterFilter) {
34
+ let result = sections;
35
+ if (sectionFilter) {
36
+ result = result.filter((s) => s.name === sectionFilter || s.name.includes(sectionFilter));
37
+ }
38
+ if (chapterFilter) {
39
+ result = result
40
+ .map((s) => ({
41
+ ...s,
42
+ chapters: s.chapters.filter((c) => c.title === chapterFilter || c.title.includes(chapterFilter)),
43
+ }))
44
+ .filter((s) => s.chapters.length > 0);
45
+ }
46
+ return result;
47
+ }
48
+ function reportResults(collected, errors, totalChapters) {
49
+ if (Object.keys(collected).length > 0) {
50
+ logger.info(`Downloaded ${Object.keys(collected).length}/${totalChapters} chapters`);
51
+ }
52
+ if (errors.length > 0) {
53
+ logger.warn(`${errors.length} errors:`);
54
+ for (const e of errors)
55
+ logger.warn(` ! ${e}`);
56
+ }
57
+ logger.info(`Done. Processed ${totalChapters} chapters.`);
58
+ }
59
+ async function processChapter(chapter, sectionName, comicTitle, browser) {
60
+ const dirName = slugify(chapter.title);
61
+ const outputDir = join(OUTPUT_BASE, slugify(comicTitle), slugify(sectionName), dirName);
62
+ const urls = await extractChapterImages(chapter.url, browser, outputDir);
63
+ if (urls.length === 0)
64
+ return null;
65
+ return { title: chapter.title, urls, chapterUrl: chapter.url };
66
+ }
67
+ function createDownloadTasks(sections, comicTitle, comicUrl, browser, resume) {
68
+ const collected = {};
69
+ const errors = [];
70
+ const comicDir = join(OUTPUT_BASE, slugify(comicTitle));
71
+ const progress = resume
72
+ ? (loadProgress(comicDir) ?? createProgress(comicTitle, comicUrl))
73
+ : createProgress(comicTitle, comicUrl);
74
+ saveProgress(comicDir, progress);
75
+ return {
76
+ collected,
77
+ errors,
78
+ tasks: new Listr(sections.map((section) => ({
79
+ title: section.name,
80
+ task: async (_, task) => {
81
+ const total = section.chapters.length;
82
+ const label = section.name;
83
+ for (let i = 0; i < section.chapters.length; i++) {
84
+ const ch = section.chapters[i];
85
+ task.title = `${label} ${i + 1}/${total}`;
86
+ try {
87
+ const r = await processChapter(ch, section.name, comicTitle, browser);
88
+ if (r) {
89
+ collected[r.title] = { urls: r.urls, chapterUrl: r.chapterUrl };
90
+ markChapter(comicDir, progress, chapterKey(section.name, ch.title), "done", {
91
+ pageCount: r.urls.length,
92
+ });
93
+ }
94
+ else {
95
+ markChapter(comicDir, progress, chapterKey(section.name, ch.title), "failed", {
96
+ error: "No images found",
97
+ });
98
+ }
99
+ }
100
+ catch (err) {
101
+ const errMsg = err instanceof Error ? err.message : String(err);
102
+ errors.push(`${ch.title}: ${errMsg}`);
103
+ markChapter(comicDir, progress, chapterKey(section.name, ch.title), "failed", {
104
+ error: errMsg,
105
+ });
106
+ }
107
+ if (i < section.chapters.length - 1) {
108
+ await humanDelay(CHAPTER_DELAY_MIN, CHAPTER_DELAY_MAX);
109
+ }
110
+ }
111
+ task.title = label;
112
+ },
113
+ })), {
114
+ concurrent: false,
115
+ rendererOptions: { collapseSubtasks: false },
116
+ }),
117
+ };
118
+ }
119
+ async function runDirect(url, sectionFilter, chapterFilter, resume, dryRun) {
120
+ const browser = await launchBrowser();
121
+ try {
122
+ const comic = await parseComicWithSpinner(browser, url);
123
+ let sections = applyFilters(comic.sections, sectionFilter, chapterFilter);
124
+ if (sections.length === 0) {
125
+ throw new Error("No chapters found matching filters");
126
+ }
127
+ if (resume) {
128
+ const comicDir = join(OUTPUT_BASE, slugify(comic.title));
129
+ const progress = loadProgress(comicDir);
130
+ if (progress) {
131
+ sections = filterPending(progress, sections);
132
+ if (sections.length === 0) {
133
+ logger.info("All chapters already downloaded.");
134
+ return;
135
+ }
136
+ }
137
+ }
138
+ const totalChapters = sections.reduce((sum, s) => sum + s.chapters.length, 0);
139
+ logger.info(`Sections: ${sections.map((s) => `${s.name}(${s.chapters.length})`).join(", ")}`);
140
+ logger.info(`Total chapters: ${totalChapters}`);
141
+ if (dryRun) {
142
+ for (const section of sections) {
143
+ for (const ch of section.chapters) {
144
+ logger.info(` [${section.name}] ${ch.title}`);
145
+ }
146
+ }
147
+ logger.info("Dry run complete. No files downloaded.");
148
+ return;
149
+ }
150
+ const { collected, errors, tasks: dl, } = createDownloadTasks(sections, comic.title, url, browser, resume);
151
+ await dl.run();
152
+ if (Object.keys(collected).length > 0) {
153
+ atomicSaveJSON(join(OUTPUT_BASE, slugify(comic.title), "urls.json"), collected);
154
+ }
155
+ reportResults(collected, errors, totalChapters);
156
+ }
157
+ finally {
158
+ await browser.close();
159
+ }
160
+ }
161
+ async function runInteractive(resume, dryRun) {
162
+ intro("Manhuagui Scraper");
163
+ const url = await promptUrl();
164
+ const browser = await launchBrowser();
165
+ try {
166
+ const comic = await parseComicWithSpinner(browser, url);
167
+ let shouldResume = resume;
168
+ const comicDir = join(OUTPUT_BASE, slugify(comic.title));
169
+ const progress = loadProgress(comicDir);
170
+ if (progress && !resume) {
171
+ let done = 0;
172
+ let total = 0;
173
+ for (const s of comic.sections) {
174
+ for (const c of s.chapters) {
175
+ total++;
176
+ if (progress.chapters[chapterKey(s.name, c.title)]?.status === "done")
177
+ done++;
178
+ }
179
+ }
180
+ if (total > 0 && done > 0) {
181
+ shouldResume = await promptResume(done, total);
182
+ }
183
+ }
184
+ let selected = await promptSections(comic.sections);
185
+ if (shouldResume && progress) {
186
+ selected = filterPending(progress, selected);
187
+ if (selected.length === 0) {
188
+ outro("All chapters already downloaded.");
189
+ return;
190
+ }
191
+ }
192
+ const totalChapters = selected.reduce((sum, s) => sum + s.chapters.length, 0);
193
+ const confirmed = await promptConfirm(totalChapters);
194
+ if (!confirmed || isCancel(confirmed)) {
195
+ outro("Cancelled.");
196
+ return;
197
+ }
198
+ if (dryRun) {
199
+ for (const section of selected) {
200
+ for (const ch of section.chapters) {
201
+ logger.info(` [${section.name}] ${ch.title}`);
202
+ }
203
+ }
204
+ outro(`Dry run complete. ${totalChapters} chapters would be downloaded.`);
205
+ return;
206
+ }
207
+ const { collected, errors, tasks: dl, } = createDownloadTasks(selected, comic.title, url, browser, shouldResume);
208
+ await dl.run();
209
+ if (Object.keys(collected).length > 0) {
210
+ atomicSaveJSON(join(OUTPUT_BASE, slugify(comic.title), "urls.json"), collected);
211
+ }
212
+ reportResults(collected, errors, totalChapters);
213
+ }
214
+ finally {
215
+ await browser.close();
216
+ }
217
+ }
218
+ export const command = defineCommand({
219
+ meta: {
220
+ name: "manhuagui-cli",
221
+ version: "1.0.0",
222
+ description: "漫画柜 (manhuagui.com) 漫画下载工具 / CLI tool for downloading manhua",
223
+ },
224
+ args: {
225
+ url: {
226
+ type: "positional",
227
+ description: "漫画 URL / Comic URL",
228
+ required: false,
229
+ },
230
+ section: {
231
+ type: "string",
232
+ description: "指定章节组名称 / Section name to download",
233
+ alias: "s",
234
+ },
235
+ chapter: {
236
+ type: "string",
237
+ description: "指定章节名称 / Chapter name to download",
238
+ alias: "c",
239
+ },
240
+ resume: {
241
+ type: "boolean",
242
+ description: "断点续传 / Resume from previous download",
243
+ alias: "r",
244
+ default: false,
245
+ },
246
+ "dry-run": {
247
+ type: "boolean",
248
+ description: "预览模式 / Preview without downloading",
249
+ alias: "d",
250
+ default: false,
251
+ },
252
+ },
253
+ async run({ args }) {
254
+ try {
255
+ if (args.url) {
256
+ await runDirect(args.url, args.section, args.chapter, args.resume, args["dry-run"]);
257
+ }
258
+ else {
259
+ await runInteractive(args.resume, args["dry-run"]);
260
+ }
261
+ }
262
+ catch (err) {
263
+ if (err instanceof CancelledError) {
264
+ outro("Cancelled.");
265
+ return;
266
+ }
267
+ logger.error(err instanceof Error ? err.message : String(err));
268
+ process.exit(1);
269
+ }
270
+ },
271
+ });
272
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/lib/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Page } from "playwright";
2
+ import type { Chapter, ComicInfo } from "./types.js";
3
+ export declare function parseChapters(ulHTML: string, baseUrl: string): Chapter[];
4
+ export declare function parseComicHTML(html: string, baseUrl: string): ComicInfo;
5
+ export declare function parseComicPage(page: Page, url: string): Promise<ComicInfo>;
6
+ //# sourceMappingURL=comic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comic.d.ts","sourceRoot":"","sources":["../../src/lib/comic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAW,MAAM,YAAY,CAAC;AAE9D,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAgBxE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CA6BvE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAkBhF"}
@@ -0,0 +1,61 @@
1
+ import * as cheerio from "cheerio";
2
+ import { ADULT_CLICK_SETTLE_DELAY, ADULT_SELECTOR_TIMEOUT, PAGE_LOAD_TIMEOUT } from "./config.js";
3
+ export function parseChapters(ulHTML, baseUrl) {
4
+ const $ = cheerio.load(ulHTML);
5
+ const chapters = [];
6
+ $("a").each((_, el) => {
7
+ const href = $(el).attr("href") ?? "";
8
+ const title = $(el).attr("title") || "Untitled";
9
+ const spanHTML = $(el).find("span").html() ?? "";
10
+ const pageMatch = spanHTML.match(/<i[^>]*>(\d+)p<\/i>/);
11
+ const pageCount = pageMatch ? parseInt(pageMatch[1], 10) : 0;
12
+ const url = new URL(href, baseUrl).href;
13
+ chapters.push({ title, url, pageCount });
14
+ });
15
+ chapters.reverse();
16
+ return chapters;
17
+ }
18
+ export function parseComicHTML(html, baseUrl) {
19
+ const $ = cheerio.load(html);
20
+ const title = $("h1").first().text().trim() || "unknown";
21
+ const idMatch = baseUrl.match(/\/comic\/(\d+)\//);
22
+ const id = idMatch?.[1] ?? "0";
23
+ const sections = [];
24
+ const chapterDiv = $(".chapter.cf").first();
25
+ if (!chapterDiv.length)
26
+ return { title, id, sections };
27
+ chapterDiv.children("h4").each((_, h4) => {
28
+ const sectionName = $(h4).find("span").text().trim();
29
+ if (!sectionName)
30
+ return;
31
+ let ul = $(h4).next("ul");
32
+ if (!ul.length) {
33
+ ul = $(h4).next(".chapter-list").find("ul").first();
34
+ if (!ul.length)
35
+ return;
36
+ }
37
+ const chapters = parseChapters(ul.html() ?? "", baseUrl);
38
+ if (chapters.length > 0) {
39
+ sections.push({ name: sectionName, chapters });
40
+ }
41
+ });
42
+ return { title, id, sections };
43
+ }
44
+ export async function parseComicPage(page, url) {
45
+ const response = await page.goto(url, {
46
+ waitUntil: "domcontentloaded",
47
+ timeout: PAGE_LOAD_TIMEOUT,
48
+ });
49
+ if (!response?.ok()) {
50
+ throw new Error(`Failed to load comic page: ${response?.status()}`);
51
+ }
52
+ const checkAdult = await page.$("#checkAdult");
53
+ if (checkAdult) {
54
+ await checkAdult.click();
55
+ await page.waitForSelector(".chapter h4", { timeout: ADULT_SELECTOR_TIMEOUT });
56
+ await page.waitForTimeout(ADULT_CLICK_SETTLE_DELAY);
57
+ }
58
+ const html = await page.content();
59
+ return parseComicHTML(html, url);
60
+ }
61
+ //# sourceMappingURL=comic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comic.js","sourceRoot":"","sources":["../../src/lib/comic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export declare const OUTPUT_BASE: string;
2
+ export declare const IMAGE_CONCURRENCY: number;
3
+ export declare const DOWNLOAD_DELAY: number;
4
+ export declare const CHAPTER_DELAY_MIN: number;
5
+ export declare const CHAPTER_DELAY_MAX: number;
6
+ export declare const RETRY_COUNT: number;
7
+ export declare const RETRY_BACKOFF_BASE: number;
8
+ export declare const IMAGE_LOAD_DELAY: number;
9
+ export declare const PAD_MIN_LENGTH = 3;
10
+ export declare const PAGE_LOAD_TIMEOUT = 30000;
11
+ export declare const CHAPTER_SELECTOR_TIMEOUT = 30000;
12
+ export declare const ADULT_SELECTOR_TIMEOUT = 10000;
13
+ export declare const ADULT_CLICK_SETTLE_DELAY = 300;
14
+ export declare const NEXT_PAGE_TIMEOUT = 15000;
15
+ export declare const USER_AGENTS: string[];
16
+ export declare function pickUserAgent(): string;
17
+ export declare const VIEWPORT_MIN_WIDTH = 1200;
18
+ export declare const VIEWPORT_MAX_WIDTH = 1600;
19
+ export declare const VIEWPORT_MIN_HEIGHT = 800;
20
+ export declare const VIEWPORT_MAX_HEIGHT = 1000;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,QAAwC,CAAC;AAEjE,eAAO,MAAM,iBAAiB,QAA6C,CAAC;AAE5E,eAAO,MAAM,cAAc,QAA6C,CAAC;AAEzE,eAAO,MAAM,iBAAiB,QAAgD,CAAC;AAC/E,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAEhF,eAAO,MAAM,WAAW,QAAuC,CAAC;AAChE,eAAO,MAAM,kBAAkB,QAAiD,CAAC;AACjF,eAAO,MAAM,gBAAgB,QAA8C,CAAC;AAC5E,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AACvC,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAC9C,eAAO,MAAM,sBAAsB,QAAQ,CAAC;AAC5C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAGvC,eAAO,MAAM,WAAW,UAWnB,CAAC;AAEN,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,OAAO,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { logger } from "./logger.js";
2
+ try {
3
+ process.loadEnvFile?.();
4
+ }
5
+ catch {
6
+ logger.warn("Failed to load .env file");
7
+ }
8
+ export const OUTPUT_BASE = process.env.OUTPUT_BASE || "./output";
9
+ export const IMAGE_CONCURRENCY = Number(process.env.IMAGE_CONCURRENCY) || 2;
10
+ export const DOWNLOAD_DELAY = Number(process.env.DOWNLOAD_DELAY) || 3000;
11
+ export const CHAPTER_DELAY_MIN = Number(process.env.CHAPTER_DELAY_MIN) || 5000;
12
+ export const CHAPTER_DELAY_MAX = Number(process.env.CHAPTER_DELAY_MAX) || 15000;
13
+ export const RETRY_COUNT = Number(process.env.RETRY_COUNT) || 3;
14
+ export const RETRY_BACKOFF_BASE = Number(process.env.RETRY_BACKOFF_BASE) || 1000;
15
+ export const IMAGE_LOAD_DELAY = Number(process.env.IMAGE_LOAD_DELAY) || 200;
16
+ export const PAD_MIN_LENGTH = 3;
17
+ export const PAGE_LOAD_TIMEOUT = 30000;
18
+ export const CHAPTER_SELECTOR_TIMEOUT = 30000;
19
+ export const ADULT_SELECTOR_TIMEOUT = 10000;
20
+ export const ADULT_CLICK_SETTLE_DELAY = 300;
21
+ export const NEXT_PAGE_TIMEOUT = 15000;
22
+ const _AGENTS_RAW = process.env.USER_AGENTS;
23
+ export const USER_AGENTS = _AGENTS_RAW
24
+ ? _AGENTS_RAW
25
+ .split("\n")
26
+ .map((s) => s.trim())
27
+ .filter(Boolean)
28
+ : [
29
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
30
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
31
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
32
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
33
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
34
+ ];
35
+ export function pickUserAgent() {
36
+ return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
37
+ }
38
+ export const VIEWPORT_MIN_WIDTH = 1200;
39
+ export const VIEWPORT_MAX_WIDTH = 1600;
40
+ export const VIEWPORT_MIN_HEIGHT = 800;
41
+ export const VIEWPORT_MAX_HEIGHT = 1000;
42
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,IAAI,CAAC;IACH,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;AAC1B,CAAC;AAAC,MAAM,CAAC;IACP,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,UAAU,CAAC;AAEjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;AAEzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC;AAC/E,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC;AAEhF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AACvC,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAC9C,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEvC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;AAC5C,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW;IACpC,CAAC,CAAC,WAAW;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC;IACpB,CAAC,CAAC;QACE,iHAAiH;QACjH,iHAAiH;QACjH,uHAAuH;QACvH,uGAAuG;QACvG,kFAAkF;KACnF,CAAC;AAEN,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AACvC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function rotateHost(url: string): string;
2
+ //# sourceMappingURL=download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/lib/download.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAW9C"}
@@ -0,0 +1,14 @@
1
+ const CDN_HOSTS = ["eu", "eu1", "eu2", "us", "us1", "us2", "us3"];
2
+ export function rotateHost(url) {
3
+ const parsed = new URL(url);
4
+ const parts = parsed.hostname.split(".");
5
+ if (parts.length >= 3) {
6
+ const idx = CDN_HOSTS.indexOf(parts[0]);
7
+ if (idx !== -1) {
8
+ parts[0] = CDN_HOSTS[(idx + 1) % CDN_HOSTS.length];
9
+ parsed.hostname = parts.join(".");
10
+ }
11
+ }
12
+ return parsed.href;
13
+ }
14
+ //# sourceMappingURL=download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/lib/download.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAElE,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YACnD,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare class CancelledError extends Error {
2
+ constructor();
3
+ }
4
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;;CAKxC"}
@@ -0,0 +1,7 @@
1
+ export class CancelledError extends Error {
2
+ constructor() {
3
+ super("User cancelled");
4
+ this.name = "CancelledError";
5
+ }
6
+ }
7
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC;QACE,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export declare const logger: {
2
+ debug: (msg: string) => false | void;
3
+ info: (msg: string) => false | void;
4
+ warn: (msg: string) => false | void;
5
+ error: (msg: string) => false | void;
6
+ };
7
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,MAAM;iBACJ,MAAM;gBACP,MAAM;gBACN,MAAM;iBACL,MAAM;CACpB,CAAC"}
@@ -0,0 +1,17 @@
1
+ const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
2
+ function getLevel() {
3
+ const env = process.env.LOG_LEVEL?.toLowerCase();
4
+ if (env && env in LEVELS)
5
+ return env;
6
+ return "info";
7
+ }
8
+ function shouldLog(level) {
9
+ return LEVELS[level] >= LEVELS[getLevel()];
10
+ }
11
+ export const logger = {
12
+ debug: (msg) => shouldLog("debug") && console.error(`[DEBUG] ${msg}`),
13
+ info: (msg) => shouldLog("info") && console.log(msg),
14
+ warn: (msg) => shouldLog("warn") && console.error(`[WARN] ${msg}`),
15
+ error: (msg) => shouldLog("error") && console.error(`[ERROR] ${msg}`),
16
+ };
17
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAElF,SAAS,QAAQ;IACf,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC;IACjD,IAAI,GAAG,IAAI,GAAG,IAAI,MAAM;QAAE,OAAO,GAAe,CAAC;IACjD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,SAAS,CAAC,KAAe;IAChC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC;IAC7E,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;IAC5D,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;IAC1E,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC;CAC9E,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Section } from "./types.js";
2
+ export interface ChapterProgress {
3
+ status: "done" | "failed";
4
+ pageCount?: number;
5
+ error?: string;
6
+ }
7
+ export interface ProgressData {
8
+ comicTitle: string;
9
+ comicUrl: string;
10
+ chapters: Record<string, ChapterProgress>;
11
+ }
12
+ export declare function loadProgress(comicDir: string): ProgressData | null;
13
+ export declare function loadProgressOrWarn(comicDir: string): ProgressData | null;
14
+ export declare function createProgress(comicTitle: string, comicUrl: string): ProgressData;
15
+ export declare function saveProgress(comicDir: string, data: ProgressData): void;
16
+ export declare function markChapter(comicDir: string, progress: ProgressData, key: string, status: "done" | "failed", extra?: {
17
+ pageCount?: number;
18
+ error?: string;
19
+ }): void;
20
+ export declare function chapterKey(sectionName: string, chapterTitle: string): string;
21
+ export declare function filterPending(progress: ProgressData | null, sections: Section[]): Section[];
22
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC3C;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAOlE;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CASxE;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAEjF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAEvE;AAED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,QAAQ,EACzB,KAAK,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,IAAI,CAGN;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAY3F"}
@@ -0,0 +1,51 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { logger } from "./logger.js";
4
+ import { atomicSaveJSON } from "./utils.js";
5
+ export function loadProgress(comicDir) {
6
+ try {
7
+ const raw = readFileSync(join(comicDir, "progress.json"), "utf-8");
8
+ return JSON.parse(raw);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ export function loadProgressOrWarn(comicDir) {
15
+ try {
16
+ const raw = readFileSync(join(comicDir, "progress.json"), "utf-8");
17
+ return JSON.parse(raw);
18
+ }
19
+ catch (err) {
20
+ const message = err instanceof Error ? err.message : String(err);
21
+ logger.warn(`Failed to load progress.json: ${message}`);
22
+ return null;
23
+ }
24
+ }
25
+ export function createProgress(comicTitle, comicUrl) {
26
+ return { comicTitle, comicUrl, chapters: {} };
27
+ }
28
+ export function saveProgress(comicDir, data) {
29
+ atomicSaveJSON(join(comicDir, "progress.json"), data);
30
+ }
31
+ export function markChapter(comicDir, progress, key, status, extra) {
32
+ progress.chapters[key] = { status, ...extra };
33
+ saveProgress(comicDir, progress);
34
+ }
35
+ export function chapterKey(sectionName, chapterTitle) {
36
+ return `${sectionName}::${chapterTitle}`;
37
+ }
38
+ export function filterPending(progress, sections) {
39
+ if (!progress)
40
+ return sections;
41
+ return sections
42
+ .map((s) => ({
43
+ ...s,
44
+ chapters: s.chapters.filter((c) => {
45
+ const p = progress.chapters[chapterKey(s.name, c.title)];
46
+ return p?.status !== "done";
47
+ }),
48
+ }))
49
+ .filter((s) => s.chapters.length > 0);
50
+ }
51
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAc5C,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,iCAAiC,OAAO,EAAE,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,QAAgB;IACjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,IAAkB;IAC/D,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,QAAsB,EACtB,GAAW,EACX,MAAyB,EACzB,KAA8C;IAE9C,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IAC9C,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,YAAoB;IAClE,OAAO,GAAG,WAAW,KAAK,YAAY,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAA6B,EAAE,QAAmB;IAC9E,IAAI,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE/B,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,GAAG,CAAC;QACJ,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9B,CAAC,CAAC;KACH,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Section } from "./types.js";
2
+ export declare function promptUrl(): Promise<string>;
3
+ export declare function promptSections(sections: Section[]): Promise<Section[]>;
4
+ export declare function promptConfirm(count: number): Promise<boolean>;
5
+ export declare function promptResume(done: number, total: number): Promise<boolean>;
6
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/lib/prompts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAgBjD;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAqB5E;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWnE;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWhF"}