これまでの記事でAdobe Stockの自動化ツールを一通り構築してきました。 データ収集、AI解析、アップスケール、振り分けまで、すべてbatファイルから呼び出すPythonスクリプトで動いています。 自分で使う分には問題ないのですが、他の人に使ってもらう、あわよくば販売するという段階になると話が変わります。 本記事ではbatファイル運用からStreamlit製GUIアプリへの移行、そして販売を見据えたライセンス認証(仮)の実装までを記録します。

本記事は、以下のような方に向いています。

  1. bat運用をGUI化したい
  2. GUIを伴ったツールを作成したい
  3. Streamlitでツールを作りたい

最終的には下図のような画面となっています。

Adobe Stockの自動化ツールのStreamlit製GUIアプリ画面

経緯:なぜUI化が必要になったのか

自分用のツールとして使っている間は、batファイルをダブルクリックして処理を走らせるスタイルで十分でした。 設定値はjsonファイルを直接編集、実行ログはコマンドプロンプトで確認、という運用です。しかし「このツールを配布したい」という目標が生まれた瞬間、いくつかの問題が浮き彫りになりました。

まず、利用者がPythonの環境を持っていない可能性があります。batファイルで動くとはいえ、裏ではPythonが走っているので、利用者側にPython環境が必要です。 次に、jsonファイルを直接編集するのはハードルが高いという問題があります。 パスの区切り文字をバックスラッシュで書かなければならないとか、ダブルクォートを忘れてはいけないとか、そういった細かいルールは一般ユーザーには敷居が高いものです。 そして配布する際の最大の問題は違法コピー対策がないこと。ファイルを受け取った人が誰にでも渡せてしまう状態では、配布として成立しません。

これらの課題を一度に解決する方法として、ブラウザで操作できるGUIを作ることを決めました。

構成の検討:ブラウザ公開 vs ローカルUI

「ブラウザで動かせるなら、外部公開してSaaSにできるのでは?」という考えに至りました。 実際に検討しましたが、使っているレンタルサーバー(コノハウィング)は共用サーバーのため、Pythonを常駐プロセスとして動かすことができません。 FastAPIやFlaskを動かすには、VPSへの移行が必要になります。

方式概要採否
外部公開SaaSFastAPI + レンタルサーバー❌ 共用サーバーでは常駐プロセス不可
VPS移行ConohaVPS + FastAPI❌ サーバー管理コストが大きい
Streamlit CloudGitHubと連携して無料公開❌ コードが公開される/model.pthが重い
ローカルUI(Streamlit)利用者PCでStreamlit起動✅ 採用

結論として、利用者のPC上でStreamlitを起動してブラウザからUIを操作する形を採用しました。 ツール本体はGumroadやnote経由でダウンロードするという構成です。 このアプローチなら、サーバー管理は不要で、既存のPythonスクリプト資産もそのまま活かせます。

ライセンス確認:同梱できるもの・できないもの

配布を考えるにあたって、ツール内で使っているコンポーネントのライセンスを確認しました。

コンポーネントライセンス商用利用同梱
model.pth(CLIP)MIT License✅ OK✅ 同梱可
moondream(Ollama)Apache 2.0✅ OK△ 別途インストール
Ollama本体MIT License✅ OK△ 別途インストール
upscayl-binAGPL-3.0⚠️ 条件あり❌ 同梱不可

問題になりそうだったのは、Upscayl(画像のアップスケール処理で必要)です。AGPL-3.0ライセンスは「このライブラリを含むソフトウェアを配布する場合、そのソフトウェア全体のソースコードを公開しなければならない」という条件を持ちます。 つまりupscayl-binを同梱して配布すると、ツール全体のソースコードを公開する義務が生じてしまいます。 対策として、Upscaylは利用者が別途インストールし、設定画面でパスを指定する方式を採用しました。READMEに明記し、インストール先URLを案内します。

