@jackwener/opencli 1.5.5 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/README.zh-CN.md +36 -4
- package/dist/browser/daemon-client.d.ts +5 -1
- package/dist/browser/page.d.ts +6 -0
- package/dist/browser/page.js +15 -0
- package/dist/cli-manifest.json +1229 -67
- package/dist/clis/band/bands.d.ts +1 -0
- package/dist/clis/band/bands.js +72 -0
- package/dist/clis/band/mentions.d.ts +1 -0
- package/dist/clis/band/mentions.js +127 -0
- package/dist/clis/band/post.d.ts +1 -0
- package/dist/clis/band/post.js +175 -0
- package/dist/clis/band/posts.d.ts +1 -0
- package/dist/clis/band/posts.js +94 -0
- package/dist/clis/doubao/detail.d.ts +1 -0
- package/dist/clis/doubao/detail.js +33 -0
- package/dist/clis/doubao/detail.test.d.ts +1 -0
- package/dist/clis/doubao/detail.test.js +42 -0
- package/dist/clis/doubao/history.d.ts +1 -0
- package/dist/clis/doubao/history.js +28 -0
- package/dist/clis/doubao/history.test.d.ts +1 -0
- package/dist/clis/doubao/history.test.js +37 -0
- package/dist/clis/doubao/meeting-summary.d.ts +1 -0
- package/dist/clis/doubao/meeting-summary.js +39 -0
- package/dist/clis/doubao/meeting-transcript.d.ts +1 -0
- package/dist/clis/doubao/meeting-transcript.js +36 -0
- package/dist/clis/doubao/utils.d.ts +27 -0
- package/dist/clis/doubao/utils.js +317 -0
- package/dist/clis/doubao/utils.test.d.ts +1 -0
- package/dist/clis/doubao/utils.test.js +24 -0
- package/dist/clis/douyin/_shared/public-api.d.ts +33 -0
- package/dist/clis/douyin/_shared/public-api.js +29 -0
- package/dist/clis/douyin/user-videos.d.ts +5 -0
- package/dist/clis/douyin/user-videos.js +74 -0
- package/dist/clis/douyin/user-videos.test.d.ts +1 -0
- package/dist/clis/douyin/user-videos.test.js +108 -0
- package/dist/clis/ones/common.d.ts +32 -0
- package/dist/clis/ones/common.js +144 -0
- package/dist/clis/ones/enrich-tasks.d.ts +5 -0
- package/dist/clis/ones/enrich-tasks.js +37 -0
- package/dist/clis/ones/login.d.ts +1 -0
- package/dist/clis/ones/login.js +80 -0
- package/dist/clis/ones/logout.d.ts +1 -0
- package/dist/clis/ones/logout.js +17 -0
- package/dist/clis/ones/me.d.ts +1 -0
- package/dist/clis/ones/me.js +30 -0
- package/dist/clis/ones/my-tasks.d.ts +1 -0
- package/dist/clis/ones/my-tasks.js +120 -0
- package/dist/clis/ones/resolve-labels.d.ts +10 -0
- package/dist/clis/ones/resolve-labels.js +64 -0
- package/dist/clis/ones/task-helpers.d.ts +29 -0
- package/dist/clis/ones/task-helpers.js +212 -0
- package/dist/clis/ones/task-helpers.test.d.ts +1 -0
- package/dist/clis/ones/task-helpers.test.js +12 -0
- package/dist/clis/ones/task.d.ts +1 -0
- package/dist/clis/ones/task.js +66 -0
- package/dist/clis/ones/tasks.d.ts +1 -0
- package/dist/clis/ones/tasks.js +79 -0
- package/dist/clis/ones/token-info.d.ts +1 -0
- package/dist/clis/ones/token-info.js +42 -0
- package/dist/clis/ones/worklog.d.ts +11 -0
- package/dist/clis/ones/worklog.js +267 -0
- package/dist/clis/ones/worklog.test.d.ts +1 -0
- package/dist/clis/ones/worklog.test.js +20 -0
- package/dist/clis/spotify/spotify.d.ts +1 -0
- package/dist/clis/spotify/spotify.js +316 -0
- package/dist/clis/spotify/utils.d.ts +21 -0
- package/dist/clis/spotify/utils.js +66 -0
- package/dist/clis/spotify/utils.test.d.ts +1 -0
- package/dist/clis/spotify/utils.test.js +67 -0
- package/dist/clis/tieba/commands.test.d.ts +4 -0
- package/dist/clis/tieba/commands.test.js +79 -0
- package/dist/clis/tieba/hot.d.ts +1 -0
- package/dist/clis/tieba/hot.js +48 -0
- package/dist/clis/tieba/posts.d.ts +1 -0
- package/dist/clis/tieba/posts.js +85 -0
- package/dist/clis/tieba/read.d.ts +1 -0
- package/dist/clis/tieba/read.js +140 -0
- package/dist/clis/tieba/search.d.ts +1 -0
- package/dist/clis/tieba/search.js +108 -0
- package/dist/clis/tieba/utils.d.ts +101 -0
- package/dist/clis/tieba/utils.js +240 -0
- package/dist/clis/tieba/utils.test.d.ts +1 -0
- package/dist/clis/tieba/utils.test.js +290 -0
- package/dist/clis/weread/book.js +100 -13
- package/dist/clis/weread/commands.test.js +221 -0
- package/dist/clis/weread/private-api-regression.test.d.ts +1 -0
- package/dist/{weread-private-api-regression.test.js → clis/weread/private-api-regression.test.js} +92 -30
- package/dist/clis/weread/search-regression.test.d.ts +1 -0
- package/dist/clis/weread/search-regression.test.js +407 -0
- package/dist/clis/weread/search.js +143 -7
- package/dist/clis/weread/shelf.js +13 -95
- package/dist/clis/weread/utils.d.ts +46 -0
- package/dist/clis/weread/utils.js +214 -7
- package/dist/clis/weread/utils.test.js +71 -1
- package/dist/clis/xiaohongshu/publish.d.ts +1 -1
- package/dist/clis/xiaohongshu/publish.js +78 -31
- package/dist/clis/xiaohongshu/publish.test.js +66 -1
- package/dist/clis/xiaohongshu/user-helpers.d.ts +1 -0
- package/dist/clis/xiaohongshu/user-helpers.js +2 -0
- package/dist/clis/xiaohongshu/user-helpers.test.js +18 -0
- package/dist/clis/xueqiu/comments.d.ts +118 -0
- package/dist/clis/xueqiu/comments.js +354 -0
- package/dist/clis/xueqiu/comments.test.d.ts +1 -0
- package/dist/clis/xueqiu/comments.test.js +696 -0
- package/dist/clis/youtube/transcript.js +2 -4
- package/dist/clis/youtube/utils.d.ts +9 -0
- package/dist/clis/youtube/utils.js +67 -3
- package/dist/clis/youtube/utils.test.d.ts +1 -0
- package/dist/clis/youtube/utils.test.js +37 -0
- package/dist/clis/youtube/video.js +16 -15
- package/dist/clis/zsxq/dynamics.d.ts +1 -0
- package/dist/clis/zsxq/dynamics.js +47 -0
- package/dist/clis/zsxq/groups.d.ts +1 -0
- package/dist/clis/zsxq/groups.js +32 -0
- package/dist/clis/zsxq/search.d.ts +1 -0
- package/dist/clis/zsxq/search.js +43 -0
- package/dist/clis/zsxq/search.test.d.ts +1 -0
- package/dist/clis/zsxq/search.test.js +24 -0
- package/dist/clis/zsxq/topic.d.ts +1 -0
- package/dist/clis/zsxq/topic.js +47 -0
- package/dist/clis/zsxq/topic.test.d.ts +1 -0
- package/dist/clis/zsxq/topic.test.js +29 -0
- package/dist/clis/zsxq/topics.d.ts +1 -0
- package/dist/clis/zsxq/topics.js +25 -0
- package/dist/clis/zsxq/topics.test.d.ts +1 -0
- package/dist/clis/zsxq/topics.test.js +24 -0
- package/dist/clis/zsxq/utils.d.ts +97 -0
- package/dist/clis/zsxq/utils.js +230 -0
- package/dist/commanderAdapter.js +1 -1
- package/dist/commanderAdapter.test.js +39 -0
- package/dist/external-clis.yaml +17 -0
- package/dist/types.d.ts +5 -0
- package/docs/.vitepress/config.mts +3 -0
- package/docs/adapters/browser/band.md +63 -0
- package/docs/adapters/browser/ones.md +59 -0
- package/docs/adapters/browser/spotify.md +62 -0
- package/docs/adapters/browser/tieba.md +45 -0
- package/docs/adapters/browser/xueqiu.md +5 -0
- package/docs/adapters/browser/zsxq.md +49 -0
- package/docs/adapters/index.md +5 -2
- package/docs/adapters-doc/ones.md +32 -0
- package/extension/src/background.ts +15 -0
- package/extension/src/cdp.ts +42 -0
- package/extension/src/protocol.ts +5 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +16 -0
- package/src/browser/daemon-client.ts +5 -1
- package/src/browser/page.ts +16 -0
- package/src/clis/band/bands.ts +76 -0
- package/src/clis/band/mentions.ts +134 -0
- package/src/clis/band/post.ts +187 -0
- package/src/clis/band/posts.ts +106 -0
- package/src/clis/doubao/detail.test.ts +53 -0
- package/src/clis/doubao/detail.ts +41 -0
- package/src/clis/doubao/history.test.ts +45 -0
- package/src/clis/doubao/history.ts +32 -0
- package/src/clis/doubao/meeting-summary.ts +53 -0
- package/src/clis/doubao/meeting-transcript.ts +48 -0
- package/src/clis/doubao/utils.test.ts +45 -0
- package/src/clis/doubao/utils.ts +371 -0
- package/src/clis/douyin/_shared/public-api.ts +84 -0
- package/src/clis/douyin/user-videos.test.ts +122 -0
- package/src/clis/douyin/user-videos.ts +101 -0
- package/src/clis/ones/common.ts +187 -0
- package/src/clis/ones/enrich-tasks.ts +47 -0
- package/src/clis/ones/login.ts +103 -0
- package/src/clis/ones/logout.ts +19 -0
- package/src/clis/ones/me.ts +34 -0
- package/src/clis/ones/my-tasks.ts +148 -0
- package/src/clis/ones/resolve-labels.ts +80 -0
- package/src/clis/ones/task-helpers.test.ts +14 -0
- package/src/clis/ones/task-helpers.ts +214 -0
- package/src/clis/ones/task.ts +79 -0
- package/src/clis/ones/tasks.ts +92 -0
- package/src/clis/ones/token-info.ts +46 -0
- package/src/clis/ones/worklog.test.ts +24 -0
- package/src/clis/ones/worklog.ts +306 -0
- package/src/clis/spotify/spotify.ts +328 -0
- package/src/clis/spotify/utils.test.ts +87 -0
- package/src/clis/spotify/utils.ts +92 -0
- package/src/clis/tieba/commands.test.ts +86 -0
- package/src/clis/tieba/hot.ts +52 -0
- package/src/clis/tieba/posts.ts +108 -0
- package/src/clis/tieba/read.ts +158 -0
- package/src/clis/tieba/search.ts +119 -0
- package/src/clis/tieba/utils.test.ts +322 -0
- package/src/clis/tieba/utils.ts +348 -0
- package/src/clis/weread/book.ts +116 -13
- package/src/clis/weread/commands.test.ts +249 -0
- package/src/{weread-private-api-regression.test.ts → clis/weread/private-api-regression.test.ts} +108 -30
- package/src/clis/weread/search-regression.test.ts +440 -0
- package/src/clis/weread/search.ts +189 -9
- package/src/clis/weread/shelf.ts +20 -122
- package/src/clis/weread/utils.test.ts +81 -1
- package/src/clis/weread/utils.ts +264 -7
- package/src/clis/xiaohongshu/publish.test.ts +79 -1
- package/src/clis/xiaohongshu/publish.ts +84 -30
- package/src/clis/xiaohongshu/user-helpers.test.ts +23 -0
- package/src/clis/xiaohongshu/user-helpers.ts +4 -0
- package/src/clis/xueqiu/comments.test.ts +823 -0
- package/src/clis/xueqiu/comments.ts +461 -0
- package/src/clis/youtube/transcript.ts +2 -4
- package/src/clis/youtube/utils.test.ts +43 -0
- package/src/clis/youtube/utils.ts +69 -0
- package/src/clis/youtube/video.ts +16 -15
- package/src/clis/zsxq/dynamics.ts +60 -0
- package/src/clis/zsxq/groups.ts +41 -0
- package/src/clis/zsxq/search.test.ts +29 -0
- package/src/clis/zsxq/search.ts +54 -0
- package/src/clis/zsxq/topic.test.ts +34 -0
- package/src/clis/zsxq/topic.ts +68 -0
- package/src/clis/zsxq/topics.test.ts +29 -0
- package/src/clis/zsxq/topics.ts +36 -0
- package/src/clis/zsxq/utils.ts +351 -0
- package/src/commanderAdapter.test.ts +47 -0
- package/src/commanderAdapter.ts +1 -1
- package/src/external-clis.yaml +17 -0
- package/src/types.ts +5 -0
- package/tests/e2e/band-auth.test.ts +20 -0
- package/tests/e2e/browser-auth-helpers.ts +18 -0
- package/tests/e2e/browser-auth.test.ts +35 -47
- package/tests/e2e/browser-public.test.ts +288 -0
- package/tests/e2e/management.test.ts +1 -1
- package/tests/e2e/plugin-management.test.ts +1 -1
- package/vitest.config.ts +1 -0
- package/SKILL.md +0 -879
- package/dist/weread-private-api-regression.test.d.ts +0 -1
- package/dist/weread-search-regression.test.d.ts +0 -1
- package/dist/weread-search-regression.test.js +0 -39
- package/src/weread-search-regression.test.ts +0 -44
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Flow:
|
|
5
5
|
* 1. Navigate to creator publish page
|
|
6
|
-
* 2. Upload images via
|
|
6
|
+
* 2. Upload images via CDP DOM.setFileInputFiles (with base64 fallback)
|
|
7
7
|
* 3. Fill title and body text
|
|
8
8
|
* 4. Add topic hashtags
|
|
9
9
|
* 5. Publish (or save as draft)
|
|
@@ -43,44 +43,98 @@ const TITLE_SELECTORS = [
|
|
|
43
43
|
'input[maxlength]',
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const SUPPORTED_EXTENSIONS: Record<string, string> = {
|
|
47
|
+
'.jpg': 'image/jpeg',
|
|
48
|
+
'.jpeg': 'image/jpeg',
|
|
49
|
+
'.png': 'image/png',
|
|
50
|
+
'.gif': 'image/gif',
|
|
51
|
+
'.webp': 'image/webp',
|
|
52
|
+
};
|
|
47
53
|
|
|
48
54
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
55
|
+
* Validate image paths: check existence and extension.
|
|
56
|
+
* Returns resolved absolute paths.
|
|
51
57
|
*/
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
};
|
|
63
|
-
const mimeType = mimeMap[ext];
|
|
64
|
-
if (!mimeType) throw new Error(`Unsupported image format "${ext}". Supported: jpg, png, gif, webp`);
|
|
65
|
-
const base64 = fs.readFileSync(absPath).toString('base64');
|
|
66
|
-
return { name: path.basename(absPath), mimeType, base64 };
|
|
58
|
+
function validateImagePaths(filePaths: string[]): string[] {
|
|
59
|
+
return filePaths.map((filePath) => {
|
|
60
|
+
const absPath = path.resolve(filePath);
|
|
61
|
+
if (!fs.existsSync(absPath)) throw new Error(`Image file not found: ${absPath}`);
|
|
62
|
+
const ext = path.extname(absPath).toLowerCase();
|
|
63
|
+
if (!SUPPORTED_EXTENSIONS[ext]) {
|
|
64
|
+
throw new Error(`Unsupported image format "${ext}". Supported: jpg, png, gif, webp`);
|
|
65
|
+
}
|
|
66
|
+
return absPath;
|
|
67
|
+
});
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
/** CSS selector for image-accepting file inputs. */
|
|
71
|
+
const IMAGE_INPUT_SELECTOR = 'input[type="file"][accept*="image"],'
|
|
72
|
+
+ 'input[type="file"][accept*=".jpg"],'
|
|
73
|
+
+ 'input[type="file"][accept*=".jpeg"],'
|
|
74
|
+
+ 'input[type="file"][accept*=".png"],'
|
|
75
|
+
+ 'input[type="file"][accept*=".gif"],'
|
|
76
|
+
+ 'input[type="file"][accept*=".webp"]';
|
|
77
|
+
|
|
69
78
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* a synthetic 'change' event on the input element.
|
|
79
|
+
* Upload images via CDP DOM.setFileInputFiles — Chrome reads files directly
|
|
80
|
+
* from the local filesystem, avoiding base64 payload size limits.
|
|
73
81
|
*
|
|
74
|
-
*
|
|
82
|
+
* Falls back to the legacy base64 DataTransfer approach if the extension
|
|
83
|
+
* does not support set-file-input (e.g. older extension version).
|
|
75
84
|
*/
|
|
76
|
-
async function
|
|
85
|
+
async function uploadImages(
|
|
86
|
+
page: IPage,
|
|
87
|
+
absPaths: string[],
|
|
88
|
+
): Promise<{ ok: boolean; count: number; error?: string }> {
|
|
89
|
+
// ── Primary: CDP DOM.setFileInputFiles ──────────────────────────────
|
|
90
|
+
if (page.setFileInput) {
|
|
91
|
+
try {
|
|
92
|
+
// Find image-accepting file input on the page
|
|
93
|
+
const selector: string | null = await page.evaluate(`
|
|
94
|
+
(() => {
|
|
95
|
+
const sels = ${JSON.stringify(IMAGE_INPUT_SELECTOR)};
|
|
96
|
+
const el = document.querySelector(sels);
|
|
97
|
+
return el ? sels : null;
|
|
98
|
+
})()
|
|
99
|
+
`);
|
|
100
|
+
if (!selector) {
|
|
101
|
+
return { ok: false, count: 0, error: 'No file input found on page' };
|
|
102
|
+
}
|
|
103
|
+
await page.setFileInput(absPaths, selector);
|
|
104
|
+
return { ok: true, count: absPaths.length };
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// If set-file-input action is not supported by extension, fall through to legacy
|
|
107
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
108
|
+
if (msg.includes('Unknown action') || msg.includes('not supported')) {
|
|
109
|
+
// Extension too old — fall through to legacy base64 method
|
|
110
|
+
} else {
|
|
111
|
+
return { ok: false, count: 0, error: msg };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Fallback: legacy base64 DataTransfer injection ─────────────────
|
|
117
|
+
const images = absPaths.map((absPath) => {
|
|
118
|
+
const base64 = fs.readFileSync(absPath).toString('base64');
|
|
119
|
+
const ext = path.extname(absPath).toLowerCase();
|
|
120
|
+
return { name: path.basename(absPath), mimeType: SUPPORTED_EXTENSIONS[ext], base64 };
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Warn if total payload is large — this may fail with older extensions
|
|
124
|
+
const totalBytes = images.reduce((sum, img) => sum + img.base64.length, 0);
|
|
125
|
+
if (totalBytes > 500_000) {
|
|
126
|
+
console.warn(
|
|
127
|
+
`[warn] Total image payload is ${(totalBytes / 1024 / 1024).toFixed(1)}MB (base64). ` +
|
|
128
|
+
'This may fail with the browser bridge. Update the extension to v1.6+ for CDP-based upload, ' +
|
|
129
|
+
'or compress images before publishing.'
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
77
133
|
const payload = JSON.stringify(images);
|
|
78
134
|
return page.evaluate(`
|
|
79
135
|
(async () => {
|
|
80
136
|
const images = ${payload};
|
|
81
137
|
|
|
82
|
-
// Only use image-capable file inputs. Do not fall back to a generic uploader,
|
|
83
|
-
// otherwise we can accidentally feed images into the video upload flow.
|
|
84
138
|
const inputs = Array.from(document.querySelectorAll('input[type="file"]'));
|
|
85
139
|
const input = inputs.find(el => {
|
|
86
140
|
const accept = el.getAttribute('accept') || '';
|
|
@@ -346,8 +400,8 @@ cli({
|
|
|
346
400
|
if (imagePaths.length > MAX_IMAGES)
|
|
347
401
|
throw new Error(`Too many images: ${imagePaths.length} (max ${MAX_IMAGES})`);
|
|
348
402
|
|
|
349
|
-
//
|
|
350
|
-
const
|
|
403
|
+
// Validate image paths before navigating (fast-fail on bad paths / unsupported formats)
|
|
404
|
+
const absImagePaths = validateImagePaths(imagePaths);
|
|
351
405
|
|
|
352
406
|
// ── Step 1: Navigate to publish page ──────────────────────────────────────
|
|
353
407
|
await page.goto(PUBLISH_URL);
|
|
@@ -377,7 +431,7 @@ cli({
|
|
|
377
431
|
}
|
|
378
432
|
|
|
379
433
|
// ── Step 3: Upload images ──────────────────────────────────────────────────
|
|
380
|
-
const upload = await
|
|
434
|
+
const upload = await uploadImages(page, absImagePaths);
|
|
381
435
|
if (!upload.ok) {
|
|
382
436
|
await page.screenshot({ path: '/tmp/xhs_publish_upload_debug.png' });
|
|
383
437
|
throw new Error(
|
|
@@ -532,7 +586,7 @@ cli({
|
|
|
532
586
|
status: isSuccess ? `✅ ${verb}` : '⚠️ 操作完成,请在浏览器中确认',
|
|
533
587
|
detail: [
|
|
534
588
|
`"${title}"`,
|
|
535
|
-
`${
|
|
589
|
+
`${absImagePaths.length}张图片`,
|
|
536
590
|
topics.length ? `话题: ${topics.join(' ')}` : '',
|
|
537
591
|
successMsg || finalUrl || '',
|
|
538
592
|
]
|
|
@@ -75,6 +75,7 @@ describe('extractXhsUserNotes', () => {
|
|
|
75
75
|
title: 'First note',
|
|
76
76
|
type: 'video',
|
|
77
77
|
likes: '4.6万',
|
|
78
|
+
cover: '',
|
|
78
79
|
url: 'https://www.xiaohongshu.com/user/profile/user-1/note-1?xsec_token=abc&xsec_source=pc_user',
|
|
79
80
|
},
|
|
80
81
|
{
|
|
@@ -82,11 +83,33 @@ describe('extractXhsUserNotes', () => {
|
|
|
82
83
|
title: 'Second note',
|
|
83
84
|
type: 'normal',
|
|
84
85
|
likes: '42',
|
|
86
|
+
cover: '',
|
|
85
87
|
url: 'https://www.xiaohongshu.com/user/profile/fallback-user/note-2',
|
|
86
88
|
},
|
|
87
89
|
]);
|
|
88
90
|
});
|
|
89
91
|
|
|
92
|
+
it('extracts cover urls with fallback priority urlDefault -> urlPre -> url', () => {
|
|
93
|
+
const rows = extractXhsUserNotes(
|
|
94
|
+
{
|
|
95
|
+
noteGroups: [
|
|
96
|
+
[
|
|
97
|
+
{ noteCard: { noteId: 'cover-1', cover: { urlDefault: 'https://img.example/default.jpg', urlPre: 'https://img.example/pre.jpg', url: 'https://img.example/raw.jpg' } } },
|
|
98
|
+
{ noteCard: { noteId: 'cover-2', cover: { urlPre: 'https://img.example/pre-only.jpg', url: 'https://img.example/raw-only.jpg' } } },
|
|
99
|
+
{ noteCard: { noteId: 'cover-3', cover: { url: 'https://img.example/raw-fallback.jpg' } } },
|
|
100
|
+
],
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
'fallback-user'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(rows.map(row => row.cover)).toEqual([
|
|
107
|
+
'https://img.example/default.jpg',
|
|
108
|
+
'https://img.example/pre-only.jpg',
|
|
109
|
+
'https://img.example/raw-fallback.jpg',
|
|
110
|
+
]);
|
|
111
|
+
});
|
|
112
|
+
|
|
90
113
|
it('deduplicates repeated notes by note id', () => {
|
|
91
114
|
const rows = extractXhsUserNotes(
|
|
92
115
|
{
|
|
@@ -8,6 +8,7 @@ export interface XhsUserNoteRow {
|
|
|
8
8
|
title: string;
|
|
9
9
|
type: string;
|
|
10
10
|
likes: string;
|
|
11
|
+
cover: string;
|
|
11
12
|
url: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -72,11 +73,14 @@ export function extractXhsUserNotes(snapshot: XhsUserPageSnapshot, fallbackUserI
|
|
|
72
73
|
const xsecToken = toCleanString(entry?.xsecToken ?? entry?.xsec_token ?? noteCard.xsecToken ?? noteCard.xsec_token);
|
|
73
74
|
const likes = toCleanString(noteCard.interactInfo?.likedCount ?? noteCard.interact_info?.liked_count ?? 0) || '0';
|
|
74
75
|
|
|
76
|
+
const cover = toCleanString(noteCard.cover?.urlDefault ?? noteCard.cover?.urlPre ?? noteCard.cover?.url ?? '');
|
|
77
|
+
|
|
75
78
|
rows.push({
|
|
76
79
|
id: noteId,
|
|
77
80
|
title: toCleanString(noteCard.displayTitle ?? noteCard.display_title ?? noteCard.title),
|
|
78
81
|
type: toCleanString(noteCard.type),
|
|
79
82
|
likes,
|
|
83
|
+
cover,
|
|
80
84
|
url: buildXhsNoteUrl(userId || fallbackUserId, noteId, xsecToken),
|
|
81
85
|
});
|
|
82
86
|
}
|