Skip to content

Commit be2c1cd

Browse files
Benjamin-eecsBenjamin Liujackwener
authored
feat(download): show saved file path in web read and weixin download output (jackwener#1042)
* feat(download): show saved file path in web read and weixin download output Closes jackwener#1038 * test(download): cover saved article path --------- Co-authored-by: Benjamin Liu <beneecs@Benjamins-Mac-mini.local> Co-authored-by: jackwener <jakevingoo@gmail.com>
1 parent 1ecbf7a commit be2c1cd

6 files changed

Lines changed: 56 additions & 8 deletions

File tree

clis/web/read.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ cli({
2727
{ name: 'download-images', type: 'boolean', default: true, help: 'Download images locally' },
2828
{ name: 'wait', type: 'int', default: 3, help: 'Seconds to wait after page load' },
2929
],
30-
columns: ['title', 'author', 'publish_time', 'status', 'size'],
30+
columns: ['title', 'author', 'publish_time', 'status', 'size', 'saved'],
3131
func: async (page, kwargs) => {
3232
const url = kwargs.url;
3333
const waitSeconds = kwargs.wait ?? 3;

clis/weixin/download.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,12 @@ cli({
179179
{ name: 'output', default: './weixin-articles', help: 'Output directory' },
180180
{ name: 'download-images', type: 'boolean', default: true, help: 'Download images locally' },
181181
],
182-
columns: ['title', 'author', 'publish_time', 'status', 'size'],
182+
columns: ['title', 'author', 'publish_time', 'status', 'size', 'saved'],
183183
func: async (page, kwargs) => {
184184
const rawUrl = kwargs.url;
185185
const url = normalizeWechatUrl(rawUrl);
186186
if (!url.startsWith('https://mp.weixin.qq.com/')) {
187-
return [{ title: 'Error', author: '-', publish_time: '-', status: 'invalid URL', size: '-' }];
187+
return [{ title: 'Error', author: '-', publish_time: '-', status: 'invalid URL', size: '-', saved: '-' }];
188188
}
189189
// Navigate and wait for content to load
190190
await page.goto(url);
@@ -297,6 +297,7 @@ cli({
297297
publish_time: '-',
298298
status: 'failed — verification required in WeChat browser page',
299299
size: '-',
300+
saved: '-',
300301
}];
301302
}
302303
return downloadArticle({

skills/opencli-usage/SKILL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ opencli browser tab close 91F4D22A7C10...
7878
| **post/create** | Twitter, Jike, Douyin, Weibo |
7979
| **AI chat** | Grok, Doubao, ChatGPT, Gemini, Cursor, Codex, NotebookLM |
8080
| **finance/stock** | Xueqiu, Yahoo Finance, Barchart, Sina Finance, Bloomberg |
81-
| **web scraping** | `opencli web read --url <url>` — any URL to Markdown |
81+
| **web scraping** | `opencli web read --url <url>` — any URL to Markdown (saves to local file, check `saved` field in output) |
8282
| **GitHub/DevOps** | `opencli gh`, `opencli docker`, `opencli vercel` — external CLI passthrough |
8383
| **collaboration** | `opencli lark-cli`, `opencli dws`, `opencli wecom-cli` — external CLI passthrough |
8484
@@ -153,9 +153,9 @@ Type legend: 🌐 = Browser (needs Chrome login) · ✅ = Public API (no browser
153153
| **tiktok** | 🌐 | `explore` `search` `profile` `user` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `live` `notifications` `friends` |
154154
| **twitter** | 🌐 | `trending` `bookmarks` `search` `profile` `timeline` `lists` `list-tweets` `list-add` `list-remove` `thread` `article` `follow` `unfollow` `bookmark` `unbookmark` `post` `like` `likes` `reply` `delete` `block` `unblock` `followers` `following` `notifications` `hide-reply` `download` `accept` `reply-dm` |
155155
| **v2ex** | ✅🌐 | Public: `hot` `latest` `topic` `node` `nodes` `member` `user` `replies` · Browser: `daily` `me` `notifications` |
156-
| **web** | 🌐 | `read` — any URL to Markdown |
156+
| **web** | 🌐 | `read` — any URL to Markdown (saves to local file, not stdout) |
157157
| **weibo** | 🌐 | `hot` `search` `feed` `user` `me` `post` `comments` |
158-
| **weixin** | 🌐 | `download` — 公众号 article to Markdown |
158+
| **weixin** | 🌐 | `download` — 公众号 article to Markdown (saves to local file, not stdout) |
159159
| **wanfang** | 🌐 | `search` |
160160
| **weread** | 🌐 | `shelf` `search` `book` `highlights` `notes` `notebooks` `ranking` |
161161
| **wikipedia** || `search` `summary` `random` `trending` |

skills/opencli-usage/commands.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ opencli v2ex notifications --limit 10 # 通知
713713
## Web 🌐
714714
715715
```bash
716-
opencli web read --url "https://..." # 抓取任意网页并导出为 Markdown
716+
opencli web read --url "https://..." # 抓取任意网页并导出为 Markdown(默认保存到 ./web-articles/,可用 --output 修改)
717717
```
718718
719719
## Wanfang 🌐
@@ -737,7 +737,7 @@ opencli weibo comments <mid> # 微博评论
737737
## Weixin (微信公众号) 🌐
738738
739739
```bash
740-
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" # 下载公众号文章为 Markdown
740+
opencli weixin download --url "https://mp.weixin.qq.com/s/xxx" # 下载公众号文章为 Markdown(默认保存到 ./weixin-articles/,可用 --output 修改)
741741
```
742742
743743
## WeRead (微信读书) 🌐
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as fs from 'node:fs';
2+
import * as os from 'node:os';
3+
import * as path from 'node:path';
4+
import { afterEach, describe, expect, it } from 'vitest';
5+
import { downloadArticle } from './article-download.js';
6+
7+
const tempDirs: string[] = [];
8+
9+
afterEach(() => {
10+
for (const dir of tempDirs) {
11+
try {
12+
fs.rmSync(dir, { recursive: true, force: true });
13+
} catch {
14+
// Ignore cleanup errors in tests.
15+
}
16+
}
17+
tempDirs.length = 0;
18+
});
19+
20+
describe('downloadArticle', () => {
21+
it('returns the saved markdown file path on success', async () => {
22+
const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'opencli-article-'));
23+
tempDirs.push(tempDir);
24+
25+
const result = await downloadArticle({
26+
title: 'Test Article',
27+
author: 'Author',
28+
publishTime: '2026-04-20 12:00:00',
29+
sourceUrl: 'https://example.com/article',
30+
contentHtml: '<p>Hello world</p>',
31+
}, {
32+
output: tempDir,
33+
downloadImages: false,
34+
});
35+
36+
expect(result).toHaveLength(1);
37+
expect(result[0].status).toBe('success');
38+
expect(result[0].saved).toMatch(new RegExp(`^${tempDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`));
39+
expect(path.extname(result[0].saved)).toBe('.md');
40+
expect(fs.existsSync(result[0].saved)).toBe(true);
41+
expect(fs.readFileSync(result[0].saved, 'utf8')).toContain('Hello world');
42+
});
43+
});

src/download/article-download.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface ArticleDownloadResult {
5656
publish_time: string;
5757
status: string;
5858
size: string;
59+
saved: string;
5960
}
6061

6162
const DEFAULT_LABELS: Required<FrontmatterLabels> = {
@@ -212,6 +213,7 @@ export async function downloadArticle(
212213
publish_time: '-',
213214
status: 'failed — no title',
214215
size: '-',
216+
saved: '-',
215217
}];
216218
}
217219

@@ -222,6 +224,7 @@ export async function downloadArticle(
222224
publish_time: data.publishTime || '-',
223225
status: 'failed — no content',
224226
size: '-',
227+
saved: '-',
225228
}];
226229
}
227230

@@ -268,5 +271,6 @@ export async function downloadArticle(
268271
publish_time: data.publishTime || '-',
269272
status: 'success',
270273
size: formatBytes(size),
274+
saved: filePath,
271275
}];
272276
}

0 commit comments

Comments
 (0)