ワークフロー&基本ツール · daymade/claude-code-skills

download-gemini-images

Google Gemini の会話/チャット/アプリ画面から画像(特にスレッド内に表示されるアップロード画像や生成画像のプレビュー)をダウンロード、エクスポート、保存、またはパッケージングします。ログイン済みの Chrome/Gemini 状態の利用、Gemini 画像ライトボックスの展開、拡大表示された画像ファイルのダウンロード、順序どおりの名前変更、および ZIP アーカイブの作成が必要なタスクで使用します。

L4 高権限 · 段階的認可と監査V1 コミット固定コマンド実行ネットワーク通信ファイル書き込みDual-pass Audit
Author daymadeGitHub daymade/claude-code-skills ↗Stars 1385License MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)Commit d5c4678cb5
管理レベルは Skill の能力と推奨統制措置を示すものであり、品質や安全性の認証、利用可否の判定ではありません。使用の可否はユーザーおよび管理者が決定します。Agent 環境によって制約が異なるため、自動保護を仮定しないでください。第三者製 Skill の著作権は原著作者に帰属し、利用リスクは利用者が負います。

1仕組みと動作原理

独自にブラウザを立ち上げるのではなく「ユーザーがログイン済みの Chrome を借用する」ことが中核の前提条件です。Gemini セッションには通常 Google の既存ログイン状態が必要となるため、最初のステップでシークレットモード等の新規ブラウザを排除します。

download-gemini-images/SKILL.md
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 で対象タブを確保します。一致しない場合は即座にエラーをスローし、自発的に新規ページを開くことはありません。

download-gemini-images/scripts/download_gemini_images.mjs
return await browser.user.claimTab(matches[0]);
同一関数の直前では matches.length === 0 の場合に throw new Error(`No open Gemini tab matched ${tabUrlIncludes}`) が実行され、直後では claimGeminiTab が downloadGeminiImagesFromChrome の options.tab のデフォルト値として使用されます。新規作成ではなく既存タブの確保(claim)方式を採ることで、ユーザーが閲覧中のセッションそのものを対象とすることを保証します。

大判画像を取得するにはライトボックスの展開が必須です。Gemini はプレビュー時のみ大きな blob: 画像を公開するため、スクリプトはボタンを1つずつクリックし、img.currentSrc が新しい blob: に変わり、かつ naturalWidth/naturalHeight が共に 0 より大きくなるまでポーリング待機します。

download-gemini-images/scripts/download_gemini_images.mjs
info.src.startsWith("blob:") &&
これは waitForSelectedImage の判定中核です(同一 if 内で naturalWidth > 0 && naturalHeight > 0 && (!previousSrc || info.src !== previousSrc) を要求)。previousBlobSrc で前回の画像を記録しておくことで、「ボタンは押したが画像がまだ切り替わっていない」段階で古い画像を取得する事態を防ぎます。SKILL.md の概要では設計意図として次のように記載されています: 『Prefer the lightbox image over thumbnail/page-preview assets because Gemini often exposes a larger `blob:` image only after the user opens a preview.』

ディスクへのダウンロード完了後に連番へリネームします。まず blob URL 末尾から UUID 正規表現で識別子を抽出し、Downloads ディレクトリから uuid+拡張子(または最新の mtime かつ mtime >= 開始時刻-2s)でダウンロード直後のファイルを特定して、copyFile で image_01.jpg のような2桁の連番名に複製・改名します。

