@cloud411716/fancy-webnovel 0.2.27 → 0.2.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud411716/fancy-webnovel",
3
- "version": "0.2.27",
3
+ "version": "0.2.28",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -73,8 +73,8 @@ export async function apply(ctx) {
73
73
  : (row[2] === '长篇' ? 'long' : 'short');
74
74
  }
75
75
 
76
- // 知乎/七猫二次选择篇幅
77
- if (platform === 'zhihu' || platform === 'qimao') {
76
+ // 知乎二次选择篇幅(保持原逻辑)
77
+ if (platform === 'zhihu') {
78
78
  if (!length || !['long', 'short'].includes(length)) {
79
79
  const lenResult = await ctx.userQuestions.ask({
80
80
  questions: [scan.askLength(platform)],
@@ -90,9 +90,30 @@ export async function apply(ctx) {
90
90
  length = 'long';
91
91
  }
92
92
 
93
+ // 七猫二次选择(男/女频 × 大热/新书榜)
94
+ if (platform === 'qimao') {
95
+ const lenResult = await ctx.userQuestions.ask({
96
+ questions: [scan.askLength(platform)],
97
+ });
98
+ const lenAnswer = lenResult.answers[0]?.selected?.[0] ?? lenResult.answers[0]?.custom;
99
+ if (!lenAnswer || lenAnswer === '取消') {
100
+ notify(session, scan.notify({ type: 'cancelled' }));
101
+ return { kind: 'success', text: '' };
102
+ }
103
+ // 把选项标签映射成 channel + type,存入 ctx.options 供 runScanWithChromiumFix 传递
104
+ const qimaoMap = {
105
+ '采集全部(男/女 × 大热/新书,4个榜单)': { channel: 'all', type: 'all' },
106
+ '男生大热榜': { channel: 'male', type: 'hot' },
107
+ '男生新书榜': { channel: 'male', type: 'new' },
108
+ '女生大热榜': { channel: 'female', type: 'hot' },
109
+ '女生新书榜': { channel: 'female', type: 'new' },
110
+ };
111
+ ctx._qimaoOverride = qimaoMap[lenAnswer] || { channel: 'all', type: 'all' };
112
+ }
113
+
93
114
  // 执行采集,包含 Chromium 缺失处理
94
115
  const scanResult = await runScanWithChromiumFix(ctx, session, invocation, {
95
- platform, length, projectRoot,
116
+ platform, length, projectRoot, qimaoOverride: ctx._qimaoOverride || null,
96
117
  });
97
118
 
98
119
  if (scanResult === 'retry') {
@@ -112,14 +133,19 @@ export async function apply(ctx) {
112
133
  * 执行采集,检测 Chromium 缺失则提示用户手动安装。
113
134
  * 返回 undefined 表示已处理完毕(取消/失败),返回 'retry' 表示重试完成。
114
135
  */
115
- async function runScanWithChromiumFix(ctx, session, invocation, { platform, length, projectRoot }) {
136
+ async function runScanWithChromiumFix(ctx, session, invocation, { platform, length, projectRoot, qimaoOverride = null }) {
116
137
  const runOnce = async (isRetry = false) => {
117
138
  const stop = startActivity(session);
118
139
  if (isRetry) notify(session, '⏳ 重新采集...\n');
140
+ const extraArgs = [];
141
+ if (platform === 'qimao' && qimaoOverride) {
142
+ extraArgs.push('--channel', qimaoOverride.channel, '--type', qimaoOverride.type);
143
+ }
119
144
  const r = await spawnScript(
120
145
  'node',
121
146
  [join(SCAN_SCRIPTS_DIR, 'run-scan.js'),
122
- '--project-root', projectRoot, '--platform', platform, '--length', length],
147
+ '--project-root', projectRoot, '--platform', platform, '--length', length,
148
+ ...extraArgs],
123
149
  { timeout: 900000, stop }
124
150
  );
125
151
  return r;
@@ -28,7 +28,7 @@ const PLATFORM_SUPPORTED_LENGTHS = {
28
28
  fanqie: ['long'],
29
29
  jinjiang: ['long'],
30
30
  zhihu: ['long', 'short'],
31
- qimao: ['long', 'short'],
31
+ qimao: ['long'],
32
32
  };
33
33
 
34
34
  const PLATFORM_CN = {
@@ -163,10 +163,10 @@ function renderQidianMarkdown(rankLabel, url, books) {
163
163
  // Browser scraper spawner(调用 scripts/scrapers/ 下的 CJS 脚本)
164
164
  // ---------------------------------------------------------------------------
165
165
 
166
- function spawnScraper(platform, length, outDir) {
166
+ function spawnScraper(platform, length, outDir, channel, type) {
167
167
  return new Promise(async (resolve) => {
168
168
  const { spawn } = await import('child_process');
169
- const args = scraperArgs(platform, length, outDir);
169
+ const args = scraperArgs(platform, length, outDir, channel, type);
170
170
  const scraperScript = join(dirname(fileURLToPath(import.meta.url)), 'scrapers', args.script);
171
171
  const fullArgs = [...args.extra];
172
172
 
@@ -191,7 +191,7 @@ function spawnScraper(platform, length, outDir) {
191
191
  });
192
192
  }
193
193
 
194
- function scraperArgs(platform, length, outDir) {
194
+ function scraperArgs(platform, length, outDir, channel, type) {
195
195
  switch (platform) {
196
196
  case 'fanqie':
197
197
  return {
@@ -212,9 +212,8 @@ function scraperArgs(platform, length, outDir) {
212
212
  case 'qimao':
213
213
  return {
214
214
  script: 'qimao-rank-scraper.cjs',
215
- extra: ['--outdir', outDir],
215
+ extra: ['--channel', channel || 'all', '--type', type || 'all', '--outdir', outDir],
216
216
  };
217
- return { script: null, extra: [] };
218
217
  default:
219
218
  return { script: null, extra: [] };
220
219
  }
@@ -316,12 +315,14 @@ function appendTopicDecision(platform, length, reportFile, outDir) {
316
315
 
317
316
  async function main() {
318
317
  const args = process.argv.slice(2);
319
- let projectRoot = '', platform = '', length = '';
318
+ let projectRoot = '', platform = '', length = '', channel = '', type = '';
320
319
 
321
320
  for (let i = 0; i < args.length; i++) {
322
321
  if (args[i] === '--project-root') projectRoot = args[i + 1] || '';
323
322
  if (args[i] === '--platform') platform = args[i + 1] || '';
324
323
  if (args[i] === '--length') length = args[i + 1] || '';
324
+ if (args[i] === '--channel') channel = args[i + 1] || 'all';
325
+ if (args[i] === '--type') type = args[i + 1] || 'all';
325
326
  }
326
327
 
327
328
  if (!projectRoot || !platform || !length) {
@@ -373,7 +374,7 @@ async function main() {
373
374
  process.exit(1);
374
375
  }
375
376
  console.error(`→ 采集 ${PLATFORM_CN[platform]}...`);
376
- const result = await spawnScraper(platform, length, scanDir);
377
+ const result = await spawnScraper(platform, length, scanDir, channel, type);
377
378
  if (!result.ok) {
378
379
  console.error(` ❌ ${PLATFORM_CN[platform]} 采集失败:${result.error}`);
379
380
  process.exit(1);
@@ -37,6 +37,19 @@ export const scan = {
37
37
  },
38
38
 
39
39
  askLength(platform) {
40
+ if (platform === 'qimao') {
41
+ return {
42
+ question: `七猫同时支持男频/女频、大热榜/新书榜,请选择采集范围:`,
43
+ hideCustomInput: true,
44
+ options: [
45
+ { label: '采集全部(男/女 × 大热/新书,4个榜单)' },
46
+ { label: '男生大热榜' },
47
+ { label: '男生新书榜' },
48
+ { label: '女生大热榜' },
49
+ { label: '女生新书榜' },
50
+ ],
51
+ };
52
+ }
40
53
  return {
41
54
  question: `${platformLabel(platform)} 同时支持长篇和短篇,请选择:`,
42
55
  hideCustomInput: true,