Home / All Skills / Workflow & Core Tools / download-gemini-images
Workflow & Core Tools · daymade/claude-code-skills

download-gemini-images

Download, export, save, or package images from a Google Gemini conversation/chat/app page, especially uploaded images or generated image previews visible in a Gemini thread. Use when the task needs logged-in Chrome/Gemini state, opening Gemini image lightboxes, downloading the larger displayed image files, renaming them in order, and producing a ZIP archive.

L4 High-Privilege · Graded auth & full auditV1 Commit-PinnedExecute CommandNetwork AccessWrite FileDual-pass Audit
Author daymadeGitHub daymade/claude-code-skills ↗Stars 1385License MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)Commit d5c4678cb5
Control levels describe skill capabilities and suggested control measures; they do not represent quality or safety certifications, nor do they dictate usability. Usage decisions rest entirely with users and administrators. Different agent hosts apply varying constraints; do not assume hosts provide automatic protection. Third-party skill copyrights belong to their original authors. Use at your own risk.

1How It Works & Mechanism

The core prerequisite is borrowing the user's already logged-in Chrome rather than launching a standalone browser: Gemini conversations typically require an existing Google login state, so incognito or freshly opened browsers are ruled out in the very first step.

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.
What is happening here: Authentication is outsourced to the user's live browser session; the skill itself never handles any username/password input and merely drives an already logged-in tab.

The script lists all open tabs via the host's browser capability, matches them by URL or dual conditions (gemini.google.com + title containing 'Gemini'), and attaches via user.claimTab; if no match is found, it throws an error immediately and never opens a new page on its own.

download-gemini-images/scripts/download_gemini_images.mjs
return await browser.user.claimTab(matches[0]);
Preceding code in the same function throws `new Error(`No open Gemini tab matched ${tabUrlIncludes}`)` when `matches.length === 0`, and following code uses `claimGeminiTab` as the default value for `options.tab` in `downloadGeminiImagesFromChrome`. This claim-based approach (rather than creating a new tab) ensures the target is strictly the active session right in front of the user.

The lightbox must be opened to fetch large images: Gemini exposes larger blob: images only in preview mode, so the script clicks buttons one by one and polls until img.currentSrc becomes a new blob: with both naturalWidth and naturalHeight greater than 0.

download-gemini-images/scripts/download_gemini_images.mjs
info.src.startsWith("blob:") &&
This is the evaluation core of `waitForSelectedImage` (the same `if` statement also requires `naturalWidth > 0 && naturalHeight > 0 && (!previousSrc || info.src !== previousSrc)`), paired with `previousBlobSrc` tracking the previous image to prevent capturing stale images when 'the button was clicked but the image has not updated yet'. The SKILL.md overview describes this as a design rationale: 'Prefer the lightbox image over thumbnail/page-preview assets because Gemini often exposes a larger `blob:` image only after the user opens a preview.'

Images are renamed sequentially after being downloaded to disk: A UUID regex first extracts the identifier from the end of the blob URL to locate the newly downloaded file in Downloads by uuid+extension (or latest mtime where mtime >= startTime - 2s), and then copyFile renames it to a two-digit sequential name like image_01.jpg.

