feat(gemini): 添加最新图片获取与下载功能
This commit is contained in:
12
SKILL.md
12
SKILL.md
@@ -49,10 +49,14 @@ Gemini 页面的操作按钮(`.send-button-container` 内)通过 `aria-label
|
|||||||
4. 将用户提示词原样输入。
|
4. 将用户提示词原样输入。
|
||||||
5. 发送后立即通知用户:正在绘图中。
|
5. 发送后立即通知用户:正在绘图中。
|
||||||
6. **分段轮询等待**(见下方"CDP 保活轮询策略",生图超时上限 120s)。
|
6. **分段轮询等待**(见下方"CDP 保活轮询策略",生图超时上限 120s)。
|
||||||
7. 结果出现后:
|
7. 结果出现后,调用 `GeminiOps.getLatestImage()` 获取最新生成的图片(Gemini 一次只生成一张):
|
||||||
- 优先用"下载原图"按钮获取原图。
|
- 返回 `{ok, src, alt, width, height, hasDownloadBtn}`。
|
||||||
- 若无下载按钮或失败,可对图片右键另存(通常是标清图)。
|
- 定位依据:`<img class="image loaded">` — 只有同时具有 `image` 和 `loaded` 两个 class 的才是已渲染完成的生成图片;DOM 中取最后一个即为最新。
|
||||||
8. 把图片返回用户;若有多张,按顺序全部回传。
|
- `src` 为 `https://lh3.googleusercontent.com/...` 格式的原图 URL。
|
||||||
|
- 若 `ok === false`,等几秒再调一次;连续两次失败则做 snapshot 排查页面状态。
|
||||||
|
- 若 `hasDownloadBtn: true`,可调用 `GeminiOps.downloadLatestImage()` 点击原图下载按钮。
|
||||||
|
- 下载按钮定位:从 `img` 向上找到 `.image-container` 容器,容器内的 `mat-icon[fonticon="download"]` 即为下载原图按钮。
|
||||||
|
8. 把图片返回用户。
|
||||||
|
|
||||||
## CDP 保活轮询策略
|
## CDP 保活轮询策略
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,63 @@
|
|||||||
|
|
||||||
## 4) 生图结果获取
|
## 4) 生图结果获取
|
||||||
|
|
||||||
优先顺序:
|
Gemini 一次只生成一张图片,流程上只关心**最新生成的那张**,历史图片不做处理。
|
||||||
1. 图片右上角"下载原图"
|
|
||||||
2. 右键另存为(标清)
|
|
||||||
|
|
||||||
下载到本地后再通过渠道回传。
|
调用 `GeminiOps.getLatestImage()` 获取最新一张生成图片。
|
||||||
|
|
||||||
|
### DOM 结构
|
||||||
|
|
||||||
|
```
|
||||||
|
<div class="image-container ...">
|
||||||
|
<button class="image-button ...">
|
||||||
|
<img class="image loaded" src="https://lh3.googleusercontent.com/..." alt="AI 生成">
|
||||||
|
</button>
|
||||||
|
<div class="button-icon-wrapper">
|
||||||
|
<mat-icon fonticon="download" data-mat-icon-name="download" class="button-icon ..."></mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 图片定位
|
||||||
|
|
||||||
|
- 选择器:`img.image.loaded`
|
||||||
|
- `image` class = Gemini 的图片元素
|
||||||
|
- `loaded` class = 图片已渲染完成(未加载完的不会有此 class)
|
||||||
|
- 两个 class 同时存在才算有效图片
|
||||||
|
- DOM 中可能存在多张历史图片,**取最后一个**即为最新生成
|
||||||
|
|
||||||
|
### 下载按钮定位
|
||||||
|
|
||||||
|
- 从 `img` 向上找到最近的 `.image-container` 祖先容器
|
||||||
|
- 在容器内查找 `mat-icon[fonticon="download"]`(即下载原图按钮)
|
||||||
|
- `getLatestImage()` 返回 `hasDownloadBtn` 字段标识是否有下载按钮
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
- `GeminiOps.getLatestImage()` → 获取最新一张图片信息
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"src": "https://lh3.googleusercontent.com/...",
|
||||||
|
"alt": "AI 生成",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
"hasDownloadBtn": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `GeminiOps.downloadLatestImage()` → 点击最新图片的下载原图按钮
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"ok": true, "src": "https://lh3.googleusercontent.com/..."}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回退
|
||||||
|
|
||||||
|
- `ok === false` → 页面可能还在渲染,等几秒再调一次
|
||||||
|
- 连续两次失败 → 做 snapshot 排查页面状态
|
||||||
|
- `hasDownloadBtn: false` → 回退到直接用 `src` URL 下载
|
||||||
|
|
||||||
## 5) 用户提示文案(建议)
|
## 5) 用户提示文案(建议)
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,67 @@
|
|||||||
return {status:s.status, label:s.label, pageVisible:!document.hidden, ts:Date.now()};
|
return {status:s.status, label:s.label, pageVisible:!document.hidden, ts:Date.now()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── 最新图片获取与下载 ──
|
||||||
|
* Gemini 一次只生成一张图片,流程上只关心最新生成的那张。
|
||||||
|
* DOM 中 img.image.loaded 按顺序排列,最后一个即为最新生成。
|
||||||
|
*
|
||||||
|
* DOM 结构:
|
||||||
|
* <div class="image-container ...">
|
||||||
|
* <button class="image-button ...">
|
||||||
|
* <img class="image loaded" src="https://lh3.googleusercontent.com/..." alt="AI 生成">
|
||||||
|
* </button>
|
||||||
|
* <div class="button-icon-wrapper">
|
||||||
|
* <mat-icon fonticon="download" data-mat-icon-name="download" ...></mat-icon>
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
|
||||||
|
function _findContainer(img){
|
||||||
|
var el=img;
|
||||||
|
while(el&&el!==document.body){
|
||||||
|
if(el.classList&&el.classList.contains('image-container')) return el;
|
||||||
|
el=el.parentElement;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _findDownloadBtn(container){
|
||||||
|
if(!container) return null;
|
||||||
|
return container.querySelector('mat-icon[fonticon="download"]')
|
||||||
|
|| container.querySelector('mat-icon[data-mat-icon-name="download"]')
|
||||||
|
|| null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取最新生成的一张图片信息(DOM 中最后一个 img.image.loaded) */
|
||||||
|
function getLatestImage(){
|
||||||
|
var imgs=[...document.querySelectorAll('img.image.loaded')];
|
||||||
|
if(!imgs.length) return {ok:false, error:'no_loaded_images'};
|
||||||
|
var img=imgs[imgs.length-1];
|
||||||
|
var container=_findContainer(img);
|
||||||
|
var dlBtn=_findDownloadBtn(container);
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
src: img.src||'',
|
||||||
|
alt: img.alt||'',
|
||||||
|
width: img.naturalWidth||0,
|
||||||
|
height: img.naturalHeight||0,
|
||||||
|
hasDownloadBtn: !!dlBtn
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 点击最新图片的"下载原图"按钮 */
|
||||||
|
function downloadLatestImage(){
|
||||||
|
var imgs=[...document.querySelectorAll('img.image.loaded')];
|
||||||
|
if(!imgs.length) return {ok:false, error:'no_loaded_images'};
|
||||||
|
var img=imgs[imgs.length-1];
|
||||||
|
var container=_findContainer(img);
|
||||||
|
var dlBtn=_findDownloadBtn(container);
|
||||||
|
if(!dlBtn) return {ok:false, error:'download_btn_not_found'};
|
||||||
|
var clickable=dlBtn.closest('button,[role="button"],.button-icon-wrapper')||dlBtn;
|
||||||
|
clickable.click();
|
||||||
|
return {ok:true, src:img.src||''};
|
||||||
|
}
|
||||||
|
|
||||||
function probe(){
|
function probe(){
|
||||||
var s=getStatus();
|
var s=getStatus();
|
||||||
return {
|
return {
|
||||||
@@ -103,5 +164,5 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
window.GeminiOps = {probe, click, fillPrompt, getStatus, pollStatus, selectors:S, version:'0.4.0'};
|
window.GeminiOps = {probe, click, fillPrompt, getStatus, pollStatus, getLatestImage, downloadLatestImage, selectors:S, version:'0.7.0'};
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user