この記事は、以下のような方を想定しています。

  • Leonardo.aiで生成した画像を毎日手作業でダウンロードしている方
  • Tampermonkeyでのユーザースクリプト作成に興味がある方
  • ブラウザ上の繰り返し作業を自動化したい方
  • AIストックフォト自動化シリーズを読んでいる方

以前の記事で紹介した通り、ストックフォト用の画像はTensor.Art、SeaArt、Leonardo AIの3サービスを使い分けて生成しています。 その中でも、Leonardo.aiだけ「画像を1枚ずつしかダウンロードできない」「まとめて削除もできない」という仕様で、地味に時間を取られていました。

今回、その部分だけをTampermonkeyの自作スクリプトで解消したので、作成の流れと実装のポイントをまとめます。

Leonardo.aiだけ地味に面倒だった問題

Tensor.ArtやSeaArtでは、プロンプト単位や日付単位でまとめてダウンロードできる仕組みがあり、多少の手間はあっても許容範囲でした。 一方Leonardo.aiは、生成した画像を1枚ずつクリックしてダウンロードボタンを押す必要があり、生成数が多い日(約50枚前後)は特に苦痛でした。

毎日の作業として積み重なると、この「1枚ずつダウンロード」だけでおよそ5分程度かかっていました。 5分と聞くと大したことがないように思えますが、毎日続けると1ヶ月で2時間半、1年では30時間近くになります。 「これはさすがに自動化すべきだ」と判断し、着手することにしました。

Leonardo.aiにはブラウザ拡張向けの公式APIやエクスポート機能は提供されていないため、ページのDOM構造を直接操作する形での実装が必要でした。

実装で工夫したポイント

単純に「画像をすべて集めてダウンロードする」だけであれば難しくありません。ただ、実際に使ってみると次のような課題が見えてきました。

課題 内容 対応方法
「今日」だけを対象にしたい 過去に生成した画像も含めて全部ダウンロードされると、重複や不要なダウンロードが発生する 「Yesterday」や日付らしき見出しをDOM上で探し、その位置より下(過去分)を除外する境界判定を実装
仮想スクロール 画面に表示されていない画像はDOM上に存在しないため、そのままでは取りこぼす 少しずつスクロールしながら画像を収集し、スクロール位置が変化しなくなったら最下部と判断して収集を終了
画質の劣化 Leonardo.aiのimgタグに表示されているURLは、クエリパラメータ付きの縮小版であることが多い クエリなしURLを高画質版として先に試し、取得できなければ元URLにフォールバック
重複ダウンロード スクロールのたびに同じ画像が再検出される クエリを除いたベースURLをキーにしたMapで管理し、重複を自動排除
連続リクエストの負荷 大量の画像を一気にダウンロードするとサーバー側やブラウザに負荷がかかる 1件ずつ待機時間(400ms〜500ms)を挟みながら順番に処理

特に「仮想スクロール対策」と「今日の境界判定」の2つは、実際に動かしてみるまで気づかなかった部分です。 最初のバージョンでは、ページを開いた瞬間に見えている範囲の画像しか集められず、「あれ、枚数が全然足りない」となったことがきっかけで、スクロール処理を追加しました。

スクリプトの中身

最終的に作成したスクリプトの全体像は以下の通りです。処理としては大きく分けて次の4ステップになっています。

  1. ページ上部までスクロールし、境界(Yesterdayなどの見出し)位置を探索
  2. 少しずつスクロールしながら、境界より上(=今日分)の画像URLを収集
  3. 収集した枚数を確認ダイアログで表示し、ユーザーの実行確認を取得
  4. 1枚ずつ高画質URLを優先してダウンロード、失敗時は元URLにフォールバック

安全面で意識したこと

  • いきなり全件ダウンロードせず、必ず件数を確認ダイアログで表示してから実行する
  • 連続リクエストの間隔を空け、サイトへの負荷を抑える
  • 二重にボタンが追加されないよう、既存ボタンの有無をチェックする

実際に使用しているコードの主要部分(画像収集とダウンロード処理)は以下の通りです。