download-gemini-images/scripts/download_gemini_images.mjs
const outputName = `image_${pad2(index + 1)}${normalizeExtension(downloadedPath)}`;
The preceding line is `const uuid = imageInfo.src.match(/\/([0-9a-f-]{36})$/i)?.[1] || null;`. `waitForDownloadedMedia` prioritizes an exact match on `${uuid}${ext}`, falling back to 'the newest media file in the directory'. Sequential naming is an explicit delivery contract of the skill (SKILL.md: 'use the script's ordered names: `image_01.jpg`, `image_02.jpg`, etc.').

Packaging is handled by a standalone bash script with built-in dual verification: It first tests integrity with zip -T, then compares the count of image_NN in the directory against the entry count inside the archive, exiting with a non-zero code upon mismatch.

download-gemini-images/scripts/package_images.sh
echo "zip image count mismatch: directory=$image_count zip=$zip_count" >&2
`image_count` above comes from `find -maxdepth 1 -type f` matching `image_*.jpg|jpeg|png|webp`, while `zip_count` comes from `zipinfo -1` piped to grep regex `'/image_[0-9][0-9]\.(jpg|jpeg|png|webp)$'`. SKILL.md documents this step as: 'The packaging script creates the ZIP, runs `zip -T`, and verifies that the archive contains the same ordered image files as the directory.'

Two anti-desynchronization guardrails exist in the loop: Each iteration presses ESC first to close any lingering lightbox, and re-counts buttons every time, throwing immediately if the count diverges from the initial count—preventing asynchronous page loading from making the N-th button point to a different image.

download-gemini-images/scripts/download_gemini_images.mjs
throw new Error(`Gemini image button count changed from ${initialCount} to ${currentCount}`);
What is happening here: It asserts the 'stability of button sequence on the same page' as an invariant. Similarly outside the loop, if `expectedCount` is provided by the user, `initialCount !== expectedCount` throws directly to prevent under- or over-downloading from being treated as a success.

The fallback path uses the host tab's pageAssets capability to filter images by URL prefix and package them, explicitly acknowledging quality degradation and requiring that the user be notified.

download-gemini-images/SKILL.md
var uploaded = inventory.assets.filter(a => a.kind === "image" && a.url.includes("lh3.googleusercontent.com/gg/"));
Immediately following lines state: 'This usually captures 512px preview JPEGs, not the larger lightbox files. Tell the user when this fallback is used.'—fallback does not happen silently; it is required to be reported in the response.

Every run leaves a manifest.json recording page title, page URL, output directory, and each image's index, original download path, and pixel dimensions, making deliverables auditable.

download-gemini-images/scripts/download_gemini_images.mjs
const manifestPath = path.join(outputDir, "manifest.json");
The same block persists data via `writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8")` and returns `pageTitle`, `pageUrl`, `outputDir`, `imageCount`, and `files` together to the caller.

2Core Capabilities

01Claim Gemini tabs within user's logged-in Chrome (no new tabs opened, no incognito used)
02Open lightboxes sequentially to capture large images (blob: preview images rather than thumbnails)
03Rename files sequentially according to thread order into image_01.jpg, image_02.jpg, ...
04Strict expectedCount verification (exact count matching the user's reported quantity)
05Package into ZIP with dual validation of archive integrity and entry count
06Fallback path via pageAssets on image quality degradation (with explicit disclosure of low resolution to the user)
07Generate manifest.json at runtime for auditable deliverables
08Strict privacy boundary: never accesses cookies, localStorage, passwords, or session storage

3External Dependencies

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 关灯箱)

4Recommended Governance & Evidence

Suggested Controls for Safe Operation
For Agent Runtime
Graded/per-use authorization, sandbox recommended
For Administrators
Approval workflow, rollback support, full audit
Evidence & Triggers (File Paths & Excerpts)
  • scripts/: Local script or command execution: download-gemini-images/scripts/download_gemini_images.mjs (in host node_repl J
  • 网络端点: Declared external network access: gemini.google.com (drives user browser to visit/remain on this site; script only performs URL matching, actual requests are dispatched by user browser)
  • 本地工作目录: File I/O and artifact output: Chrome downloads directory (default ~/Downloads): intermediate files <uuid>.jpg/.png/.webp written by browser itself (script reads only, does not delete)
Audit Notes: Categorized under standard classification: Outbound network targets are limited to two completely predictable domains (gemini.google.com and lh3.googleusercontent.com, both Google-owned domains), with no third-party webhooks, no API keys, and no telemetry tracking; local write surface is explicit (download directory + output directory + manifest + ZIP). Why not blue: The actual execution environment is the user's Chrome session logged into Google, where the script claims real tabs to click and download, operating in a high-privilege context; why not orange: It does not read any credential carriers like cookies/environment variables/keychains (explicitly prohibited in SKILL.md), nor does it depend on third-party remote packages or mirrors—it borrows browser capabilities provided by the host rather than credential data itself. Additional note: If misused on-site (e.g., user opened an incognito window first), the script exits with an error due to tab match failure and will not degrade into an anonymous path.

5Second-Pass Verification

  • [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。

6Verdict & Scenarios

  • Clean authentication: Borrows the user's live Chrome session without requesting or reading any credential artifacts.
  • Optimal capture path: Targets lightbox large images directly rather than thumbnails or page-preview assets.
  • Hard invariants on order and quantity: Extraction errors are surfaced much earlier than missing-download failures.
  • Self-verifying packaging: Deliverables verify their own integrity for dependable handoff.
  • Non-silent degradation: Low-resolution fallbacks are explicitly stated in the response so users will not mistake them for full-resolution originals.
  • Best for: Ideal for scenarios like 'I already have this Gemini conversation open in Chrome; rip the images out and package them for me': valid login state already exists, the image count is explicit (expectedCount can be passed), and sequentially numbered files following thread order are needed. Also suitable for users who want to fetch an entire image set at once for offline processing (e.g., slide decks, asset libraries).
    Not for: Not suitable for logged-out states or scenarios with no open tabs (the script throws immediately and will not log in on its own); not suitable when maximum original resolution is mandatory and fallback is unacceptable (the fallback only yields 512px previews); not suitable for batch scraping across multiple sessions or account-wide galleries (claims only one tab per run); not suitable without a Chrome extension host; and not suitable for tidy environments that cannot tolerate leftover artifacts in the download directory.
    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
    Agents automatically download and verify mirrors from this JSON guide.
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit d5c4678cb5.