見出し画像

stable-diffusion-webui-forgeをビルドする

タイトル通りです。
いや正しくはstable-diffusion-webui-forgeをバックエンドとして組み込んだアプリをビルドする、なんですが。詰まったところを書きます。

①ビルドする為に一括ファイル変換

まず普通にstable-diffusion-webui-forgeのファイルをvenv以外丸ごとコピーします(くそ雑導入)
そして名前を『C:/Line2Shadow』に変更
pyinstallerでビルドする際、ファイルのパス指定が原因でビルドした実行ファイルが正常起動しません。のでそれを一括修正します。

以下のスクリプトを使用
PyInstallerPathFixer.py

import os
import re

# 対象のディレクトリパス
target_directory = 'C:/Line2Shadow'

# 追加する関数と変数のコード
prepend_code = """import sys
# 'frozen'状態に応じて適切なファイルパスを取得する関数
def get_appropriate_file_path():
    if getattr(sys, 'frozen', False):
        # ビルドされたアプリケーションの場合、sys.executableのパスを使用
        return sys.executable + "/Line2Shadow/"
    else:
        # そうでない場合は、従来通り__file__を使用
        return __file__

# 適切なファイルパスを取得
appropriate_file_path = get_appropriate_file_path()

"""

# 検索対象から除外するファイルパスのリスト(絶対パスで指定)
exclude_files = [
    os.path.abspath("C:/Line2Shadow/PyInstallerPathFixer.py")
]

# 検索対象から除外するフォルダのリスト(絶対パスで指定)
exclude_folders = [
]

def file_needs_update(filepath):
    """ファイルが __file__ 変数を使用しているかどうかを確認"""
    try:
        with open(filepath, 'r', encoding='utf-8') as file:
            content = file.read()
            return re.search(r'(?<!")(__file__)(?!")', content) is not None
    except UnicodeDecodeError:
        print(f"UnicodeDecodeError occurred in file: {filepath}")
        return False

def update_file(filepath):
    """ファイルを更新する"""
    if not any(filepath.startswith(excluded) for excluded in exclude_folders) and filepath not in exclude_files:
        with open(filepath, 'r+', encoding='utf-8') as file:
            content = file.read()
            updated_content = re.sub(r'(?<!")(__file__)(?!")', 'appropriate_file_path', content)
            file.seek(0)
            file.write(prepend_code + updated_content)
            file.truncate()

# 特定のディレクトリ以下の.pyファイルを対象に処理
for root, dirs, files in os.walk(target_directory):
    dirs[:] = [d for d in dirs if os.path.join(root, d) not in exclude_folders]
    for file in files:
        filepath = os.path.join(root, file)
        if file.endswith('.py') and file_needs_update(filepath):
            update_file(filepath)

print("処理が完了しました。")
python PyInstallerPathFixer.py

②インストール

webui-user.batを『COMMANDLINE_ARGS=--xformers』追加して実行。
ついでにビルドする際に使うpyinstallerと、自作アプリで使うライブラリを手動でインストールしときます。
pygit2はGITがインストールされていないPCでも動くように改変したいのでインストール。

pip install tkinterdnd2
pip install onnx==1.15.0
pip install onnxruntime==1.16
pip install onnxruntime-gpu==1.17.1
pip install pyinstaller
pip install pygit2

③自作アプリの起動確認

下記のファイルを追加

"C:\Line2Shadow\Line2Shadow.py"
"C:\Line2Shadow\gui.py"
"C:\Line2Shadow\modules\launch_utils_Line2Shadow.py"
"C:\Line2Shadow\utils\request_api.py"
"C:\utils\tagger.py"
"C:\Line2Shadow\utils\application.py"
"C:\Line2Shadow\utils\models_dl.py"
これでとりあえず自作アプリが動くことを確認します。

python Line2Shadow.py

④一部スクリプトの書き換え

"C:\Line2Shadow\modules\shared_cmd_options.py"を以下のように書き換えます。

import os

import launch
from modules import cmd_args, script_loading
from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir  # noqa: F401

parser = cmd_args.parser

