Skip to content

Commit db782d6

Browse files
committed
fix(image-skill): 只下载最新生成块的最终图
- 禁止全页扫描历史 estuary 图片 - 必须等待下载按钮出现 - 要求 img.complete + naturalWidth>=1024 - 连续两次检查 src 稳定后才下载 - 优先使用 currentSrc
1 parent 4f78b30 commit db782d6

1 file changed

Lines changed: 70 additions & 7 deletions

File tree

  • skills/contentpipe-chatgpt-browser

skills/contentpipe-chatgpt-browser/SKILL.md

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,26 +75,89 @@ description: 通过 OpenClaw 浏览器插件操作 ChatGPT 进行 DALL-E 图片
7575
2. `evaluate` 输入 prompt → 点击发送
7676
3. 等 15-30 秒 → `screenshot` 查看结果
7777

78-
### 3. 📥 图片下载(已验证方案
78+
### 3. 📥 图片下载(强化版:只下当前这轮的最终图
7979

80-
**流程**:浏览器 fetch → 导出 cookies → WSL curl 下载
80+
**核心原则:不要扫整页历史图片;只下载“最新一轮生成”的最终图。**
81+
82+
常见误区:
83+
- 看到 `img[src*=estuary]` 就立刻下载 ❌
84+
- 全页 `querySelectorAll('article img')`,结果拿到旧图/历史图 ❌
85+
- 下载按钮还没出现就抓 URL ❌
86+
87+
**只有满足以下 3 条,才允许下载:**
88+
1. 当前最新 assistant 图片块里已经出现 **`下载此图片`** 按钮
89+
2. 该块中的图片 `img.complete === true``naturalWidth >= 1024`
90+
3. 同一张图的 `currentSrc/src` 连续两次检查一致(间隔 3-5 秒)
91+
92+
**流程**:定位最新图片块 → 等最终态 → 提取该块图片 URL → 导出 cookies → WSL curl 下载
8193

8294
```javascript
83-
// Step 1: 获取所有生成图片的 URL
84-
(async function(){ var imgs = document.querySelectorAll('article img[src*="estuary"]'); var urls = []; var seen = {}; for(var i=0;i<imgs.length;i++){var s=imgs[i].src; if(!seen[s]){seen[s]=true; urls.push(s)}} return JSON.stringify(urls)})()
95+
// Step 1: 只定位“最新一个带图片的 assistant 块”
96+
(() => {
97+
const articles = Array.from(document.querySelectorAll('main article'));
98+
const blocks = articles.map((article, idx) => {
99+
const imgs = Array.from(article.querySelectorAll('img[src*="estuary"], img[src*="oaiusercontent"], img[src*="backend-api/estuary"]'));
100+
const downloadBtns = Array.from(article.querySelectorAll('button')).filter(b => /下载此图片|download/i.test((b.getAttribute('aria-label')||'') + ' ' + (b.innerText||'')));
101+
return {
102+
idx,
103+
imgCount: imgs.length,
104+
downloadCount: downloadBtns.length,
105+
imgs: imgs.map(img => ({
106+
src: img.currentSrc || img.src || '',
107+
complete: !!img.complete,
108+
naturalWidth: img.naturalWidth || 0,
109+
naturalHeight: img.naturalHeight || 0,
110+
})),
111+
};
112+
}).filter(x => x.imgCount > 0);
113+
return JSON.stringify(blocks[blocks.length - 1] || null);
114+
})()
85115

86-
// Step 2: 获取 cookies
116+
// Step 2: 最终态检查(必须满足 downloadCount >= 1 / complete / naturalWidth>=1024)
117+
(() => {
118+
const articles = Array.from(document.querySelectorAll('main article'));
119+
const article = [...articles].reverse().find(a => a.querySelector('img[src*="estuary"], img[src*="oaiusercontent"], img[src*="backend-api/estuary"]'));
120+
if (!article) return JSON.stringify({ok:false, reason:'no image article found'});
121+
const imgs = Array.from(article.querySelectorAll('img[src*="estuary"], img[src*="oaiusercontent"], img[src*="backend-api/estuary"]'));
122+
const downloadBtns = Array.from(article.querySelectorAll('button')).filter(b => /下载此图片|download/i.test((b.getAttribute('aria-label')||'') + ' ' + (b.innerText||'')));
123+
const ready = imgs.length > 0 && downloadBtns.length >= 1 && imgs.every(img => img.complete && (img.naturalWidth || 0) >= 1024);
124+
return JSON.stringify({
125+
ok: ready,
126+
imgCount: imgs.length,
127+
downloadCount: downloadBtns.length,
128+
imgs: imgs.map(img => ({src: img.currentSrc || img.src || '', w: img.naturalWidth || 0, h: img.naturalHeight || 0, complete: !!img.complete}))
129+
});
130+
})()
131+
132+
// Step 3: 若上一步 ok=true,再隔 3-5 秒重复一次,确认 src 稳定后取 URL 列表
133+
(() => {
134+
const articles = Array.from(document.querySelectorAll('main article'));
135+
const article = [...articles].reverse().find(a => a.querySelector('img[src*="estuary"], img[src*="oaiusercontent"], img[src*="backend-api/estuary"]'));
136+
if (!article) return JSON.stringify([]);
137+
const urls = Array.from(article.querySelectorAll('img[src*="estuary"], img[src*="oaiusercontent"], img[src*="backend-api/estuary"]'))
138+
.map(img => img.currentSrc || img.src || '')
139+
.filter(Boolean);
140+
return JSON.stringify([...new Set(urls)]);
141+
})()
142+
143+
// Step 4: 获取 cookies
87144
(function(){ return document.cookie })()
88145
```
89146

90147
```bash
91-
# Step 3: WSL curl 下载
92-
curl -sS -o output.png \
148+
# Step 5: WSL curl 下载
149+
curl -L --fail -sS -o output.png \
93150
-H "Cookie: <cookies>" \
94151
"<image_url>" \
95152
-x http://172.27.112.1:7890 # 代理(如需要)
96153
```
97154

155+
**强制规则:**
156+
- 如果当前最新图片块没有 `下载此图片` 按钮 → 继续等,不要下载
157+
- 如果提取到的是“整页很多历史图” → 说明你选错作用域了,改成“最后一个带图 article”
158+
- 只下载当前这轮生成所在块中的图,不要下载历史对话中的图
159+
- 优先使用 `currentSrc`,不要只读 `src`
160+
98161
**⚠️ 不要用这些方式下载**(均被 CSP 阻止):
99162
- `<a download>` + data URL ❌
100163
- `window.open(blobURL)` → 需手动保存 ⚠️

0 commit comments

Comments
 (0)