From d4b1ac48533dbbb8c18d750477ab1a60e2e3b6d7 Mon Sep 17 00:00:00 2001 From: pancato Date: Mon, 23 Mar 2026 12:14:23 +0800 Subject: [PATCH 1/2] refactor: Update image selector for improved media extraction and standardize string literals. --- src/clis/xiaohongshu/download.ts | 63 +++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/clis/xiaohongshu/download.ts b/src/clis/xiaohongshu/download.ts index 2e12297e..df66bc80 100644 --- a/src/clis/xiaohongshu/download.ts +++ b/src/clis/xiaohongshu/download.ts @@ -5,29 +5,41 @@ * opencli xiaohongshu download --note_id abc123 --output ./xhs */ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { cli, Strategy } from '../../registry.js'; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { cli, Strategy } from "../../registry.js"; import { httpDownload, sanitizeFilename, formatCookieHeader, -} from '../../download/index.js'; -import { DownloadProgressTracker, formatBytes } from '../../download/progress.js'; +} from "../../download/index.js"; +import { + DownloadProgressTracker, + formatBytes, +} from "../../download/progress.js"; cli({ - site: 'xiaohongshu', - name: 'download', - description: '下载小红书笔记中的图片和视频', - domain: 'www.xiaohongshu.com', + site: "xiaohongshu", + name: "download", + description: "下载小红书笔记中的图片和视频", + domain: "www.xiaohongshu.com", strategy: Strategy.COOKIE, args: [ - { name: 'note-id', positional: true, required: true, help: 'Note ID (from URL)' }, - { name: 'output', default: './xiaohongshu-downloads', help: 'Output directory' }, + { + name: "note-id", + positional: true, + required: true, + help: "Note ID (from URL)", + }, + { + name: "output", + default: "./xiaohongshu-downloads", + help: "Output directory", + }, ], - columns: ['index', 'type', 'status', 'size'], + columns: ["index", "type", "status", "size"], func: async (page, kwargs) => { - const noteId = kwargs['note-id']; + const noteId = kwargs["note-id"]; const output = kwargs.output; // Navigate to note page @@ -58,7 +70,7 @@ cli({ '.note-slider img', '.note-image img', '.image-wrapper img', - '#noteContainer img[src*="xhscdn"]', + '#noteContainer .media-container img[src*="xhscdn"]', 'img[src*="ci.xiaohongshu.com"]' ]; @@ -109,11 +121,15 @@ cli({ `); if (!data || !data.media || data.media.length === 0) { - return [{ index: 0, type: '-', status: 'failed', size: 'No media found' }]; + return [ + { index: 0, type: "-", status: "failed", size: "No media found" }, + ]; } // Extract cookies for authenticated downloads - const cookies = formatCookieHeader(await page.getCookies({ domain: 'xiaohongshu.com' })); + const cookies = formatCookieHeader( + await page.getCookies({ domain: "xiaohongshu.com" }), + ); // Create output directory const outputDir = path.join(output, noteId); @@ -125,7 +141,7 @@ cli({ for (let i = 0; i < data.media.length; i++) { const media = data.media[i]; - const ext = media.type === 'video' ? 'mp4' : 'jpg'; + const ext = media.type === "video" ? "mp4" : "jpg"; const filename = `${noteId}_${i + 1}.${ext}`; const destPath = path.join(outputDir, filename); @@ -141,7 +157,10 @@ cli({ }); if (progressBar) { - progressBar.complete(result.success, result.success ? formatBytes(result.size) : undefined); + progressBar.complete( + result.success, + result.success ? formatBytes(result.size) : undefined, + ); } tracker.onFileComplete(result.success); @@ -149,8 +168,10 @@ cli({ results.push({ index: i + 1, type: media.type, - status: result.success ? 'success' : 'failed', - size: result.success ? formatBytes(result.size) : (result.error || 'unknown error'), + status: result.success ? "success" : "failed", + size: result.success + ? formatBytes(result.size) + : result.error || "unknown error", }); } catch (err: any) { if (progressBar) progressBar.fail(err.message); @@ -159,7 +180,7 @@ cli({ results.push({ index: i + 1, type: media.type, - status: 'failed', + status: "failed", size: err.message, }); } From d0815d46ba5c112c884eba945709dadf5c657218 Mon Sep 17 00:00:00 2001 From: pancato Date: Mon, 23 Mar 2026 12:17:27 +0800 Subject: [PATCH 2/2] feat: standardize string literals to single quotes and consolidate import statements. --- src/clis/xiaohongshu/download.ts | 61 +++++++++++--------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/src/clis/xiaohongshu/download.ts b/src/clis/xiaohongshu/download.ts index df66bc80..39f071b9 100644 --- a/src/clis/xiaohongshu/download.ts +++ b/src/clis/xiaohongshu/download.ts @@ -5,41 +5,29 @@ * opencli xiaohongshu download --note_id abc123 --output ./xhs */ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { cli, Strategy } from "../../registry.js"; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { cli, Strategy } from '../../registry.js'; import { httpDownload, sanitizeFilename, formatCookieHeader, -} from "../../download/index.js"; -import { - DownloadProgressTracker, - formatBytes, -} from "../../download/progress.js"; +} from '../../download/index.js'; +import { DownloadProgressTracker, formatBytes } from '../../download/progress.js'; cli({ - site: "xiaohongshu", - name: "download", - description: "下载小红书笔记中的图片和视频", - domain: "www.xiaohongshu.com", + site: 'xiaohongshu', + name: 'download', + description: '下载小红书笔记中的图片和视频', + domain: 'www.xiaohongshu.com', strategy: Strategy.COOKIE, args: [ - { - name: "note-id", - positional: true, - required: true, - help: "Note ID (from URL)", - }, - { - name: "output", - default: "./xiaohongshu-downloads", - help: "Output directory", - }, + { name: 'note-id', positional: true, required: true, help: 'Note ID (from URL)' }, + { name: 'output', default: './xiaohongshu-downloads', help: 'Output directory' }, ], - columns: ["index", "type", "status", "size"], + columns: ['index', 'type', 'status', 'size'], func: async (page, kwargs) => { - const noteId = kwargs["note-id"]; + const noteId = kwargs['note-id']; const output = kwargs.output; // Navigate to note page @@ -121,15 +109,11 @@ cli({ `); if (!data || !data.media || data.media.length === 0) { - return [ - { index: 0, type: "-", status: "failed", size: "No media found" }, - ]; + return [{ index: 0, type: '-', status: 'failed', size: 'No media found' }]; } // Extract cookies for authenticated downloads - const cookies = formatCookieHeader( - await page.getCookies({ domain: "xiaohongshu.com" }), - ); + const cookies = formatCookieHeader(await page.getCookies({ domain: 'xiaohongshu.com' })); // Create output directory const outputDir = path.join(output, noteId); @@ -141,7 +125,7 @@ cli({ for (let i = 0; i < data.media.length; i++) { const media = data.media[i]; - const ext = media.type === "video" ? "mp4" : "jpg"; + const ext = media.type === 'video' ? 'mp4' : 'jpg'; const filename = `${noteId}_${i + 1}.${ext}`; const destPath = path.join(outputDir, filename); @@ -157,10 +141,7 @@ cli({ }); if (progressBar) { - progressBar.complete( - result.success, - result.success ? formatBytes(result.size) : undefined, - ); + progressBar.complete(result.success, result.success ? formatBytes(result.size) : undefined); } tracker.onFileComplete(result.success); @@ -168,10 +149,8 @@ cli({ results.push({ index: i + 1, type: media.type, - status: result.success ? "success" : "failed", - size: result.success - ? formatBytes(result.size) - : result.error || "unknown error", + status: result.success ? 'success' : 'failed', + size: result.success ? formatBytes(result.size) : (result.error || 'unknown error'), }); } catch (err: any) { if (progressBar) progressBar.fail(err.message); @@ -180,7 +159,7 @@ cli({ results.push({ index: i + 1, type: media.type, - status: "failed", + status: 'failed', size: err.message, }); }