script_loading.preload_extensions(extensions_dir, parser, extension_list=launch.list_extensions(launch.args.ui_settings_file))
script_loading.preload_extensions(extensions_builtin_dir, parser)

if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None:
    cmd_opts, _ = parser.parse_known_args()
else:
    cmd_opts, _ = parser.parse_known_args()

# --nowebuiを強制的にTrueに設定
cmd_opts.nowebui = True

cmd_opts.webui_is_non_local = any([cmd_opts.share, cmd_opts.listen, cmd_opts.ngrok, cmd_opts.server_name])
cmd_opts.disable_extension_access = cmd_opts.webui_is_non_local and not cmd_opts.enable_insecure_extension_access

PyInstallerビルド時コマンドプロンプトの指定がないことでエラーを吐くので手動修正。モデルロード時jsonの読み込みパスがおかしくなるので書き換え
ldm_patched\modules\sd1_clip.py
ldm_patched\modules\sd2_clip.py
ldm_patched\modules\sdxl_clip.py

gitのインストールされていないPCだとおかしくなるのでpygit2使うように手動修正。
modules\ui_extensions.py
modules\gitpython_hack.py
modules\config_states.py

⑤pyinstallerでビルド

pyinstaller "C:\Line2Shadow\Line2Shadow.py" ^
--clean ^
--collect-data tkinterdnd2 ^
--add-data "C:\Line2Shadow\javascript;.\javascript" ^
--add-data "C:\Line2Shadow\ldm_patched;.\ldm_patched" ^
--add-data "C:\Line2Shadow\localizations;.\localizations" ^
--add-data "C:\Line2Shadow\modules;.\modules" ^
--add-data "C:\Line2Shadow\modules_forge;.\modules_forge" ^
--add-data "C:\Line2Shadow\repositories;.\repositories" ^
--add-data "C:\Line2Shadow\cache.json;." ^
--add-data "C:\Line2Shadow\script.js;." ^
--add-data "C:\Line2Shadow\ui-config.json;." ^
--add-data "C:\Line2Shadow\config_states;.\config_states" ^
--add-data "C:\Line2Shadow\configs;.\configs" ^
--add-data "C:\Line2Shadow\extensions-builtin;.\extensions-builtin" ^
--add-data "C:\Line2Shadow\html;.\html"

⑥必要なモジュールをコピー

xcopy /E /I /Y venv\Lib\site-packages\xformers dist\Line2Shadow\_internal\xformers
xcopy /E /I /Y venv\Lib\site-packages\pytorch_lightning dist\Line2Shadow\_internal\pytorch_lightning
xcopy /E /I /Y venv\Lib\site-packages\lightning_fabric dist\Line2Shadow\_internal\lightning_fabric
xcopy /E /I /Y venv\Lib\site-packages\gradio dist\Line2Shadow\_internal\gradio
xcopy /E /I /Y venv\Lib\site-packages\gradio_client dist\Line2Shadow\_internal\gradio_client
xcopy /E /I /Y venv\Lib\site-packages\kornia dist\Line2Shadow\_internal\kornia
xcopy /E /I /Y venv\Lib\site-packages\open_clip dist\Line2Shadow\_internal\open_clip
xcopy /E /I /Y venv\Lib\site-packages\jsonmerge dist\Line2Shadow\_internal\jsonmerge
xcopy /E /I /Y venv\Lib\site-packages\torchdiffeq dist\Line2Shadow\_internal\torchdiffeq
xcopy /E /I /Y venv\Lib\site-packages\cleanfid dist\Line2Shadow\_internal\cleanfid
xcopy /E /I /Y venv\Lib\site-packages\clip dist\Line2Shadow\_internal\clip
xcopy /E /I /Y venv\Lib\site-packages\resize_right dist\Line2Shadow\_internal\resize_right
xcopy /E /I /Y venv\Lib\site-packages\diffusers dist\Line2Shadow\_internal\diffusers
xcopy /E /I /Y venv\Lib\site-packages\onnx dist\Line2Shadow\_internal\onnx
xcopy /E /I /Y venv\Lib\site-packages\onnxruntime dist\Line2Shadow\_internal\onnxruntime
xcopy /E /I /Y config_states dist\Line2Shadow\config_states
xcopy /E /I /Y configs dist\Line2Shadow\configs
xcopy /E /I /Y embeddings dist\Line2Shadow\embeddings
xcopy /E /I /Y extensions-builtin dist\Line2Shadow\extensions-builtin
xcopy /E /I /Y html dist\Line2Shadow\html
xcopy /E /I /Y javascript dist\Line2Shadow\javascript
xcopy /E /I /Y ldm_patched dist\Line2Shadow\ldm_patched
xcopy /E /I /Y localizations dist\Line2Shadow\localizations
xcopy /E /I /Y modules dist\Line2Shadow\modules
xcopy /E /I /Y modules_forge dist\Line2Shadow\modules_forge
xcopy /E /I /Y repositories dist\Line2Shadow\repositories
xcopy /E /I /Y scripts dist\Line2Shadow\scripts
copy script.js dist\Line2Shadow\script.js
copy model_DL.cmd dist\Line2Shadow\model_DL.cmd
copy Line2Shadow_ReadMe.txt dist\Line2Shadow\Line2Shadow_ReadMe.txt 