CLIP(品質スコア時に必要)はMITライセンスのため同梱・商用利用ともに問題ありませんでした。

Streamlit UIの設計と実装

UIの構成はサイドバーナビゲーションで5つのページを切り替える形にしました。

ページ対応するbat/スクリプト主な機能
📥 データ収集run_collect_exam_ok_ng.batEXAM/OK/NG収集 → history更新 → Excel更新
🤖 AI解析run_analyze_stock.batCLIPスコア算出 + Ollamaメタデータ生成
🔍 アップスケールrun_upscayl_filerename.bat高解像度化 → done移動 → リネーム
📂 OK/NG振り分けrun_furiwake_okng.batExcelSTATUS参照してフォルダ振り分け
📄 出力ファイル-CSV・Excel・ログをワンクリックで開く

各ページにはヘルプボタンを設置し、クリックすると処理内容・前提条件・出力ファイルの説明が展開されます。 フォルダパスの設定はテキスト入力だけでなく参照ボタンを用意し、tkinterのfiledialogでGUIのフォルダ選択ダイアログを呼び出せます。 ある程度時間を要する処理もあるため、処理実行中は進捗バー+固定枠ログエリアで状況を確認できるようにしました。

UIのこだわりポイント

フォルダパスはjsonから自動で読み込むため、初回設定さえすれば以降は何も入力しなくてよい設計にしました。 また設定変更はUI上で行い、そのまま stockphoto.json に書き戻すため、jsonを直接編集する必要がありません。

実装中に発生したエラーと解決

Streamlitはシンプルに見えて、ローカルPC上での動作には落とし穴がいくつかありました。実際に遭遇したエラーと対応策を記録します。

__file__bat\file\ フォルダを指す問題

最初のエラーはスクリプト起動直後に発生しました。

python: can't open file 'E:\temp\ai_stockphoto\bat\file\python\collect_exam.py': [Errno 2] No such file or directory

ファイルは bat\python\ にあるはずなのに、bat\file\python\ を探しに行っています。原因はStreamlitの内部挙動でした。 Streamlitは起動時にアップロードファイルのキャッシュとして file/ というフォルダを作成することがあり、__file__ がそのパスに書き換えられてしまうのです。

解決策として、start_ui.bat側で環境変数 STOCKPHOTO_BASE_DIR に正しいフォルダパスをセットして渡す方式を採用しました。

SET STOCKPHOTO_BASE_DIR=%~dp0
streamlit run "%STOCKPHOTO_BASE_DIR%\app.py"

%~dp0 はbatファイル自身のフォルダパスを返すWindows特有の変数です。app.py側はまずこの環境変数を読み、なければ上位フォルダに向かって stockphoto.json を探すフォールバックも実装しています。

② batファイルの文字化け

start_ui.bat に日本語のコメントを書いていたところ、コマンドプロンプト上で盛大に文字化けし、コマンドとして認識されるエラーが発生しました。

'ガ繧キ繧ケ繝・繝 BASE_DIR' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。

原因はbatファイルのエンコーディングです。Windowsのbatファイルはデフォルトでcp932(Shift-JIS)で処理されるため、UTF-8で保存したファイルの日本語が化けます。 解決策は単純で、batファイルから日本語を完全に排除してすべて英語に統一しました。(bat作成処理でも発生していたので、解決に至るまでの時間は一瞬でした)

③ ログのリアルタイム表示

処理を実行してもログが全く出ない、あるいは処理完了後に一気に表示されるという問題がありました。 Pythonの標準出力はデフォルトでバッファリングされるため、一定量溜まるまで出力されません。

解決策は2つ組み合わせました。まず子プロセス起動時に python -u オプションを付加してバッファリングを無効化します。 次に読み取り方を for line in proc.stdout から iter(proc.stdout.readline, "") に変えて1行ずつ即時読み取りにしました。

下図のようにログが表示されます。