// "Yesterday" や日付らしき見出し(=今日より古いセクションの境界)を画面内で探す
function findOlderSectionBoundaryTop() {
  const candidates = document.querySelectorAll('h1, h2, h3, h4, div, span, p');
  for (const el of candidates) {
    if (el.children.length === 0) {
      const text = el.textContent.trim();
      if (
        text.length < 30 &&
        /^(Yesterday|\d{1,2}\s*(days?)?\s*ago|Last\s+\d+\s+days|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/i.test(text)
      ) {
        return el.getBoundingClientRect().top;
      }
    }
  }
  return null;
}

// 現在DOM上にある画像を集める。境界より下(古いセクション)にあるものは除外
function collectVisibleImages(imageMap) {
  const boundaryTop = findOlderSectionBoundaryTop();
  document.querySelectorAll('img[src*="cdn.leonardo.ai"]').forEach((img) => {
    if (!img.src) return;
    const top = img.getBoundingClientRect().top;
    if (boundaryTop !== null && top >= boundaryTop) return;
    const baseUrl = img.src.split('?')[0];
    if (!imageMap.has(baseUrl)) {
      imageMap.set(baseUrl, img.src);
    }
  });
  return boundaryTop !== null;
}

ダウンロード部分は、GM_xmlhttpRequestを使ってBlobとして取得し、aタグ経由で保存する形にしています。 高画質URL(クエリなし)を先に試し、失敗したら元のURLにフォールバックする作りにすることで、なるべく高解像度の画像を確保できるようにしています。

Tampermonkeyへの導入手順

今回はChromeにTampermonkey拡張を導入し、そこにスクリプトを登録する形にしました。

📦 スクリプトをGitHubで公開しています

このスクリプトは、GitHub上の公開リポジトリからいつでも入手できます。今後のアップデートも同じリポジトリに反映していく予定です。

GitHub:asupon-tools/userscripts(leonardo-bulk-download)

詳しいインストール手順は、以下のツールページにまとめています。

📖 インストール手順・使い方はこちら(ツールページ)

大まかな流れとしては、Chromeに「Tampermonkey」拡張機能を追加し、上記GitHubのスクリプトを登録するだけです。 設定のポイントとして、外部通信を行うため @grant GM_xmlhttpRequest@connect * を指定しています。

導入が完了すると、Leonardo.aiの生成画面(app.leonardo.ai/generate)を開いた際、画面右上に「📥 今日の画像を一括DL」という緑色のボタンが自動的に追加されます。 SPA(シングルページアプリケーション)のため、ページ遷移直後だとボタンが表示されないことがあり、その対策として一定間隔でボタンの存在を再チェックする処理も入れています。

Leonardo.ai画面に追加された一括ダウンロードボタン

ボタンを押すと、ページ上部から少しずつスクロールしながら画像を収集していきます。 収集が完了すると「◯◯枚の画像が見つかりました。ダウンロードを開始しますか?」という確認ダイアログが表示され、 「はい」を選択すると1枚ずつ順番にダウンロードが始まります。(画面付きの詳しい手順はツールページをご覧ください)

実際に使ってみた結果

導入前は、Leonardo.aiの画像ダウンロードだけで毎日5分程度かかっていました。 導入後は、ボタンを1回押して確認ダイアログで「はい」を選ぶだけで完結するため、体感としてはほぼ0分になりました。

自動化してよかったこと

  • 1枚ずつクリックする作業がなくなった
  • ダウンロード漏れ(見えていない画像の取りこぼし)がなくなった
  • ファイル名にタイムスタンプが入るため、他サービス分と混ざっても管理しやすい
  • 他の2サービス(Tensor.Art、SeaArt)と合わせて、画像収集フロー全体がほぼ自動化された

他のサービスに比べると対象が1サイトだけの小さな自動化ではありますが、 「面倒だけど毎日発生する作業」を潰せたという意味では、体感の負担軽減はかなり大きいです。 ちょっとした自動化でも、毎日積み重なる作業であれば効果は無視できません。

今後の課題

現状のスクリプトにはいくつか改善の余地も残っています。

  • 「今日」の境界判定が、Leonardo.ai側のUI文言変更に弱い(テキストベースの判定のため)
  • ダウンロード後のファイル振り分け(日付別フォルダへの自動移動)はまだ手動
  • 他サービス(Tensor.Art、SeaArt)と合わせた、生成〜フォルダ整理までの一気通貫の自動化

特にファイル振り分け以降は、以前紹介したUpscayl自動化のbatファイルと連携させることで、 さらに手離れを良くできそうだと考えています。この部分は今後の記事で改めて紹介する予定です。

FAQ

Tampermonkeyとは何ですか?

Tampermonkeyは、ブラウザに独自のJavaScript(ユーザースクリプト)を組み込める拡張機能です。 特定のサイトを開いたときだけ、自作の処理やボタンを追加することができます。

なぜLeonardo.aiだけ一括ダウンロードが必要だったのですか?

利用している生成AIサイトの中で、Leonardo.aiだけ画像を1枚ずつしかダウンロードできず、 まとめて削除する機能もないためです。生成数も多いため、手動作業の負担が特に大きくなっていました。

仮想スクロール対策とは何ですか?

画面に表示されていない要素をブラウザが描画しない「仮想スクロール」という仕組みへの対策です。 少しずつスクロールしながら画像を収集することで、画面外にある画像も取りこぼさないようにしています。

このスクリプトは他の生成AIサイトでも使えますか?

@matchやセレクタの部分がLeonardo.ai専用の記述になっているため、そのままでは動作しません。 ただし考え方(境界判定・仮想スクロール対策・高画質フォールバック)は他サイトにも応用可能です。

スクリプトはどこで入手できますか?

GitHubの公開リポジトリ(asupon-tools/userscripts)で公開しています。 インストール手順はツールページにまとめています。