正直これ多分もっとやり方あると思う(脳筋解決法)
自分用メモ(一行ずつ実行するのが面倒なのでまとめて実行できるようにしただけ)

xcopy /E /I /Y venv\Lib\site-packages\xformers dist\Line2Shadow\_internal\xformers && xcopy /E /I /Y venv\Lib\site-packages\pytorch_lightning dist\Line2Shadow\_internal\pytorch_lightning && xcopy /E /I /Y venv\Lib\site-packages\lightning_fabric dist\Line2Shadow\_internal\lightning_fabric && xcopy /E /I /Y venv\Lib\site-packages\gradio dist\Line2Shadow\_internal\gradio && xcopy /E /I /Y venv\Lib\site-packages\gradio_client dist\Line2Shadow\_internal\gradio_client && xcopy /E /I /Y venv\Lib\site-packages\kornia dist\Line2Shadow\_internal\kornia && xcopy /E /I /Y venv\Lib\site-packages\open_clip dist\Line2Shadow\_internal\open_clip && xcopy /E /I /Y venv\Lib\site-packages\jsonmerge dist\Line2Shadow\_internal\jsonmerge && xcopy /E /I /Y venv\Lib\site-packages\torchdiffeq dist\Line2Shadow\_internal\torchdiffeq && xcopy /E /I /Y venv\Lib\site-packages\cleanfid dist\Line2Shadow\_internal\cleanfid && xcopy /E /I /Y venv\Lib\site-packages\clip dist\Line2Shadow\_internal\clip && xcopy /E /I /Y venv\Lib\site-packages\resize_right dist\Line2Shadow\_internal\resize_right && xcopy /E /I /Y venv\Lib\site-packages\diffusers dist\Line2Shadow\_internal\diffusers && xcopy /E /I /Y config_states dist\Line2Shadow\config_states && xcopy /E /I /Y configs dist\Line2Shadow\configs && xcopy /E /I /Y embeddings dist\Line2Shadow\embeddings && xcopy /E /I /Y extensions-builtin dist\Line2Shadow\extensions-builtin && xcopy /E /I /Y html dist\Line2Shadow\html && xcopy /E /I /Y javascript dist\Line2Shadow\javascript && xcopy /E /I /Y ldm_patched dist\Line2Shadow\ldm_patched && xcopy /E /I /Y localizations dist\Line2Shadow\localizations && xcopy /E /I /Y modules dist\Line2Shadow\modules && xcopy /E /I /Y modules_forge dist\Line2Shadow\modules_forge && xcopy /E /I /Y repositories dist\Line2Shadow\repositories && xcopy /E /I /Y scripts dist\Line2Shadow\scripts && copy script.js dist\Line2Shadow\script.js && copy model_DL.cmd dist\Line2Shadow\model_DL.cmd && copy Line2Shadow_ReadMe.txt dist\Line2Shadow\Line2Shadow_ReadMe.txt

これにてアプリが実行できるようになりました!完!!!!!!!!!!!!

いいなと思ったら応援しよう!