wechat-media-writer 2.2.3 → 2.2.5
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/SKILL.md +71 -37
- package/bin/cli.js +9 -15
- package/package.json +1 -1
- package/references/publish_template.py +105 -24
- package/scripts/__pycache__/book_cover.cpython-314.pyc +0 -0
- package/scripts/__pycache__/image_downloader.cpython-314.pyc +0 -0
- package/scripts/book_cover.py +75 -28
- package/scripts/publish.py +46 -59
package/SKILL.md
CHANGED
|
@@ -137,9 +137,9 @@ Token 缓存在 `~/.wechat/token_cache.json`(自动管理,2小时有效期
|
|
|
137
137
|
<span>副标题(一句话点睛)</span>
|
|
138
138
|
</p>
|
|
139
139
|
|
|
140
|
-
<!--
|
|
140
|
+
<!-- 主题配图(第一张图,使用主题贴图,非书籍原图) -->
|
|
141
141
|
<p style="margin:0 0 20px;text-align:center;">
|
|
142
|
-
<img src="{
|
|
142
|
+
<img src="{THEME_IMG_1}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图" />
|
|
143
143
|
</p>
|
|
144
144
|
|
|
145
145
|
<!-- 章节(无数字编号,直接标题) -->
|
|
@@ -150,11 +150,26 @@ Token 缓存在 `~/.wechat/token_cache.json`(自动管理,2小时有效期
|
|
|
150
150
|
<span>正文段落。所有段落必须有 text-indent:2em。</span>
|
|
151
151
|
</p>
|
|
152
152
|
|
|
153
|
-
<!--
|
|
153
|
+
<!-- 主题配图(章节间穿插,与文风统一) -->
|
|
154
|
+
<p style="margin:0 0 20px;text-align:center;">
|
|
155
|
+
<img src="{THEME_IMG_2}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图" />
|
|
156
|
+
</p>
|
|
157
|
+
|
|
158
|
+
<!-- 金句引用块(用主题色高亮,加左侧色条) -->
|
|
154
159
|
<p
|
|
155
|
-
style="font-size:17px;line-height:1.9;color:
|
|
160
|
+
style="font-size:17px;line-height:1.9;color:{T};font-weight:bold;text-indent:2em;margin:0 0 20px;padding:14px 18px;background:{CBG};border-left:4px solid {T2};border-radius:6px;"
|
|
156
161
|
>
|
|
157
|
-
<span
|
|
162
|
+
<span style="color:{T2};">▍</span> 金句或核心观点引用。整段用主题主色加粗,加左侧强调色条,与正文形成视觉对比。
|
|
163
|
+
</p>
|
|
164
|
+
|
|
165
|
+
<!-- 重点启发语句(行内主题色 + 强调色高亮) -->
|
|
166
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
167
|
+
<span>正文段落中关键句子用 <strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">主题主色高亮</strong>,核心概念用 <span style="color:{T2};font-weight:bold;">强调色加粗</span>,让读者一眼锁定重点。</span>
|
|
168
|
+
</p>
|
|
169
|
+
|
|
170
|
+
<!-- 主题配图(章节中段) -->
|
|
171
|
+
<p style="margin:0 0 20px;text-align:center;">
|
|
172
|
+
<img src="{THEME_IMG_3}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图" />
|
|
158
173
|
</p>
|
|
159
174
|
|
|
160
175
|
<!-- 分隔符(章节之间) -->
|
|
@@ -162,54 +177,52 @@ Token 缓存在 `~/.wechat/token_cache.json`(自动管理,2小时有效期
|
|
|
162
177
|
</section>
|
|
163
178
|
```
|
|
164
179
|
|
|
165
|
-
### 关键排版规则
|
|
180
|
+
### 关键排版规则
|
|
166
181
|
|
|
167
182
|
1. **所有正文段落必须有 `text-indent:2em`** — 首行缩进
|
|
168
183
|
2. **图片URL必须用完整的 `mmbiz.qpic.cn` 硬编码URL** — 不用模板变量
|
|
169
184
|
3. **`<img>` 必须包裹在 `<p>` 中**,且 `style="width:100%;display:block;border-radius:4px;"`
|
|
170
185
|
4. **行高统一 `line-height:2`** — 阅读舒适
|
|
171
|
-
5. **引用块用 `<p>` + `background` + `border-radius:8px
|
|
186
|
+
5. **引用块用 `<p>` + `background` + `border-radius:8px` + 左侧 `border-left:4px solid {T2}` 色条** — 不是 `<blockquote>`
|
|
172
187
|
6. **章节标题不使用数字编号** — 直接用"作者背景与创作时代",不用"壹"
|
|
173
188
|
7. **强调文字用 `<strong style="color:{T};">`** — 不是加粗或改色
|
|
174
|
-
8.
|
|
175
|
-
9.
|
|
176
|
-
10.
|
|
189
|
+
8. **重点语句/概念高亮**:用 `<strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">` 或 `<span style="color:{T2};font-weight:bold;">` — 让主题色贯穿全文
|
|
190
|
+
9. **星标用 `<span>★★★★☆</span>`** — `font-size:28px;color:{T2}`
|
|
191
|
+
10. **每篇文章只在一个 `<section>` 内** — 不嵌套多个 section
|
|
192
|
+
11. **全文插图统一为主题贴图** — 不再使用书籍原封面(书籍原图往往与文章主题风格不搭),全部用主题贴图
|
|
193
|
+
12. **图片在章节间穿插** — 每 1-2 段正文插一张图,避免长段无图的视觉疲劳
|
|
177
194
|
|
|
178
195
|
## 图片工作流
|
|
179
196
|
|
|
180
|
-
###
|
|
197
|
+
### 第一步:准备主题贴图(不再使用书籍原图作为正文配图)
|
|
181
198
|
|
|
182
|
-
|
|
199
|
+
**书评文章的所有插图统一为主题贴图**(Pexels 等无版权图库),与文章主题、章节氛围贴合。
|
|
200
|
+
不推荐使用书籍原图作为正文封面——书籍封面图风格往往与内容图不一致,且某些封面图清晰度/分辨率不适合正文展示。
|
|
183
201
|
|
|
184
|
-
使用 `scripts/
|
|
202
|
+
使用 `scripts/image_downloader.py` 下载主题贴图:
|
|
185
203
|
|
|
186
204
|
```python
|
|
187
|
-
from
|
|
188
|
-
|
|
189
|
-
# 自动获取封面URL(Google Books -> Open Library -> 豆瓣)
|
|
190
|
-
cover_url = fetch_book_cover("书名", "作者", "ISBN")
|
|
205
|
+
from image_downloader import download_theme_images
|
|
191
206
|
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
cover_path, cover_900_path = download_and_process_cover(cover_url, "/tmp/output")
|
|
207
|
+
# 根据主题下载 6-8 张贴图(用于正文穿插)
|
|
208
|
+
urls = download_theme_images("books", "/tmp/images", count=6)
|
|
195
209
|
```
|
|
196
210
|
|
|
197
|
-
|
|
198
|
-
- 文章内封面:宽度 600px,高度按比例缩放
|
|
199
|
-
- 公众号封面:900x500px(用于文章列表展示)
|
|
211
|
+
### 第二步:下载公众号封面图(精确 900x500)
|
|
200
212
|
|
|
201
|
-
|
|
213
|
+
**公众号封面必须是 900x500 精确尺寸的精美图片**,与正文主题风格统一。
|
|
214
|
+
不再从主题贴图裁剪(裁剪可能损失画面关键内容)——直接从 Pexels CDN 请求 900x500 精确裁切。
|
|
202
215
|
|
|
203
|
-
|
|
216
|
+
每个主题预存 3 张高质量 Pexels 候选图(见 `scripts/book_cover.py` 的 `THEME_COVER_CANDIDATES`),
|
|
217
|
+
通过 `?w=900&h=500&fit=crop` 参数让 Pexels 服务端精确裁切为 900x500:
|
|
204
218
|
|
|
205
219
|
```python
|
|
206
|
-
from
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
urls = download_theme_images("nature", "/tmp/images", count=5)
|
|
220
|
+
from book_cover import download_cover_900x500
|
|
221
|
+
# 下载一张 books 主题的 900x500 封面(seed=0/1/2 轮换候选图)
|
|
222
|
+
download_cover_900x500("books", "/tmp/wx", seed=0)
|
|
210
223
|
```
|
|
211
224
|
|
|
212
|
-
|
|
225
|
+
**支持的主题**:`abstract` / `books` / `nature` / `technology` / `business`
|
|
213
226
|
|
|
214
227
|
### 第三步:上传图片到微信
|
|
215
228
|
|
|
@@ -230,6 +243,8 @@ img_url = upload_content_image(token, "image.jpg")
|
|
|
230
243
|
将上传后的 media_id 和 mmbiz URL **硬编码到发布脚本中**,不用模板变量。
|
|
231
244
|
这是因为微信图片URL有时效性,硬编码已验证的URL最可靠。
|
|
232
245
|
|
|
246
|
+
每篇文章需要:1张公众号封面 + 6-8张正文主题贴图。
|
|
247
|
+
|
|
233
248
|
## 微信 API 集成
|
|
234
249
|
|
|
235
250
|
### API 列表
|
|
@@ -354,15 +369,32 @@ python3 scripts/publish.py --title "标题" --content-file content.html --cover-
|
|
|
354
369
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 8px;text-indent:2em;">
|
|
355
370
|
<strong style="color:{T};">适合人群:</strong>
|
|
356
371
|
</p>
|
|
357
|
-
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;
|
|
358
|
-
<span
|
|
372
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
373
|
+
<span>✅ 创业者/管理者——具体理由<br />✅ 陷入职业倦怠期的人——具体理由<br />✅ 对某领域感兴趣的读者——具体理由</span>
|
|
359
374
|
</p>
|
|
360
375
|
|
|
361
376
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 8px;text-indent:2em;">
|
|
362
377
|
<strong style="color:{T};">不适合人群:</strong>
|
|
363
378
|
</p>
|
|
364
|
-
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;
|
|
365
|
-
<span
|
|
379
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
380
|
+
<span>🚫 已读过类似书籍的人——内容高度重复<br />🚫 讨厌某种风格的人——具体理由<br />🚫 寻找其他类型内容的人——具体理由</span>
|
|
381
|
+
</p>
|
|
382
|
+
|
|
383
|
+
<!-- 结尾页(与正文风格延续,使用主题色) -->
|
|
384
|
+
<p style="margin:0 0 16px;text-align:center;">
|
|
385
|
+
<img src="{THEME_IMG_END}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图" />
|
|
386
|
+
</p>
|
|
387
|
+
|
|
388
|
+
<p style="font-size:14px;color:#ccc;text-align:center;letter-spacing:8px;margin:24px 0 16px;">· · · · · ·</p>
|
|
389
|
+
|
|
390
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 8px;line-height:1.6;">
|
|
391
|
+
<span>📖 《书名》</span>
|
|
392
|
+
</p>
|
|
393
|
+
<p style="font-size:14px;color:{BODY};text-align:center;margin:0 0 4px;line-height:1.6;">
|
|
394
|
+
<span>作者 · 读书笔记</span>
|
|
395
|
+
</p>
|
|
396
|
+
<p style="font-size:14px;color:{T2};text-align:center;margin:0 0 4px;line-height:1.8;font-weight:bold;">
|
|
397
|
+
<span>「 一句话总结:核心洞察 」</span>
|
|
366
398
|
</p>
|
|
367
399
|
```
|
|
368
400
|
|
|
@@ -371,6 +403,8 @@ python3 scripts/publish.py --title "标题" --content-file content.html --cover-
|
|
|
371
403
|
1. **星级显示**:`font-size:28px;color:{T2}`,居中
|
|
372
404
|
2. **分数显示**:`4 / 5 星`,在星标下方
|
|
373
405
|
3. **评分理由**:1-2句话解释为什么给这个分数
|
|
374
|
-
4. **适合人群**:用
|
|
375
|
-
5. **不适合人群**:用
|
|
376
|
-
6.
|
|
406
|
+
4. **适合人群**:用 `✅ ` 标记,每条用——连接理由
|
|
407
|
+
5. **不适合人群**:用 `🚫 ` 标记,每条用——连接理由
|
|
408
|
+
6. **人群列表与首行对齐**:用 `text-indent:2em`(不用 `padding-left`),保证列表项与"适合人群:"首行视觉对齐
|
|
409
|
+
7. **结尾页**:用主题主色显示书名+作者,强调色显示一句话总结,配一张主题贴图作收尾
|
|
410
|
+
8. **不要直接放链接**——所有内容用文案表达
|
package/bin/cli.js
CHANGED
|
@@ -43,9 +43,9 @@ function showHelp() {
|
|
|
43
43
|
npx wechat-media-writer <command> [options]
|
|
44
44
|
|
|
45
45
|
命令:
|
|
46
|
-
cover <书名/电影名> [作者] [ISBN]
|
|
47
|
-
download <主题> [数量]
|
|
48
|
-
publish
|
|
46
|
+
cover <书名/电影名> [作者] [ISBN] 获取书籍原封面(仅用于书籍插图占位)
|
|
47
|
+
download <主题> [数量] [输出目录] 下载主题贴图
|
|
48
|
+
publish 发布文章到微信(主题贴图/封面缺失会自动补齐)
|
|
49
49
|
help 显示帮助信息
|
|
50
50
|
|
|
51
51
|
publish 选项:
|
|
@@ -55,20 +55,14 @@ publish 选项:
|
|
|
55
55
|
--author <作者> 文章作者,默认"读书笔记"
|
|
56
56
|
--digest <摘要> 文章摘要
|
|
57
57
|
--theme <主题> 图片主题:abstract/books/nature/technology/business,默认 abstract
|
|
58
|
-
--image-count <数量>
|
|
59
|
-
--book-title <书名> 书籍标题(封面缺失时自动获取)
|
|
60
|
-
--book-author <作者> 书籍作者
|
|
61
|
-
--book-isbn <ISBN> 书籍ISBN
|
|
58
|
+
--image-count <数量> 主题贴图数量,默认 6(5正文 + 1结尾)
|
|
62
59
|
|
|
63
60
|
示例:
|
|
64
|
-
npx wechat-media-writer
|
|
65
|
-
npx wechat-media-writer
|
|
66
|
-
|
|
67
|
-
--
|
|
68
|
-
--
|
|
69
|
-
--cover-dir /tmp/wx \\
|
|
70
|
-
--book-title "人类简史" \\
|
|
71
|
-
--book-author "尤瓦尔·赫拉利" \\
|
|
61
|
+
npx wechat-media-writer download books 6
|
|
62
|
+
npx wechat-media-writer publish ^
|
|
63
|
+
--title "《人类简史》全书深度拆解" ^
|
|
64
|
+
--content-file content.html ^
|
|
65
|
+
--cover-dir /tmp/wx ^
|
|
72
66
|
--theme books
|
|
73
67
|
|
|
74
68
|
配置:
|
package/package.json
CHANGED
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
用法:
|
|
7
7
|
1. 填写下方的变量(标题、摘要、HTML内容等)
|
|
8
|
-
2. 确保封面图已准备好(使用 scripts/
|
|
8
|
+
2. 确保封面图已准备好(使用 scripts/image_downloader.py 下载主题贴图)
|
|
9
9
|
3. 运行: python3 references/publish_template.py
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import os
|
|
13
13
|
import sys
|
|
14
14
|
|
|
15
|
-
# 添加 scripts 目录到模块搜索路径
|
|
16
15
|
scripts_dir = os.path.join(
|
|
17
16
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts"
|
|
18
17
|
)
|
|
@@ -23,12 +22,11 @@ from wechat_api import load_config, get_access_token, upload_cover, create_draft
|
|
|
23
22
|
|
|
24
23
|
# ========== 替换以下内容 ==========
|
|
25
24
|
|
|
26
|
-
# 标题和摘要
|
|
27
25
|
TITLE = "《书名》全书深度拆解:犀利洞察"
|
|
28
26
|
AUTHOR = "读书笔记"
|
|
29
27
|
DIGEST = "摘要文本,不超过120字。"
|
|
30
28
|
|
|
31
|
-
# 封面图目录(包含 cover_900x500.jpg
|
|
29
|
+
# 封面图目录(包含 cover_900x500.jpg + img_*.jpg 主题贴图)
|
|
32
30
|
COVER_DIR = "/tmp/book_output"
|
|
33
31
|
|
|
34
32
|
# 主题色:主色(深色)+ 强调色(暖/亮色)
|
|
@@ -37,7 +35,16 @@ T2 = "#D4A24C" # 强调色
|
|
|
37
35
|
BODY = "#555" # 正文色
|
|
38
36
|
CBG = "#F0EDE5" # 引用块背景
|
|
39
37
|
|
|
40
|
-
#
|
|
38
|
+
# 图片 URL(替换为上传微信后返回的 mmbiz.qpic.cn URL)
|
|
39
|
+
# 通过 publish.py 自动上传后,从 upload_result.json 中获取
|
|
40
|
+
THEME_IMG_1 = "http://mmbiz.qpic.cn/REPLACE_WITH_THEME_IMG_1"
|
|
41
|
+
THEME_IMG_2 = "http://mmbiz.qpic.cn/REPLACE_WITH_THEME_IMG_2"
|
|
42
|
+
THEME_IMG_3 = "http://mmbiz.qpic.cn/REPLACE_WITH_THEME_IMG_3"
|
|
43
|
+
THEME_IMG_4 = "http://mmbiz.qpic.cn/REPLACE_WITH_THEME_IMG_4"
|
|
44
|
+
THEME_IMG_5 = "http://mmbiz.qpic.cn/REPLACE_WITH_THEME_IMG_5"
|
|
45
|
+
THEME_IMG_END = "http://mmbiz.qpic.cn/REPLACE_WITH_THEME_IMG_END"
|
|
46
|
+
|
|
47
|
+
# 文章 HTML 内容(7段式,无数字编号,全文主题贴图穿插)
|
|
41
48
|
HTML = f"""<section style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif;color:#333;background:#FBF8FC;">
|
|
42
49
|
|
|
43
50
|
<p style="font-size:12px;color:{T2};text-align:center;letter-spacing:4px;margin:24px 0 8px;line-height:3em;">
|
|
@@ -52,25 +59,94 @@ HTML = f"""<section style="margin:0;padding:0;font-family:-apple-system,BlinkMac
|
|
|
52
59
|
<span>副标题(一句话点睛)</span>
|
|
53
60
|
</p>
|
|
54
61
|
|
|
55
|
-
<!--
|
|
62
|
+
<!-- 主题配图1(头部) -->
|
|
56
63
|
<p style="margin:0 0 20px;text-align:center;">
|
|
57
|
-
<img src="
|
|
64
|
+
<img src="{THEME_IMG_1}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图"/>
|
|
58
65
|
</p>
|
|
59
66
|
|
|
60
|
-
<!--
|
|
67
|
+
<!-- 作者背景与创作时代 -->
|
|
61
68
|
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 16px;line-height:1em;">
|
|
62
69
|
<span>作者背景与创作时代</span>
|
|
63
70
|
</p>
|
|
71
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
72
|
+
<span>正文段落。所有段落必须有 text-indent:2em。重点句用 <strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">主题主色高亮</strong>,核心概念用 <span style="color:{T2};font-weight:bold;">强调色加粗</span>。</span>
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<!-- 主题配图2(作者背景后) -->
|
|
76
|
+
<p style="margin:0 0 20px;text-align:center;">
|
|
77
|
+
<img src="{THEME_IMG_2}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图"/>
|
|
78
|
+
</p>
|
|
79
|
+
|
|
80
|
+
<!-- 金句引用块(左侧色条 + 主题色加粗) -->
|
|
81
|
+
<p style="font-size:17px;line-height:1.9;color:{T};font-weight:bold;text-indent:2em;margin:0 0 20px;padding:14px 18px;background:{CBG};border-left:4px solid {T2};border-radius:6px;">
|
|
82
|
+
<span style="color:{T2};">▍</span> 金句或核心观点。整段用主题主色加粗,加左侧强调色条与正文形成视觉对比。
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
<!-- 全书完整逻辑思维导图 -->
|
|
86
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 16px;line-height:1em;">
|
|
87
|
+
<span>全书完整逻辑思维导图</span>
|
|
88
|
+
</p>
|
|
89
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
90
|
+
<span>本章按章节脉络梳理<strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">全书核心逻辑</strong>,用文字框图提炼主线。</span>
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
<!-- 主题配图3(逻辑导图后) -->
|
|
94
|
+
<p style="margin:0 0 20px;text-align:center;">
|
|
95
|
+
<img src="{THEME_IMG_3}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图"/>
|
|
96
|
+
</p>
|
|
97
|
+
|
|
98
|
+
<!-- 三大颠覆性核心论点 -->
|
|
99
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 16px;line-height:1em;">
|
|
100
|
+
<span>三大颠覆性核心论点</span>
|
|
101
|
+
</p>
|
|
102
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
103
|
+
<span><span style="color:{T2};font-weight:bold;">论点一:</span><strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">核心颠覆性观点</strong>,附书中具体案例支撑。</span>
|
|
104
|
+
</p>
|
|
105
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
106
|
+
<span><span style="color:{T2};font-weight:bold;">论点二:</span><strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">核心颠覆性观点</strong>,附书中具体案例支撑。</span>
|
|
107
|
+
</p>
|
|
108
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
109
|
+
<span><span style="color:{T2};font-weight:bold;">论点三:</span><strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">核心颠覆性观点</strong>,附书中具体案例支撑。</span>
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
<!-- 主题配图4(论点后) -->
|
|
113
|
+
<p style="margin:0 0 20px;text-align:center;">
|
|
114
|
+
<img src="{THEME_IMG_4}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图"/>
|
|
115
|
+
</p>
|
|
64
116
|
|
|
117
|
+
<!-- 批判性客观评析 -->
|
|
118
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 16px;line-height:1em;">
|
|
119
|
+
<span>批判性客观评析</span>
|
|
120
|
+
</p>
|
|
65
121
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
66
|
-
<span
|
|
122
|
+
<span>短板一:客观指出书中局限。<br/>短板二:逻辑漏洞分析。<br/>短板三:适用边界。<br/>短板四:时代局限。</span>
|
|
67
123
|
</p>
|
|
68
124
|
|
|
69
|
-
|
|
70
|
-
|
|
125
|
+
<!-- 金句二 -->
|
|
126
|
+
<p style="font-size:17px;line-height:1.9;color:{T};font-weight:bold;text-indent:2em;margin:0 0 20px;padding:14px 18px;background:{CBG};border-left:4px solid {T2};border-radius:6px;">
|
|
127
|
+
<span style="color:{T2};">▍</span> 第二个金句或关键启发。
|
|
71
128
|
</p>
|
|
72
129
|
|
|
73
|
-
<!--
|
|
130
|
+
<!-- 收获与成长 -->
|
|
131
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 16px;line-height:1em;">
|
|
132
|
+
<span>收获与成长</span>
|
|
133
|
+
</p>
|
|
134
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
135
|
+
<span>读完能带走的<strong style="color:{T};background:{CBG};padding:1px 6px;border-radius:3px;">三样东西</strong>,从个人成长视角展开。</span>
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
<!-- 主题配图5(收获与成长后) -->
|
|
139
|
+
<p style="margin:0 0 20px;text-align:center;">
|
|
140
|
+
<img src="{THEME_IMG_5}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图"/>
|
|
141
|
+
</p>
|
|
142
|
+
|
|
143
|
+
<!-- 现实落地应用方案 -->
|
|
144
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 16px;line-height:1em;">
|
|
145
|
+
<span>现实落地应用方案</span>
|
|
146
|
+
</p>
|
|
147
|
+
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
148
|
+
<span>方案一:具体执行步骤。<br/>方案二:可操作建议。<br/>方案三:拿来就用的行动项。</span>
|
|
149
|
+
</p>
|
|
74
150
|
|
|
75
151
|
<p style="font-size:14px;color:#ccc;text-align:center;letter-spacing:8px;margin:20px 0;">· · · · · ·</p>
|
|
76
152
|
|
|
@@ -88,30 +164,38 @@ HTML = f"""<section style="margin:0;padding:0;font-family:-apple-system,BlinkMac
|
|
|
88
164
|
</p>
|
|
89
165
|
|
|
90
166
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
91
|
-
<span>为什么给X
|
|
167
|
+
<span>为什么给X星?简要说明评分理由,诚实评价,3-4星是常态,5星极少。</span>
|
|
92
168
|
</p>
|
|
93
169
|
|
|
94
170
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 8px;text-indent:2em;">
|
|
95
171
|
<strong style="color:{T};">适合人群:</strong>
|
|
96
172
|
</p>
|
|
97
173
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
98
|
-
<span
|
|
174
|
+
<span>✅ 创业者/管理者——理解系统脆弱性和反脆弱设计<br />✅ 对个人成长感兴趣的读者——掌握从压力中受益的方法<br />✅ 对决策科学感兴趣的读者——理解非线性风险的本质</span>
|
|
99
175
|
</p>
|
|
100
176
|
|
|
101
177
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 8px;text-indent:2em;">
|
|
102
178
|
<strong style="color:{T};">不适合人群:</strong>
|
|
103
179
|
</p>
|
|
104
180
|
<p style="font-size:16px;line-height:2;color:{BODY};margin:0 0 16px;text-indent:2em;">
|
|
105
|
-
<span
|
|
181
|
+
<span>🚫 已读过类似书籍的人——内容高度重复<br />🚫 讨厌抽象论证的人——本书充满概念性思维<br />🚫 寻找具体操作手册的人——本书偏哲学思辨</span>
|
|
106
182
|
</p>
|
|
107
183
|
|
|
108
|
-
|
|
184
|
+
<!-- 结尾页:主题配图 + 书名 + 作者 + 核心总结 -->
|
|
185
|
+
<p style="margin:0 0 16px;text-align:center;">
|
|
186
|
+
<img src="{THEME_IMG_END}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="主题配图"/>
|
|
187
|
+
</p>
|
|
109
188
|
|
|
110
|
-
<p style="font-size:
|
|
111
|
-
|
|
189
|
+
<p style="font-size:14px;color:#ccc;text-align:center;letter-spacing:8px;margin:24px 0 16px;">· · · · · ·</p>
|
|
190
|
+
|
|
191
|
+
<p style="font-size:18px;font-weight:bold;color:{T};text-align:center;margin:0 0 8px;line-height:1.6;">
|
|
192
|
+
<span>📖 《书名》</span>
|
|
193
|
+
</p>
|
|
194
|
+
<p style="font-size:14px;color:{BODY};text-align:center;margin:0 0 4px;line-height:1.6;">
|
|
195
|
+
<span>作者 · 读书笔记</span>
|
|
112
196
|
</p>
|
|
113
|
-
<p style="font-size:14px;color
|
|
114
|
-
<span
|
|
197
|
+
<p style="font-size:14px;color:{T2};text-align:center;margin:0 0 24px;line-height:1.8;font-weight:bold;">
|
|
198
|
+
<span>「 一句话总结:核心洞察 」</span>
|
|
115
199
|
</p>
|
|
116
200
|
|
|
117
201
|
</section>"""
|
|
@@ -122,22 +206,19 @@ def main():
|
|
|
122
206
|
print("微信公众号 - 书名 发布")
|
|
123
207
|
print("=" * 50)
|
|
124
208
|
|
|
125
|
-
# 1. 加载配置和获取token
|
|
126
209
|
config = load_config()
|
|
127
210
|
token = get_access_token(config)
|
|
128
211
|
print("\n✓ Token 已获取\n")
|
|
129
212
|
|
|
130
|
-
# 2. 上传封面图
|
|
131
213
|
cover_900_path = os.path.join(COVER_DIR, "cover_900x500.jpg")
|
|
132
214
|
if not os.path.exists(cover_900_path):
|
|
133
215
|
print(f"错误: 封面图不存在 {cover_900_path}", file=sys.stderr)
|
|
134
|
-
print("
|
|
216
|
+
print("请先运行主题贴图下载与封面裁剪流程", file=sys.stderr)
|
|
135
217
|
sys.exit(1)
|
|
136
218
|
|
|
137
219
|
print("上传封面图...")
|
|
138
220
|
cover_mid = upload_cover(token, cover_900_path)
|
|
139
221
|
|
|
140
|
-
# 3. 创建草稿
|
|
141
222
|
print("\n创建草稿...")
|
|
142
223
|
draft_id = create_draft(token, TITLE, AUTHOR, DIGEST, HTML, cover_mid)
|
|
143
224
|
|
|
Binary file
|
|
Binary file
|
package/scripts/book_cover.py
CHANGED
|
@@ -97,7 +97,12 @@ def fetch_book_cover_douban(book_title, author=""):
|
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
def fetch_book_cover(book_title, author="", isbn=""):
|
|
100
|
-
"""
|
|
100
|
+
"""按优先级尝试获取书籍封面(书籍原图),最后回退到占位图
|
|
101
|
+
|
|
102
|
+
注意:此函数返回的是书籍原图,仅用于 article 内首图占位。
|
|
103
|
+
公众号封面(cover_900x500.jpg)现在改用主题贴图,与正文风格统一。
|
|
104
|
+
如需主题封面,请调用 fetch_theme_cover()。
|
|
105
|
+
"""
|
|
101
106
|
cover_url = None
|
|
102
107
|
|
|
103
108
|
print(f"尝试从 Google Books 获取《{book_title}》封面...")
|
|
@@ -112,15 +117,72 @@ def fetch_book_cover(book_title, author="", isbn=""):
|
|
|
112
117
|
cover_url = fetch_book_cover_douban(book_title, author)
|
|
113
118
|
|
|
114
119
|
if not cover_url:
|
|
115
|
-
print("⚠️
|
|
120
|
+
print("⚠️ 三个数据源均未返回书籍封面,回退到占位图")
|
|
116
121
|
cover_url = "https://images.pexels.com/photos/159711/books-bookstore-book-reading-159711.jpeg?auto=compress&cs=tinysrgb&w=600"
|
|
117
122
|
|
|
118
|
-
print(f"✓
|
|
123
|
+
print(f"✓ 书籍封面URL: {cover_url}")
|
|
119
124
|
return cover_url
|
|
120
125
|
|
|
121
126
|
|
|
127
|
+
# 每个主题对应 3 张精选的 900x500 Pexels 封面图(fit=crop 自动精确裁切)
|
|
128
|
+
# 选用宽高比接近 1.8 的高质量原图,避免裁切损失重要内容
|
|
129
|
+
THEME_COVER_CANDIDATES = {
|
|
130
|
+
"abstract": [1108572, 1762851, 3109808],
|
|
131
|
+
"books": [256450, 590493, 762687],
|
|
132
|
+
"nature": [2387873, 2387874, 2387876],
|
|
133
|
+
"technology": [3568520, 1181244, 546819],
|
|
134
|
+
"business": [3184292, 210607, 3184296],
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _build_cover_url(pid, w=900, h=500):
|
|
139
|
+
"""构造 Pexels CDN URL,含精确 900x500 fit=crop 裁切参数"""
|
|
140
|
+
return f"https://images.pexels.com/photos/{pid}/pexels-photo-{pid}.jpeg?auto=compress&cs=tinysrgb&w={w}&h={h}&fit=crop"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def fetch_theme_cover(theme="abstract", seed=0):
|
|
144
|
+
"""从 Pexels CDN 获取一张与主题对应的 900x500 精确封面 URL(不裁剪原图)"""
|
|
145
|
+
candidates = THEME_COVER_CANDIDATES.get(theme, THEME_COVER_CANDIDATES["abstract"])
|
|
146
|
+
pid = candidates[seed % len(candidates)]
|
|
147
|
+
url = _build_cover_url(pid)
|
|
148
|
+
print(f"✓ 主题封面URL ({theme}, pid={pid}): {url}")
|
|
149
|
+
return url
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def download_cover_900x500(theme="abstract", output_dir="/tmp", seed=0):
|
|
153
|
+
"""下载一张 900x500 的主题封面图(Pexels 服务端精确裁剪,无需本地处理)
|
|
154
|
+
|
|
155
|
+
返回: cover_900x500.jpg 绝对路径
|
|
156
|
+
"""
|
|
157
|
+
ctx = ssl.create_default_context()
|
|
158
|
+
ctx.check_hostname = False
|
|
159
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
160
|
+
|
|
161
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
162
|
+
cover_900_path = os.path.join(output_dir, "cover_900x500.jpg")
|
|
163
|
+
|
|
164
|
+
candidates = THEME_COVER_CANDIDATES.get(theme, THEME_COVER_CANDIDATES["abstract"])
|
|
165
|
+
# 顺序尝试每个候选 PID,跳过 404
|
|
166
|
+
for i, pid in enumerate(candidates):
|
|
167
|
+
idx = (seed + i) % len(candidates)
|
|
168
|
+
pid_try = candidates[idx]
|
|
169
|
+
url = _build_cover_url(pid_try)
|
|
170
|
+
try:
|
|
171
|
+
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
|
172
|
+
data = urllib.request.urlopen(req, context=ctx, timeout=15).read()
|
|
173
|
+
with open(cover_900_path, "wb") as f:
|
|
174
|
+
f.write(data)
|
|
175
|
+
print(f"✓ 公众号封面已下载 ({theme}, pid={pid_try}): {cover_900_path}")
|
|
176
|
+
return cover_900_path
|
|
177
|
+
except Exception as e:
|
|
178
|
+
print(f" 候选 pid={pid_try} 失败: {e}")
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
raise RuntimeError(f"主题 {theme} 的所有候选封面均下载失败")
|
|
182
|
+
|
|
183
|
+
|
|
122
184
|
def download_and_process_cover(cover_url, output_dir):
|
|
123
|
-
"""
|
|
185
|
+
"""从主题贴图URL下载并处理为 600px 文章封面(兼容旧调用)"""
|
|
124
186
|
from PIL import Image
|
|
125
187
|
|
|
126
188
|
ctx = ssl.create_default_context()
|
|
@@ -131,7 +193,7 @@ def download_and_process_cover(cover_url, output_dir):
|
|
|
131
193
|
data = urllib.request.urlopen(req, context=ctx, timeout=15).read()
|
|
132
194
|
|
|
133
195
|
os.makedirs(output_dir, exist_ok=True)
|
|
134
|
-
original_path = os.path.join(output_dir, "
|
|
196
|
+
original_path = os.path.join(output_dir, "theme_cover_original.jpg")
|
|
135
197
|
with open(original_path, "wb") as f:
|
|
136
198
|
f.write(data)
|
|
137
199
|
|
|
@@ -139,32 +201,17 @@ def download_and_process_cover(cover_url, output_dir):
|
|
|
139
201
|
w, h = img.size
|
|
140
202
|
if w > 600:
|
|
141
203
|
ratio = 600 / w
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
img = img.resize((new_w, new_h), Image.LANCZOS)
|
|
204
|
+
img = img.resize((600, int(h * ratio)), Image.LANCZOS)
|
|
205
|
+
img.save(os.path.join(output_dir, "theme_cover_600.jpg"), "JPEG", quality=90)
|
|
145
206
|
|
|
146
|
-
|
|
147
|
-
|
|
207
|
+
print(f"✓ 主题封面(600px)已保存: theme_cover_600.jpg")
|
|
208
|
+
return os.path.join(output_dir, "theme_cover_600.jpg"), None
|
|
148
209
|
|
|
149
|
-
w, h = img.size
|
|
150
|
-
target_ratio = 900 / 500
|
|
151
|
-
if w / h > target_ratio:
|
|
152
|
-
new_w = int(h * target_ratio)
|
|
153
|
-
left = (w - new_w) // 2
|
|
154
|
-
img = img.crop((left, 0, left + new_w, h))
|
|
155
|
-
else:
|
|
156
|
-
new_h = int(w / target_ratio)
|
|
157
|
-
top = (h - new_h) // 2
|
|
158
|
-
img = img.crop((0, top, w, top + new_h))
|
|
159
|
-
img = img.resize((900, 500), Image.LANCZOS)
|
|
160
|
-
|
|
161
|
-
cover_900_path = os.path.join(output_dir, "cover_900x500.jpg")
|
|
162
|
-
img.save(cover_900_path, "JPEG", quality=90)
|
|
163
210
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return
|
|
211
|
+
def crop_cover_from_local(source_path, output_dir):
|
|
212
|
+
"""兼容旧 API:建议改用 download_cover_900x500()"""
|
|
213
|
+
print("⚠️ crop_cover_from_local 已弃用,请改用 download_cover_900x500(theme=...)")
|
|
214
|
+
return None
|
|
168
215
|
|
|
169
216
|
|
|
170
217
|
if __name__ == "__main__":
|
package/scripts/publish.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
3
|
微信公众号读书拆解发布脚本
|
|
4
|
-
|
|
4
|
+
完整流程:准备主题贴图 -> 裁剪公众号封面 -> 上传图片 -> 创建草稿
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
@@ -12,7 +12,7 @@ import sys
|
|
|
12
12
|
# 添加当前目录到模块搜索路径
|
|
13
13
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
14
14
|
|
|
15
|
-
from book_cover import
|
|
15
|
+
from book_cover import download_cover_900x500
|
|
16
16
|
from image_downloader import download_theme_images
|
|
17
17
|
from wechat_api import (
|
|
18
18
|
load_config,
|
|
@@ -23,34 +23,12 @@ from wechat_api import (
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def
|
|
27
|
-
"""
|
|
28
|
-
cover_900_path = os.path.join(cover_dir, "cover_900x500.jpg")
|
|
29
|
-
book_cover_path = os.path.join(cover_dir, "book_cover.jpg")
|
|
30
|
-
|
|
31
|
-
if os.path.exists(cover_900_path) and os.path.exists(book_cover_path):
|
|
32
|
-
print("✓ 封面图已存在,跳过获取")
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
if not book_title:
|
|
36
|
-
print(
|
|
37
|
-
f"⚠️ 缺少封面图且未提供 --book-title,无法自动获取。\n"
|
|
38
|
-
f" 期望路径: {cover_900_path}",
|
|
39
|
-
file=sys.stderr,
|
|
40
|
-
)
|
|
41
|
-
sys.exit(1)
|
|
26
|
+
def ensure_theme_images(cover_dir, theme="abstract", count=6):
|
|
27
|
+
"""确保主题插图存在,缺失则自动下载
|
|
42
28
|
|
|
43
|
-
|
|
29
|
+
默认 6 张:用于正文穿插(5 张)+ 结尾页(1 张)= 6 张
|
|
30
|
+
"""
|
|
44
31
|
os.makedirs(cover_dir, exist_ok=True)
|
|
45
|
-
cover_url = fetch_book_cover(book_title, book_author, book_isbn)
|
|
46
|
-
if not cover_url:
|
|
47
|
-
print("错误: 无法获取书籍封面URL", file=sys.stderr)
|
|
48
|
-
sys.exit(1)
|
|
49
|
-
download_and_process_cover(cover_url, cover_dir)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def ensure_theme_images(cover_dir, theme="abstract", count=5):
|
|
53
|
-
"""确保主题插图存在,缺失则自动下载"""
|
|
54
32
|
existing = [
|
|
55
33
|
f
|
|
56
34
|
for f in os.listdir(cover_dir)
|
|
@@ -64,6 +42,21 @@ def ensure_theme_images(cover_dir, theme="abstract", count=5):
|
|
|
64
42
|
download_theme_images(theme, cover_dir, count)
|
|
65
43
|
|
|
66
44
|
|
|
45
|
+
def ensure_cover_image(cover_dir, theme="abstract", seed=0):
|
|
46
|
+
"""确保公众号封面(900x500)存在,缺失则下载一张对应主题的精确尺寸封面
|
|
47
|
+
|
|
48
|
+
注意:不再从主题插图裁剪(裁剪可能损失画面重要内容),
|
|
49
|
+
而是从 Pexels 直接请求 900x500 精确尺寸的对应主题图片。
|
|
50
|
+
"""
|
|
51
|
+
cover_900_path = os.path.join(cover_dir, "cover_900x500.jpg")
|
|
52
|
+
if os.path.exists(cover_900_path):
|
|
53
|
+
print("✓ 公众号封面已存在,跳过下载")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
print(f"封面图缺失,下载一张 {theme} 主题的 900x500 封面...")
|
|
57
|
+
download_cover_900x500(theme, cover_dir, seed=seed)
|
|
58
|
+
|
|
59
|
+
|
|
67
60
|
def publish_article(
|
|
68
61
|
title,
|
|
69
62
|
author,
|
|
@@ -71,41 +64,39 @@ def publish_article(
|
|
|
71
64
|
content_html,
|
|
72
65
|
cover_dir,
|
|
73
66
|
theme="abstract",
|
|
74
|
-
image_count=
|
|
75
|
-
|
|
76
|
-
book_author="",
|
|
77
|
-
book_isbn="",
|
|
67
|
+
image_count=6,
|
|
68
|
+
cover_seed=0,
|
|
78
69
|
):
|
|
79
|
-
"""
|
|
70
|
+
"""完整发布流程:准备主题贴图 -> 下载公众号封面 -> 上传图片 -> 创建草稿"""
|
|
80
71
|
print("=" * 50)
|
|
81
72
|
print("微信公众号 - 书评发布")
|
|
82
73
|
print("=" * 50)
|
|
83
74
|
|
|
84
75
|
os.makedirs(cover_dir, exist_ok=True)
|
|
85
76
|
|
|
86
|
-
# 1.
|
|
87
|
-
print("\n[1/4]
|
|
88
|
-
ensure_book_cover(cover_dir, book_title, book_author, book_isbn)
|
|
89
|
-
|
|
90
|
-
# 2. 确保主题插图存在
|
|
91
|
-
print("\n[2/4] 准备主题插图...")
|
|
77
|
+
# 1. 准备主题贴图
|
|
78
|
+
print("\n[1/4] 准备主题贴图...")
|
|
92
79
|
ensure_theme_images(cover_dir, theme, image_count)
|
|
93
80
|
|
|
94
|
-
#
|
|
81
|
+
# 2. 准备公众号封面(900x500 精确尺寸)
|
|
82
|
+
print("\n[2/4] 准备公众号封面...")
|
|
83
|
+
ensure_cover_image(cover_dir, theme, seed=cover_seed)
|
|
84
|
+
|
|
85
|
+
# 3. 加载配置和获取 token
|
|
95
86
|
config = load_config()
|
|
96
87
|
token = get_access_token(config)
|
|
97
88
|
print("\n✓ Token 已获取\n")
|
|
98
89
|
|
|
99
|
-
# 4.
|
|
90
|
+
# 4. 上传公众号封面
|
|
100
91
|
cover_900_path = os.path.join(cover_dir, "cover_900x500.jpg")
|
|
101
92
|
if not os.path.exists(cover_900_path):
|
|
102
93
|
print(f"错误: 封面图不存在 {cover_900_path}", file=sys.stderr)
|
|
103
94
|
sys.exit(1)
|
|
104
95
|
|
|
105
|
-
print("
|
|
96
|
+
print("上传公众号封面...")
|
|
106
97
|
cover_mid = upload_cover(token, cover_900_path)
|
|
107
98
|
|
|
108
|
-
# 5.
|
|
99
|
+
# 5. 上传内容图片(img_*.jpg)
|
|
109
100
|
print("\n上传内容图片...")
|
|
110
101
|
image_urls = {}
|
|
111
102
|
for fname in sorted(os.listdir(cover_dir)):
|
|
@@ -116,14 +107,7 @@ def publish_article(
|
|
|
116
107
|
name = os.path.splitext(fname)[0]
|
|
117
108
|
image_urls[name] = url
|
|
118
109
|
|
|
119
|
-
# 6.
|
|
120
|
-
book_cover_path = os.path.join(cover_dir, "book_cover.jpg")
|
|
121
|
-
if os.path.exists(book_cover_path):
|
|
122
|
-
book_cover_url = upload_content_image(token, book_cover_path)
|
|
123
|
-
if book_cover_url:
|
|
124
|
-
image_urls["book_cover"] = book_cover_url
|
|
125
|
-
|
|
126
|
-
# 7. 保存上传结果
|
|
110
|
+
# 6. 保存上传结果
|
|
127
111
|
result_path = os.path.join(cover_dir, "upload_result.json")
|
|
128
112
|
with open(result_path, "w") as f:
|
|
129
113
|
json.dump(
|
|
@@ -137,7 +121,7 @@ def publish_article(
|
|
|
137
121
|
)
|
|
138
122
|
print(f"\n✓ 上传结果已保存: {result_path}")
|
|
139
123
|
|
|
140
|
-
#
|
|
124
|
+
# 7. 创建草稿
|
|
141
125
|
print("\n创建草稿...")
|
|
142
126
|
draft_id = create_draft(token, title, author, digest, content_html, cover_mid)
|
|
143
127
|
|
|
@@ -161,10 +145,15 @@ def main():
|
|
|
161
145
|
parser.add_argument("--content-file", required=True, help="HTML内容文件路径")
|
|
162
146
|
parser.add_argument("--cover-dir", required=True, help="封面和图片目录")
|
|
163
147
|
parser.add_argument("--theme", default="abstract", help="图片主题")
|
|
164
|
-
parser.add_argument(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
parser.add_argument(
|
|
148
|
+
parser.add_argument(
|
|
149
|
+
"--image-count", type=int, default=6, help="主题插图数量(默认6:5正文+1结尾)"
|
|
150
|
+
)
|
|
151
|
+
parser.add_argument(
|
|
152
|
+
"--cover-seed",
|
|
153
|
+
type=int,
|
|
154
|
+
default=0,
|
|
155
|
+
help="封面图候选序号(0/1/2,相同主题下可轮换)",
|
|
156
|
+
)
|
|
168
157
|
args = parser.parse_args()
|
|
169
158
|
|
|
170
159
|
with open(args.content_file, "r", encoding="utf-8") as f:
|
|
@@ -178,9 +167,7 @@ def main():
|
|
|
178
167
|
args.cover_dir,
|
|
179
168
|
args.theme,
|
|
180
169
|
args.image_count,
|
|
181
|
-
args.
|
|
182
|
-
args.book_author,
|
|
183
|
-
args.book_isbn,
|
|
170
|
+
args.cover_seed,
|
|
184
171
|
)
|
|
185
172
|
|
|
186
173
|
|