wechat-media-writer 2.2.8 → 2.2.9
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 +4 -4
- package/package.json +1 -1
- package/references/publish_template.py +6 -6
- package/scripts/image_downloader.py +32 -12
- package/scripts/publish.py +7 -1
package/SKILL.md
CHANGED
|
@@ -140,7 +140,7 @@ Token 缓存在 `~/.wechat/token_cache.json`(自动管理,2小时有效期
|
|
|
140
140
|
|
|
141
141
|
<!-- 主题配图(第一张图,使用主题贴图,非书籍原图) -->
|
|
142
142
|
<p style="margin:0 0 20px;text-align:center;">
|
|
143
|
-
<img src="{THEME_IMG_1}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="
|
|
143
|
+
<img src="{THEME_IMG_1}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="" />
|
|
144
144
|
</p>
|
|
145
145
|
|
|
146
146
|
<!-- 章节(无数字编号,直接标题) -->
|
|
@@ -153,7 +153,7 @@ Token 缓存在 `~/.wechat/token_cache.json`(自动管理,2小时有效期
|
|
|
153
153
|
|
|
154
154
|
<!-- 主题配图(章节间穿插,与文风统一) -->
|
|
155
155
|
<p style="margin:0 0 20px;text-align:center;">
|
|
156
|
-
<img src="{THEME_IMG_2}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="
|
|
156
|
+
<img src="{THEME_IMG_2}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="" />
|
|
157
157
|
</p>
|
|
158
158
|
|
|
159
159
|
<!-- 金句引用块(圆角浅背景,无左侧竖条) -->
|
|
@@ -170,7 +170,7 @@ Token 缓存在 `~/.wechat/token_cache.json`(自动管理,2小时有效期
|
|
|
170
170
|
|
|
171
171
|
<!-- 主题配图(章节中段) -->
|
|
172
172
|
<p style="margin:0 0 20px;text-align:center;">
|
|
173
|
-
<img src="{THEME_IMG_3}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="
|
|
173
|
+
<img src="{THEME_IMG_3}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="" />
|
|
174
174
|
</p>
|
|
175
175
|
|
|
176
176
|
<!-- 分隔符(章节之间) -->
|
|
@@ -383,7 +383,7 @@ python3 scripts/publish.py --title "标题" --content-file content.html --cover-
|
|
|
383
383
|
|
|
384
384
|
<!-- 结尾页(与正文风格延续,使用主题色) -->
|
|
385
385
|
<p style="margin:0 0 16px;text-align:center;">
|
|
386
|
-
<img src="{THEME_IMG_END}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="
|
|
386
|
+
<img src="{THEME_IMG_END}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="" />
|
|
387
387
|
</p>
|
|
388
388
|
|
|
389
389
|
<p style="font-size:14px;color:#ccc;text-align:center;letter-spacing:8px;margin:24px 0 16px;">· · · · · ·</p>
|
package/package.json
CHANGED
|
@@ -61,7 +61,7 @@ HTML = f"""<section style="margin:0;padding:0 0 48px;font-family:-apple-system,B
|
|
|
61
61
|
|
|
62
62
|
<!-- 主题配图1(头部) -->
|
|
63
63
|
<p style="margin:0 0 20px;text-align:center;">
|
|
64
|
-
<img src="{THEME_IMG_1}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="
|
|
64
|
+
<img src="{THEME_IMG_1}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt=""/>
|
|
65
65
|
</p>
|
|
66
66
|
|
|
67
67
|
<!-- 先搞懂写书的这个人 -->
|
|
@@ -74,7 +74,7 @@ HTML = f"""<section style="margin:0;padding:0 0 48px;font-family:-apple-system,B
|
|
|
74
74
|
|
|
75
75
|
<!-- 主题配图2(先搞懂写书的这个人后) -->
|
|
76
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="
|
|
77
|
+
<img src="{THEME_IMG_2}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt=""/>
|
|
78
78
|
</p>
|
|
79
79
|
|
|
80
80
|
<!-- 金句引用块(圆角浅背景,无左侧竖条) -->
|
|
@@ -92,7 +92,7 @@ HTML = f"""<section style="margin:0;padding:0 0 48px;font-family:-apple-system,B
|
|
|
92
92
|
|
|
93
93
|
<!-- 主题配图3(逻辑导图后) -->
|
|
94
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="
|
|
95
|
+
<img src="{THEME_IMG_3}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt=""/>
|
|
96
96
|
</p>
|
|
97
97
|
|
|
98
98
|
<!-- 三个颠覆认知的真相 -->
|
|
@@ -111,7 +111,7 @@ HTML = f"""<section style="margin:0;padding:0 0 48px;font-family:-apple-system,B
|
|
|
111
111
|
|
|
112
112
|
<!-- 主题配图4(论点后) -->
|
|
113
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="
|
|
114
|
+
<img src="{THEME_IMG_4}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt=""/>
|
|
115
115
|
</p>
|
|
116
116
|
|
|
117
117
|
<!-- 这本书的短板,不吐不快 -->
|
|
@@ -137,7 +137,7 @@ HTML = f"""<section style="margin:0;padding:0 0 48px;font-family:-apple-system,B
|
|
|
137
137
|
|
|
138
138
|
<!-- 主题配图5(读完真正留下的东西后) -->
|
|
139
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="
|
|
140
|
+
<img src="{THEME_IMG_5}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt=""/>
|
|
141
141
|
</p>
|
|
142
142
|
|
|
143
143
|
<!-- 四条拿来就用的行动项 -->
|
|
@@ -195,7 +195,7 @@ HTML = f"""<section style="margin:0;padding:0 0 48px;font-family:-apple-system,B
|
|
|
195
195
|
|
|
196
196
|
<!-- 结尾页:主题配图 + 书名 + 作者 + 核心总结 -->
|
|
197
197
|
<p style="margin:0 0 16px;text-align:center;">
|
|
198
|
-
<img src="{THEME_IMG_END}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt="
|
|
198
|
+
<img src="{THEME_IMG_END}" style="width:100%;max-width:600px;display:block;margin:0 auto;border-radius:4px;" alt=""/>
|
|
199
199
|
</p>
|
|
200
200
|
|
|
201
201
|
<p style="font-size:14px;color:#ccc;text-align:center;letter-spacing:8px;margin:24px 0 16px;">· · · · · ·</p>
|
|
@@ -74,22 +74,35 @@ THEME_CANDIDATES = {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
def download_image(url, output_path):
|
|
78
|
-
"""
|
|
77
|
+
def download_image(url, output_path, retries=3, retry_delay=1):
|
|
78
|
+
"""下载图片到指定路径,带重试机制"""
|
|
79
|
+
import time
|
|
80
|
+
|
|
79
81
|
ctx = ssl.create_default_context()
|
|
80
82
|
ctx.check_hostname = False
|
|
81
83
|
ctx.verify_mode = ssl.CERT_NONE
|
|
82
84
|
|
|
83
85
|
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
87
|
+
|
|
88
|
+
for attempt in range(1, retries + 1):
|
|
89
|
+
try:
|
|
90
|
+
data = urllib.request.urlopen(req, context=ctx, timeout=20).read()
|
|
91
|
+
# 验证下载到的是真实图片(> 5KB 且 magic bytes 正确)
|
|
92
|
+
if len(data) < 5000:
|
|
93
|
+
raise ValueError(f"下载文件过小: {len(data)} bytes")
|
|
94
|
+
if not data[:3] in (b"\xff\xd8\xff", b"\x89PN", b"GIF"):
|
|
95
|
+
raise ValueError(f"非图片格式: {data[:8]!r}")
|
|
96
|
+
with open(output_path, "wb") as f:
|
|
97
|
+
f.write(data)
|
|
98
|
+
return True
|
|
99
|
+
except Exception as e:
|
|
100
|
+
if attempt < retries:
|
|
101
|
+
time.sleep(retry_delay * attempt)
|
|
102
|
+
continue
|
|
103
|
+
print(f"下载失败(重试 {retries} 次后放弃): {e}")
|
|
104
|
+
return False
|
|
105
|
+
return False
|
|
93
106
|
|
|
94
107
|
|
|
95
108
|
def download_pexels_image(pid, output_path, w=1200):
|
|
@@ -154,12 +167,14 @@ def aesthetics_score(image_path):
|
|
|
154
167
|
def download_theme_images(theme, output_dir, count=6):
|
|
155
168
|
"""根据主题下载多张精美图片
|
|
156
169
|
|
|
157
|
-
自动跳过 404
|
|
170
|
+
自动跳过 404/失败候选(带 3 次重试),下载到临时文件名,
|
|
171
|
+
再按美学评分重命名为 img_1.jpg ~ img_N.jpg
|
|
158
172
|
返回: 已下载图片路径列表(按美学评分降序)
|
|
159
173
|
"""
|
|
160
174
|
os.makedirs(output_dir, exist_ok=True)
|
|
161
175
|
candidates = THEME_CANDIDATES.get(theme, THEME_CANDIDATES["books"])
|
|
162
176
|
downloaded = [] # 临时文件列表
|
|
177
|
+
failed_pids = []
|
|
163
178
|
|
|
164
179
|
# 1) 全部下载到临时文件
|
|
165
180
|
for i, pid in enumerate(candidates):
|
|
@@ -168,8 +183,13 @@ def download_theme_images(theme, output_dir, count=6):
|
|
|
168
183
|
tmp_path = os.path.join(output_dir, f"_tmp_{theme}_{i}_{pid}.jpg")
|
|
169
184
|
if download_pexels_image(pid, tmp_path):
|
|
170
185
|
downloaded.append(tmp_path)
|
|
186
|
+
else:
|
|
187
|
+
failed_pids.append(pid)
|
|
171
188
|
|
|
189
|
+
if failed_pids:
|
|
190
|
+
print(f" ⚠️ 候选 {failed_pids} 下载失败(共 {len(failed_pids)} 张)")
|
|
172
191
|
if not downloaded:
|
|
192
|
+
print(f" ❌ 主题 {theme} 所有候选图下载失败")
|
|
173
193
|
return []
|
|
174
194
|
|
|
175
195
|
# 2) 按美学评分排序
|
package/scripts/publish.py
CHANGED
|
@@ -40,7 +40,13 @@ def ensure_theme_images(cover_dir, theme="books", count=6):
|
|
|
40
40
|
return
|
|
41
41
|
|
|
42
42
|
print(f"主题插图不足,自动下载 {count} 张(主题: {theme})...")
|
|
43
|
-
download_theme_images(theme, cover_dir, count)
|
|
43
|
+
result = download_theme_images(theme, cover_dir, count)
|
|
44
|
+
|
|
45
|
+
if len(result) < count:
|
|
46
|
+
print(
|
|
47
|
+
f"⚠️ 实际下载 {len(result)}/{count} 张,建议换其他主题或重试",
|
|
48
|
+
file=sys.stderr,
|
|
49
|
+
)
|
|
44
50
|
|
|
45
51
|
|
|
46
52
|
def ensure_cover_image(cover_dir):
|