download-gemini-images/scripts/download_gemini_images.mjs
const outputName = `image_${pad2(index + 1)}${normalizeExtension(downloadedPath)}`;
直前のコードは const uuid = imageInfo.src.match(/\/([0-9a-f-]{36})$/i)?.[1] || null; です。waitForDownloadedMedia は `${uuid}${ext}` による完全一致を優先し、該当しない場合は「ディレクトリ内の最新メディアファイル」にフォールバックします。整列された連番命名はスキルの明示的な仕様です(SKILL.md: 『use the script's ordered names: `image_01.jpg`, `image_02.jpg`, etc.』)。

アーカイブ化は独立した bash スクリプトで行われ、二重の検証機能を備えています。まず zip -T で整合性をテストし、続いてディレクトリ内の image_NN の数と圧縮ファイル内のエントリ数を照合し、不一致の場合は非ゼロで終了します。

download-gemini-images/scripts/package_images.sh
echo "zip image count mismatch: directory=$image_count zip=$zip_count" >&2
上記の image_count は find -maxdepth 1 -type f と image_*.jpg|jpeg|png|webp の組み合わせで取得され、zip_count は zipinfo -1 の出力をパイプで渡し正規表現 '/image_[0-9][0-9]\.(jpg|jpeg|png|webp)$' を grep して取得します。SKILL.md ではこの手順を次のように説明しています: 『The packaging script creates the ZIP, runs `zip -T`, and verifies that the archive contains the same ordered image files as the directory.』

ループ内には位置ずれ防止の2重のガードレールがあります。各画像の処理前に ESC キーを押して古いライトボックスを閉じ、毎回ボタン総数を再集計して初期数量と合致しない場合は即座にエラーをスローします。これにより、ページの非同期読み込みによって N 番目のボタンが別の画像を指してしまう事態を防ぎます。

download-gemini-images/scripts/download_gemini_images.mjs
throw new Error(`Gemini image button count changed from ${initialCount} to ${currentCount}`);
ここでの処理内容:「同一ページ上でのボタン順序の安定性」を不変条件(不変量)として検証しています。同様にループ外でも、ユーザーから expectedCount が指定されている場合に initialCount !== expectedCount であれば即座にエラーとし、過不足があるまま成功扱いとなるのを防ぎます。

フォールバックパスでは、ホスト側タブの pageAssets 機能を用いて URL プレフィックスで画像をフィルタリングしてパッケージ化しますが、画質の劣化を明記した上でユーザーへその旨を通知することを義務付けています。

download-gemini-images/SKILL.md
var uploaded = inventory.assets.filter(a => a.kind === "image" && a.url.includes("lh3.googleusercontent.com/gg/"));
直後の行には次のように記載されています: 『This usually captures 512px preview JPEGs, not the larger lightbox files. Tell the user when this fallback is used.』——フォールバックは警告なしにサイレント実行されるのではなく、応答メッセージ内への記載が求められます。

実行ごとにページタイトル、ページ URL、出力ディレクトリ、各画像の連番/元のダウンロードパス/ピクセルサイズを記録した manifest.json を生成し、成果物の追跡を可能にします。

download-gemini-images/scripts/download_gemini_images.mjs
const manifestPath = path.join(outputDir, "manifest.json");
同セクションでは writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8") でディスクへ保存し、pageTitle / pageUrl / outputDir / imageCount / files を呼び出し元へ返却します。

2コア機能

01ユーザーがログイン済みの Chrome 内で Gemini タブを確保(新規タブ作成なし、シークレットモード不使用)
02ライトボックスを順次開き大判画像を取得(サムネイルではなく blob: プレビュー画像)
03スレッドの出現順に沿って image_01.jpg / image_02.jpg … とリネーム
04expectedCount による厳格な数量検証(ユーザー指定の枚数と完全一致を要求)
05ZIP パッケージ化および整合性・エントリ数の二重検証
06画質劣化時の pageAssets フォールバック経路(低解像度である旨をユーザーへ明示)
07実行時の manifest.json 生成による監査性の担保
08明確なプライバシー境界:cookie / localStorage / パスワード / セッションストレージには一切アクセスしない

3外部依存関係

KindDependency / Endpoint
api宿主 Chrome 插件能力 browser(user.openTabs / user.claimTab)
api宿主 node_repl(homeDir、在同一 JS 会话里 import 脚本)
api宿主标签页 pageAssets 能力(降级路径)
networkgemini.google.com(默认标签页匹配串,实际流量由用户浏览器发出)
networklh3.googleusercontent.com(降级路径按该 CDN 前缀筛图片资产)
clizip / zipinfo(打包与条目校验)
clifind / wc / grep(图片计数与条目数比对)
api宿主 CUA 按键能力 tab.cua.keypress(发 ESC 关灯箱)

4推奨統制措置と判定根拠

安全運用のための推奨統制措置
Agent 向け
段階的・都度認可、サンドボックス推奨
管理者向け
承認フロー、ロールバック可能、全履歴監査
判定根拠(ファイルパスと抜粋)
  • scripts/: ローカルスクリプトまたはコマンドの実行: download-gemini-images/scripts/download_gemini_images.mjs(ホスト側の node_repl J
  • 网络端点: 外部ネットワークアクセスの宣言: gemini.google.com(ユーザーのブラウザを同サイトへアクセス/滞在させる目的。スクリプトは URL マッチングのみを行い、実際のリクエストはブラウザが送信)
  • 本地工作目录: ファイル I/O と成果物出力: Chrome ダウンロードディレクトリ(デフォルト ~/Downloads):ブラウザ自身が出力する <uuid>.jpg/.png/.webp の中間ファイル(スクリプトは読み取りのみで削除は行わない)
監査ノート: 標準基準に基づく判定:外部送信先は2件のみで完全に予測可能(gemini.google.com および lh3.googleusercontent.com。いずれも Google 自社のドメイン)であり、サードパーティへのコールバック、API キー、トラッキング等は存在しません。ローカルの書き込み対象も明確です(ダウンロードディレクトリ + 出力ディレクトリ + manifest + ZIP)。Blue ではない理由:実際の実行環境がユーザーの Google ログイン済み Chrome セッションであり、スクリプトが実タブを確保してクリックやダウンロードを行う高権限コンテキストであるため。Orange ではない理由:cookie/環境変数/キーストアなどの認証情報媒体を一切読み取らず(SKILL.md で明示的に禁止)、サードパーティ製のリモートパッケージやイメージにも依存せず、認証データそのものではなくホストが提供するブラウザ機能を利用しているためです。付記事項:現場での誤用時(例:ユーザーが先にシークレットウィンドウを開いていた場合)、スクリプトはタブが一致せず即座にエラー終了し、匿名パスへフォールバックすることはありません。

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結論

  • クリーンな認証方式:ユーザーのアクティブな Chrome セッションを利用し、認証情報を要求・読み込みしません。
  • 適切な抽出経路:サムネイルやページ内プレビューアセットではなく、ライトボックスの大判画像へ直接アプローチします。
  • 順序と数量の厳密な不変条件:誤った取得が発生した場合、取得漏れよりも早い段階でエラーとして検知されます。
  • 自己検証付きパッケージング:成果物の完全性が自動検証され、納品物の照合が容易です。
  • サイレントな品質低下を防止:低解像度フォールバック時は応答で明示されるため、ユーザーが原寸画像と誤認するのを防ぎます。
  • 最適な用途: 「Chrome で開いているこの Gemini の会話から、画像を一括抽出して ZIP にまとめてほしい」といったユースケースに最適です(ログイン状態が存在し、画像枚数が明確(expectedCount の指定が可能)、スレッド順に連番付けされたファイルが必要な場合)。また、オフライン作業(スライド作成や素材ライブラリ構築)のために画像一式を一度に取得したいユーザーにも適しています。
    不向きな用途: 未ログイン状態や対象タブが開かれていない環境(スクリプトが自動ログインすることはなく、即座にエラーになります)、最高解像度の原寸が必須で品質低下が許容されない用途(フォールバック時は 512px プレビューのみ)、複数セッションやアカウント全域の画像一括収集(1回の実行につき1タブのみ確保)、Chrome 拡張ホストが存在しない環境、ダウンロードディレクトリ内の中間生成物を完全に残したくないクリーンな環境には適していません。
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload download-gemini-images.tar.gz
    sha256: 2c75fce87072dc44…
    Option B · Copy Guide for Agent
    install.json
    Agent はこの JSON ガイドから自動的にダウンロードして検証します。
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit d5c4678cb5.