1仕組みと動作原理
独自にブラウザを立ち上げるのではなく「ユーザーがログイン済みの Chrome を借用する」ことが中核の前提条件です。Gemini セッションには通常 Google の既存ログイン状態が必要となるため、最初のステップでシークレットモード等の新規ブラウザを排除します。
1. Use the Chrome plugin, not a fresh unauthenticated browser, because Gemini conversations usually require the user's existing Google session.
スクリプトはホストの browser 機能経由で開いている全タブを列挙し、URL または(gemini.google.com + タイトルに Gemini を含む)の複合条件でマッチング後、user.claimTab で対象タブを確保します。一致しない場合は即座にエラーをスローし、自発的に新規ページを開くことはありません。
return await browser.user.claimTab(matches[0]);
大判画像を取得するにはライトボックスの展開が必須です。Gemini はプレビュー時のみ大きな blob: 画像を公開するため、スクリプトはボタンを1つずつクリックし、img.currentSrc が新しい blob: に変わり、かつ naturalWidth/naturalHeight が共に 0 より大きくなるまでポーリング待機します。
info.src.startsWith("blob:") &&
ディスクへのダウンロード完了後に連番へリネームします。まず blob URL 末尾から UUID 正規表現で識別子を抽出し、Downloads ディレクトリから uuid+拡張子(または最新の mtime かつ mtime >= 開始時刻-2s)でダウンロード直後のファイルを特定して、copyFile で image_01.jpg のような2桁の連番名に複製・改名します。
const outputName = `image_${pad2(index + 1)}${normalizeExtension(downloadedPath)}`;
アーカイブ化は独立した bash スクリプトで行われ、二重の検証機能を備えています。まず zip -T で整合性をテストし、続いてディレクトリ内の image_NN の数と圧縮ファイル内のエントリ数を照合し、不一致の場合は非ゼロで終了します。
echo "zip image count mismatch: directory=$image_count zip=$zip_count" >&2
ループ内には位置ずれ防止の2重のガードレールがあります。各画像の処理前に ESC キーを押して古いライトボックスを閉じ、毎回ボタン総数を再集計して初期数量と合致しない場合は即座にエラーをスローします。これにより、ページの非同期読み込みによって N 番目のボタンが別の画像を指してしまう事態を防ぎます。
throw new Error(`Gemini image button count changed from ${initialCount} to ${currentCount}`);
フォールバックパスでは、ホスト側タブの pageAssets 機能を用いて URL プレフィックスで画像をフィルタリングしてパッケージ化しますが、画質の劣化を明記した上でユーザーへその旨を通知することを義務付けています。
var uploaded = inventory.assets.filter(a => a.kind === "image" && a.url.includes("lh3.googleusercontent.com/gg/"));
実行ごとにページタイトル、ページ URL、出力ディレクトリ、各画像の連番/元のダウンロードパス/ピクセルサイズを記録した manifest.json を生成し、成果物の追跡を可能にします。
const manifestPath = path.join(outputDir, "manifest.json");
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| api | 宿主 Chrome 插件能力 browser(user.openTabs / user.claimTab) |
| api | 宿主 node_repl(homeDir、在同一 JS 会话里 import 脚本) |
| api | 宿主标签页 pageAssets 能力(降级路径) |
| network | gemini.google.com(默认标签页匹配串,实际流量由用户浏览器发出) |
| network | lh3.googleusercontent.com(降级路径按该 CDN 前缀筛图片资产) |
| cli | zip / zipinfo(打包与条目校验) |
| cli | find / wc / grep(图片计数与条目数比对) |
| api | 宿主 CUA 按键能力 tab.cua.keypress(发 ESC 关灯箱) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: download-gemini-images/scripts/download_gemini_images.mjs(ホスト側の node_repl J
- 网络端点: 外部ネットワークアクセスの宣言: gemini.google.com(ユーザーのブラウザを同サイトへアクセス/滞在させる目的。スクリプトは URL マッチングのみを行い、実際のリクエストはブラウザが送信)
- 本地工作目录: ファイル I/O と成果物出力: Chrome ダウンロードディレクトリ(デフォルト ~/Downloads):ブラウザ自身が出力する <uuid>.jpg/.png/.webp の中間ファイル(スクリプトは読み取りのみで削除は行わない)
5二重目の独立検証
- [ok] 外部依赖:宿主 browser 能力真实存在(不是推测) — mjs:124 return await browser.user.claimTab(matches[0]); 与 mjs:118 await browser.user.openTabs() 是真实调用点;SKILL.md 第 3 步要求 import 后在 node_repl 里跑,两处对得上。
- [ok] 外部依赖:zip / zipinfo / find / wc 调用点 — package_images.sh 内 zip -qr、zip -T、zipinfo -1、find ... -iname 'image_*.jpg'、wc -l 均实读命中,脚本是真的会跑这些二进制,不是文档口号。
- [ok] 网络端点数量:只有 Google 两个域名 — 全目录域名级检索仅 gemini.google.com 与 lh3.googleusercontent.com;无第三方 API、无 webhook、无 telemetry。注意 mjs 中出现的 blob: 是页面内对象 URL,不是网络端点。
- [discrepancy] 凭证面:是否真的不碰 cookie(找反例) — 〔第二遍标注:措辞需精确〕反例检索结果:源码中确实没有任何 document.cookie / chrome.cookies / localStorage 访问,SKILL.md 也明文禁止。但『不读凭证』不等于『不使用凭证态』——claimTab 借用的正是用户已登录会话。第一遍若把此条简单写成「无凭证接触」会掩盖真实权限面,故在 credential_reads 中改写为『无凭证文件读取,但运行于凭证态上下文』。
- [ok] 『绝不自开新浏览器』是否成立 — claimGeminiTab 在 matches.length === 0 时抛错(No open Gemini tab matched),downloadGeminiImagesFromChrome 中 options.tab 与 claimGeminiTab 二选一,无 puppeteer.launch 之类的建窗代码,与 SKILL.md『claim the already-open Gemini tab』一致。
- [ok] 输出完整性校验是否真做了(非文档承诺) — package_images.sh: zip -T 有实调用,且 image_count 与 zip_count 不等时 exit 1;mjs 侧有 initialCount 校验、按钮数不变量、selectedCount !== 1 抛错——三道都是代码而非描述。
- [ok] expectedCount 语义是否被夸大 — description 未提 expectedCount;SKILL.md 只说『Set `expectedCount` only when the user gave a specific count』,代码里 initialCount !== expectedCount 时抛错,与文档一致,无越界承诺。
- [ok] 元数据(license/stars/last_push) — 取自同 repo 同 commit 的池内既有值(与 auto-repo-setup.json / bilibili-source.json 一致:MIT、1385★、2026-09-09T12:33:29Z);本次未联网复核 gh。
6結論
2c75fce87072dc44…