Adobe Stockの自動化ツールのStreamlit製GUIアプリ画面

batとの処理対応:抜け漏れを防ぐ

UIを作る際に最も注意したのが、既存のbatで行っている処理を正確に再現することです。 batは複数のスクリプトを順番に呼び出す複雑なパイプラインになっているため、一部を見落とすと動作が変わってしまいます。

実際に最初の実装では update_history.py の呼び出しが完全に抜けていました。 データ収集後にCSVをExcelに書き込む処理は実装できていたのですが、その間に挟まるhistory CSV更新ステップを見落としていたのです。 これはユーザー側から指摘されて気づきました。batファイルの中身を最初から最後まで丁寧に読み込んで確認すべきでした。

正しいデータ収集のパイプラインは以下の通りです。

  1. collect_exam.py / collect_ok.py / collect_ng.py(各CSVを収集)
  2. update_history.py --mode exam/ok/ng(history CSVに累積追記)
  3. update_exam_excel.py / update_ok_excel.py / update_ng_excel.py(ExcelへSTATUS書き込み)

同様にアップスケール処理も、upscayl-binの実行だけでなく入力ファイルのdoneフォルダへの移動出力ファイルのリネームがセットです。 batではPowerShellを使っていたリネーム処理も、UI版ではPython(pathlib)で再実装しました。

アップスケールの3ステップ

STEP1: upscayl-bin実行(高解像度化)→ 進捗40%
STEP2: 入力ファイルをdoneフォルダへ移動 → 進捗70%
STEP3: YYYYMMDD_NNN.jpg 形式にリネーム → 進捗100%

配布・ライセンス認証(仮)の仕組み

ツールを配布する場合は、違法コピー対策は必須です。以下の構成を検討中です。

対策内容実装場所
ライセンスキー認証利用者ごとに固有キーを発行、起動時に入力app.py + コノハウィングPHP
マシンロックMACアドレス等でPC固有情報と紐付けコノハウィングDB
.exe化(将来)PyInstallerでコード非公開化PyInstaller

配布プラットフォームはGumroadを予定しています。GumroadにはWebhook機能があり、 購入完了時に自動でコノハウィング上のPHPスクリプトを呼び出してキーを生成・メール送信できそうです。 noteやtipsはWebhookを持たないため、完全自動化が難しいですが、どうにかできないかと検討中です。

現在のapp.pyに実装されているライセンス認証は、起動時にキーを入力するとSHA-256ハッシュとして .license ファイルに保存され、以降は自動認証されます。 未認証の場合は設定ページ以外にアクセスできません。本番では、このハッシュ検証部分をコノハウィングのAPIと通信するオンライン認証に差し替えることを想定しています。

完成した全体構成

今回の作業で完成した全体構成をまとめます。

レイヤー技術・ツール役割
UI層Streamlit + tkinterブラウザUIとフォルダ選択ダイアログ
処理層既存Python群(bat置き換え)収集・解析・アップスケール・振り分け
設定層stockphoto.json全設定値の一元管理
認証層SHA-256ハッシュ → コノハウィングAPIライセンスキー検証・マシンロック
配布層Gumroad + Webhook購入→キー発行→メール送信の自動化

batファイルで動いていたツールがブラウザから操作できるようになり、利用者はPythonのコマンドを一切覚える必要がなくなりました。 フォルダパスは参照ボタンで設定、処理の進行はリアルタイムログで確認、完了後はワンクリックでExcelやCSVを開けます。

次のステップはPyInstallerによる.exe化です。これによりPython環境のないPCでも動作するスタンドアロンアプリになり、配布ハードルがさらに下がります。 また、コノハウィング側のPHP認証サーバー構築とGumroad Webhookとの連携も残タスクです。

Adobe Stockへの画像投稿から審査追跡、そしてツールのUI化・配布準備まで、一連の自動化の記録は引き続きこのシリーズで更新していきます。