├── .gitignore
├── CHANGELOG.md
├── ConvertLora.bat
├── DeleteOutput.bat
├── EasyPromptAnimeEditor.bat
├── FixCheckpoint.bat
├── FpsX4.bat
├── FpsX6.bat
├── Frames2Mp4.bat
├── Generate.bat
├── GenerateFolder.bat
├── GenerateFolderForever.bat
├── GenerateForever.bat
├── LICENSE.txt
├── Mp4Crf.bat
├── Mp4Crf26.bat
├── Mp4Crf32.bat
├── Mp4Crf38.bat
├── OpenColabEditor.bat
├── README.md
├── Update.bat
├── XMp4.bat
├── XMp4H1200.bat
├── XMp4H1900.bat
├── XMp4W1200.bat
├── XMp4W1920.bat
├── doc
└── img
│ └── title.webp
├── editor
├── DownloadMenu.json
├── bat
│ ├── refine.bat
│ └── tile-upscale.bat
└── src
│ ├── EasyPromptAnimeEditor.py
│ ├── config.py
│ ├── const.py
│ ├── controller.py
│ ├── ctr_basic.py
│ ├── ctr_control_net.py
│ ├── ctr_file.py
│ ├── ctr_generate.py
│ ├── ctr_input.py
│ ├── ctr_ip_adapter.py
│ ├── ctr_menu.py
│ ├── ctr_mosaic.py
│ ├── ctr_output.py
│ ├── ctr_preview.py
│ ├── ctr_upscale.py
│ ├── l10n.py
│ ├── l10n_en.py
│ ├── l10n_ja.py
│ ├── log.py
│ ├── mdl_editor.py
│ ├── mdl_generate.py
│ ├── mdl_notifier.py
│ ├── mdl_prompt.py
│ ├── model.py
│ ├── mosaic.py
│ ├── prompt_perser.py
│ ├── prompt_travel.py
│ ├── serializer.py
│ ├── task.py
│ ├── tsk_command_prompt.py
│ ├── tsk_generate.py
│ ├── tsk_upscale.py
│ ├── ui.py
│ ├── ui_basic.py
│ ├── ui_const.py
│ ├── ui_control_net.py
│ ├── ui_generate.py
│ ├── ui_input.py
│ ├── ui_ip_adapter.py
│ ├── ui_menu.py
│ ├── ui_mosaic.py
│ ├── ui_output.py
│ ├── ui_preview.py
│ └── ui_upscale.py
├── sample
├── Gacha-L120-C16-W448-H512.json
├── Gacha.bat
├── Oiyami-L60-C16-W512-H384-T768.json
├── Oiyami.bat
├── UpscaledGacha-L120-C16-W448-H512-T768.json
├── UpscaledGacha.bat
├── Vram12G-L120-C16-W608-H384-T768-T1152.json
├── Vram12G.bat
├── Vram8G-L120-C16-W448-H512-T768-T1024.json
└── Vram8G.bat
└── src
├── Setup-ECCV2022-RIFE.bat
├── Setup-EasyPromptAnime.bat
├── Setup-Practical-RIFE.bat
├── Setup-animatediff-cli-prompt-travel.bat
├── Setup-editor.bat
├── Setup-ffmpeg-master-latest-win64-gpl.bat
├── Setup-sd-scripts-accelerate_config.bat
├── Setup-sd-scripts.bat
├── Setup.bat
├── Update-ECCV2022-RIFE.bat
├── Update-Practical-RIFE.bat
├── Update-animatediff-cli-prompt-travel.bat
├── Update-sd-scripts.bat
└── Update.bat
/.gitignore:
--------------------------------------------------------------------------------
1 | /animatediff-cli-prompt-travel/
2 | /ECCV2022-RIFE/
3 | /ffmpeg-*-essentials_build/
4 | /sample/**/*.mp4
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 過去の更新履歴
2 |
3 | - 2023/10/27
4 | - 連番画像へのモザイクに対応しました。
5 | - 2023/10/25
6 | - フレーム補間をする RIFE のバージョンを 3.6 から 4.8 に上げました。
7 | - アップデート後は Practical-RIFE/ を使用しますので、ECCV2022-RIFE/ を削除できます。
8 | - 2023/10/22
9 | - MP4 の動画からコントロールネット用の連番画像をワンアクションで取り出せる `MP4 画像取り込み` を追加しました。
10 | - 利用方法は『[MP4 取り込みで動きをつける](https://twitter.com/Zuntan03/status/1716386352508608629)』をご覧ください。
11 | - アップスケールで `ip2p` と `line_anime` を使えるようにしました。
12 | - 2023/10/20
13 | - モーション LoRA に対応しました。
14 | - `ダウンロード` メニューの `モーション LoRA` からダウンロードできます。
15 | - LoRAと似たように `M: v2_lora_ZoomIn: 1.0` とプロンプトに記載すると使えます。
16 | - 動画の出力先にファイルも保存するようにしました。
17 | - `ファイル` メニューに `新規作成` と `終了` を追加しました。
18 | - 変更が未保存の場合に確認するようにしました。
19 | - 2023/10/17
20 | - 簡単プロンプトアニメのエディタの状態を保存して、読み込めるようにしました。
21 | - 2023/10/15
22 | - ControlNet の tile, canny, depth, inpaint, ip2p, lineart, lineart_anime, mlsd, normalbae, openpose, scribble, seg, shuffle, softedge に対応しました。
23 | - 2023/10/14
24 | - `ダウンロード` メニューの `LoRA` に汎用的な LoRA を追加しました。
25 | - [3dSlider_v2](https://twitter.com/Zuntan03/status/1713064888397181256),
26 | [flat2](https://twitter.com/Zuntan03/status/1713067612874359173)
27 | - `設定` メニューに `現在のプロンプトを起動時のプロンプトにする` と `現在の設定を起動時の設定にする` と `ライト/ダークモードの切り替え(再起動後に適用)` を追加しました。
28 | - 2023/10/12
29 | - プロンプト入力欄で `元に戻す(Ctrl+Z)` , `やり直し(Ctrl+Y)` を使えるようにしました。
30 | - `ダウンロード` メニューの項目を充実しました。
31 | - 2023/10/10
32 | - `ツール` メニューの `使えない LoRA を使えるように変換` でこれまで使えなかった LoRA が使える場合があります。
33 | - `アニメ` メニューのアップスケールで、アニメ生成とアップスケールを別々に実行できます。
34 | - 生成と同じオプションでアプスケする `生成時の設定で初回アップスケール` と、エディタの設定を適用して色々変更できる `現在の設定で初回アップスケール` です。
35 | - 黒画面への対処や、モデルやLoRAパラメータを変えてのアップスケールができます。
36 | - これに合わせてタイルアップスケールの強度と開始と終了を設定できるようにしました。
37 | - `IPアダプタ` で[生成時に画像をプロンプトとして使えます](https://www.google.com/search?q=ip-adapter)。
38 | - プロンプトとして使いたい画像をフォルダにまとめ、ファイル名で 010.png などとフレーム数を指定する必要があります。
39 | - `ダウンロード` からモデルやモーションモジュールなどをダウンロードできます。
40 | - おすすめのものがあれば教えてください。
41 | - 2023/10/09
42 | - ローカル版エディタをα版としてひっそり公開しました。
43 | - `EasyPromptAnimeEditor.bat` で起動します。
44 | - α版なのでドキュメントはまだありません。
45 | - Colab 版エディタは廃止予定です。
46 | - セットアップの仕組みを大幅に変更して、最初にダウンロードするファイルの量を大幅に減らしました。
47 | - ローカル版エディタの「ダウンロード」メニューでダウンロードできます。
48 | - これまでインストールできていたのに、再インストールしても動かない場合は知らせてください。
49 | - 2023/09/29
50 | - フォルダ内の生成設定ファイルで一通り生成する `GenerateFolder.bat` と、これを連続生成し続ける `GenerateFolderForever.bat` を追加しました。
51 | - 2023/09/28
52 | - 昨日の「インストール時のエラーチェックを強化しました。」に問題があり、インストールできなかった不具合を修正しました。
53 | - [Setup-EasyPromptAnime.bat](https://github.com/Zuntan03/EasyPromptAnime/raw/main/src/Setup-EasyPromptAnime.bat?20231011) を再ダウンロードして、インストールしてください。
54 | - animatediff のコマンドでエラーが発生した際に、処理を終了するようにしました。
55 | - GenerateForever.exe も同様に、処理を終了するようにしました。
56 | - 2023/09/27
57 | - インストール時のエラーチェックを強化しました。
58 | - 0~9時に生成した際に、動画ファイル名の時間部分をゼロパディングするようにしました。
59 | - モデルファイルなどのシンボリックリンクを削除しないようにしました。
60 | - 2023/09/26
61 | - サンプルに『[美味しいヤミー感謝感謝](https://www.google.com/search?q=%E7%BE%8E%E5%91%B3%E3%81%97%E3%81%84%E3%83%A4%E3%83%9F%E3%83%BC%E6%84%9F%E8%AC%9D%E6%84%9F%E8%AC%9D)』の `sample/Oiyami.bat` を追加しました。
62 | - 2023/09/25
63 | - FFmpeg のインストールに失敗する不具合を修正しました。
64 | - `Update.bat` を実行すると修正します。
65 | - 2023/09/24
66 | - **今回の更新では `Update.bat` を 2回!実行してください(`Update.bat` を更新したため)。**
67 | - [AnimateDiff prompt travel](https://github.com/s9roll7/animatediff-cli-prompt-travel) の更新に対応しました。
68 | - AnimateDiff 用にモデルを修正する `FixCheckpoint.bat` を追加しました。
69 | が、手元で利用しているモデルは修正不要でした。
70 | - 生成した動画のサイズを低減する `Mp4Crf(26|32|38)` を追加しました。
71 | - 生成した動画から X(Twitter) 投稿用の動画を生成する `XMp4*.bat` を追加しました。
72 | - 2023/09/23
73 | - 生成する動画のデフォルトの FPS を、様々なサービスとの互換性の観点から 40FPS にしました。
74 | - 生成設定ファイル名に `-D3` を付け足すと、以前と同様に 80FPS になります。
75 | - FPS 指定の詳細は [ファイル名オプション一覧](#ファイル名オプション一覧) や `FpsX4.bat` の説明を参照してください。
76 | - 生成した mp4 ファイル名に、日時のプレフィックスを追加するようにしました。
77 | - 同一シードで生成しても、mp4 ファイルを上書きしなくなります。
78 | - 2023/09/22
79 | - motion-module に mm-Stabilized_high.pth と mm-Stabilized_mid.pth を追加しました。
80 | - `Update.bat` を実行するとダウンロードします。
81 | - 同一シードで同じフォルダに再出力した際に、正しくフレーム補間されない不具合を修正しました。
82 | - インストール済みの方は `Update.bat` を実行してください。
83 | - インストーラーで `C:\Windows\System32` にパスが通っていない場合にエラー扱いにしました。
84 | - 2023/09/21
85 | - 公開
86 |
--------------------------------------------------------------------------------
/ConvertLora.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the C3Lier LoRA or LoCon LoRA file.
4 | pause & exit /b 1
5 | )
6 |
7 | set DIM=32
8 | if not "%~2" == "" if not "%~2" == "0" (
9 | set DIM=%~2
10 | )
11 |
12 | set MODEL_PATH="%~dp0animatediff-cli-prompt-travel\data\models\sd\featurelessMix_v2020237Clearvae.safetensors"
13 | if not "%~3" == "" (
14 | set MODEL_PATH="%~f3"
15 | )
16 |
17 | set SRC_PATH="%~f1"
18 | set MERGED_PATH="%~dpn1-merged.safetensors"
19 | set DEST_PATH="%~dp0animatediff-cli-prompt-travel\data\lora\%~n1.safetensors"
20 |
21 | pushd %~dp0sd-scripts
22 | call venv\Scripts\activate.bat
23 |
24 | echo SRC_PATH: %SRC_PATH%
25 | echo MERGED_PATH: %MERGED_PATH%
26 | echo DEST_PATH: %DEST_PATH%
27 |
28 | python networks\merge_lora.py^
29 | --sd_model %MODEL_PATH%^
30 | --save_to %MERGED_PATH%^
31 | --models %SRC_PATH%^
32 | --ratios 1.0^
33 | --save_precision float
34 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
35 |
36 | python networks\extract_lora_from_models.py^
37 | --model_org %MODEL_PATH%^
38 | --model_tuned %MERGED_PATH%^
39 | --save_to %DEST_PATH%^
40 | --dim %DIM%^
41 | --save_precision bf16^
42 | --device cpu
43 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
44 |
45 | del %MERGED_PATH%
46 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
47 |
48 | call venv\Scripts\deactivate.bat
49 | popd rem %~dp0sd-scripts
50 |
--------------------------------------------------------------------------------
/DeleteOutput.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | @REM If you want to disable (y/n), delete the three lines below.
4 | echo Delete output/*/ upscaled/*/ refine/*/ (y/n)
5 | set /p YES_OR_NO=
6 | if /i not "%YES_OR_NO%" == "Y" ( exit /b 0 )
7 |
8 | pushd %~dp0animatediff-cli-prompt-travel
9 |
10 | for /d %%d in ("output\*") do ( rmdir /s /q "%%~d" )
11 | for /d %%d in ("upscaled\*") do ( rmdir /s /q "%%~d" )
12 | for /d %%d in ("refine\*") do ( rmdir /s /q "%%~d" )
13 |
14 | popd rem %~dp0animatediff-cli-prompt-travel
15 |
--------------------------------------------------------------------------------
/EasyPromptAnimeEditor.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0
4 | call editor\venv\Scripts\activate.bat
5 |
6 | if not exist output ( mkdir output )
7 | if not exist editor\log ( mkdir editor\log )
8 | set ZERO_PADDING_TIME=%TIME: =0%
9 | for /f "tokens=2-7 delims=/:. " %%a in ("echo %DATE% %ZERO_PADDING_TIME%") do (
10 | set YYYY_MMDD_HHMM_SS=%%a_%%b%%c_%%d%%e_%%f
11 | )
12 |
13 | python editor\src\EasyPromptAnimeEditor.py > editor\log\EasyPromptAnimeEditorLog-%YYYY_MMDD_HHMM_SS%.txt
14 |
15 | call editor\venv\Scripts\deactivate.bat
16 | popd rem %~dp0
17 |
--------------------------------------------------------------------------------
/FixCheckpoint.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the model file.
4 | pause & exit /b 1
5 | )
6 |
7 | set MODEL_PATH="%~f1"
8 |
9 | pushd %~dp0animatediff-cli-prompt-travel
10 | call venv\Scripts\activate.bat
11 |
12 | echo animatediff fix-checkpoint %MODEL_PATH%
13 | animatediff fix-checkpoint %MODEL_PATH%
14 |
15 | call venv\Scripts\deactivate.bat
16 | popd rem %~dp0%~dp0animatediff-cli-prompt-travel
17 |
--------------------------------------------------------------------------------
/FpsX4.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the mp4 file.
4 | pause & exit /b 1
5 | )
6 | set PATH=%~dp0ffmpeg-master-latest-win64-gpl\bin;%PATH%
7 |
8 | set RIFE_DIV_NUM=2
9 | set RIFE_FPS_NAME=
10 | set RIFE_FPS_OPTION=
11 | set FFMPEG_FPS_NAME=
12 | set FFMPEG_FPS_OPTION=
13 | set FFMPEG_CRF=20
14 |
15 | if not "%~2" == "" if not "%~2" == "0" (
16 | set RIFE_DIV_NUM=%~2
17 | )
18 | if not "%~3" == "" if not "%~3" == "0" (
19 | set RIFE_FPS_NAME=-f%~3
20 | set RIFE_FPS_OPTION=--fps=%~3
21 | )
22 | if not "%~4" == "" if not "%~4" == "0" (
23 | set FFMPEG_FPS_NAME=-F%~4
24 | set FFMPEG_FPS_OPTION=-r %~4
25 | )
26 | if not "%~5" == "" if not "%~5" == "0" (
27 | set FFMPEG_CRF=%~5
28 | )
29 |
30 | set SRC_PATH="%~f1"
31 | set RIFE_DEST_PATH="%~dpn1-D%RIFE_DIV_NUM%%RIFE_FPS_NAME%.mp4"
32 | set FFMPEG_DEST_PATH="%~dpn1-D%RIFE_DIV_NUM%%RIFE_FPS_NAME%e%FFMPEG_FPS_NAME%.mp4"
33 |
34 | pushd %~dp0Practical-RIFE
35 | call venv\Scripts\activate.bat
36 |
37 | @REM echo ARGS: %1 %2 %3 %4 %5
38 | @REM echo RIFE_DIV_NUM: %RIFE_DIV_NUM%
39 | @REM echo RIFE_FPS_NAME: %RIFE_FPS_NAME%
40 | @REM echo RIFE_FPS_OPTION: %RIFE_FPS_OPTION%
41 | @REM echo FFMPEG_FPS_NAME: %FFMPEG_FPS_NAME%
42 | @REM echo FFMPEG_FPS_OPTION: %FFMPEG_FPS_OPTION%
43 | @REM echo FFMPEG_CRF: %FFMPEG_CRF%
44 | @REM echo SRC_PATH: %SRC_PATH%
45 | @REM echo RIFE_DEST_PATH: %RIFE_DEST_PATH%
46 | @REM echo FFMPEG_DEST_PATH: %FFMPEG_DEST_PATH%
47 | @REM pause
48 |
49 | echo python inference_video.py --exp=%RIFE_DIV_NUM% --video %SRC_PATH% %RIFE_FPS_OPTION% --output %RIFE_DEST_PATH%
50 | python inference_video.py --exp=%RIFE_DIV_NUM% --video %SRC_PATH% %RIFE_FPS_OPTION% --output %RIFE_DEST_PATH%
51 |
52 | echo ffmpeg.exe -y -i %RIFE_DEST_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation %FFMPEG_FPS_OPTION% -crf %FFMPEG_CRF% %FFMPEG_DEST_PATH%
53 | ffmpeg.exe -y -i %RIFE_DEST_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation %FFMPEG_FPS_OPTION% -crf %FFMPEG_CRF% %FFMPEG_DEST_PATH%
54 |
55 | call venv\Scripts\deactivate.bat
56 | popd rem %~dp0Practical-RIFE
57 |
--------------------------------------------------------------------------------
/FpsX6.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the mp4 file.
4 | pause & exit /b 1
5 | )
6 | set PATH=%~dp0ffmpeg-master-latest-win64-gpl\bin;%PATH%
7 |
8 | set RIFE_MULTI_NUM=6
9 | set RIFE_FPS_NAME=
10 | set RIFE_FPS_OPTION=
11 | set FFMPEG_FPS_NAME=
12 | set FFMPEG_FPS_OPTION=
13 | set FFMPEG_CRF=20
14 |
15 | if not "%~2" == "" if not "%~2" == "0" (
16 | set RIFE_MULTI_NUM=%~2
17 | )
18 | if not "%~3" == "" if not "%~3" == "0" (
19 | set RIFE_FPS_NAME=-f%~3
20 | set RIFE_FPS_OPTION=--fps=%~3
21 | )
22 | if not "%~4" == "" if not "%~4" == "0" (
23 | set FFMPEG_FPS_NAME=-F%~4
24 | set FFMPEG_FPS_OPTION=-r %~4
25 | )
26 | if not "%~5" == "" if not "%~5" == "0" (
27 | set FFMPEG_CRF=%~5
28 | )
29 |
30 | set SRC_PATH="%~f1"
31 | set RIFE_DEST_PATH="%~dpn1-M%RIFE_MULTI_NUM%%RIFE_FPS_NAME%.mp4"
32 | set FFMPEG_DEST_PATH="%~dpn1-M%RIFE_MULTI_NUM%%RIFE_FPS_NAME%e%FFMPEG_FPS_NAME%.mp4"
33 |
34 | pushd %~dp0Practical-RIFE
35 | call venv\Scripts\activate.bat
36 |
37 | @REM echo ARGS: %1 %2 %3 %4 %5
38 | @REM echo RIFE_MULTI_NUM: %RIFE_MULTI_NUM%
39 | @REM echo RIFE_FPS_NAME: %RIFE_FPS_NAME%
40 | @REM echo RIFE_FPS_OPTION: %RIFE_FPS_OPTION%
41 | @REM echo FFMPEG_FPS_NAME: %FFMPEG_FPS_NAME%
42 | @REM echo FFMPEG_FPS_OPTION: %FFMPEG_FPS_OPTION%
43 | @REM echo FFMPEG_CRF: %FFMPEG_CRF%
44 | @REM echo SRC_PATH: %SRC_PATH%
45 | @REM echo RIFE_DEST_PATH: %RIFE_DEST_PATH%
46 | @REM echo FFMPEG_DEST_PATH: %FFMPEG_DEST_PATH%
47 | @REM pause
48 |
49 | echo python inference_video.py --multi=%RIFE_MULTI_NUM% --video %SRC_PATH% %RIFE_FPS_OPTION% --output %RIFE_DEST_PATH%
50 | python inference_video.py --multi=%RIFE_MULTI_NUM% --video %SRC_PATH% %RIFE_FPS_OPTION% --output %RIFE_DEST_PATH%
51 |
52 | echo ffmpeg.exe -y -i %RIFE_DEST_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation %FFMPEG_FPS_OPTION% -crf %FFMPEG_CRF% %FFMPEG_DEST_PATH%
53 | ffmpeg.exe -y -i %RIFE_DEST_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation %FFMPEG_FPS_OPTION% -crf %FFMPEG_CRF% %FFMPEG_DEST_PATH%
54 |
55 | call venv\Scripts\deactivate.bat
56 | popd rem %~dp0Practical-RIFE
57 |
--------------------------------------------------------------------------------
/Frames2Mp4.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the frames directory.
4 | pause & exit /b 1
5 | )
6 |
7 | set PATH=%~dp0ffmpeg-master-latest-win64-gpl\bin;%PATH%
8 |
9 | set FRAMES_DIR=%~dpn1
10 | set FPS=10
11 | set CRF=20
12 | if not "%2" == "" ( set FPS=%2 )
13 | if not "%3" == "" ( set CRF=%3 )
14 |
15 | pushd %~dp0animatediff-cli-prompt-travel
16 |
17 | echo ffmpeg.exe -y -framerate %FPS% -i "%FRAMES_DIR%\%%08d.png" -pix_fmt yuv420p -vcodec libx264 -tune animation -r %FPS% -crf %CRF% "%FRAMES_DIR%.mp4"
18 |
19 | ffmpeg.exe -y -framerate %FPS%^
20 | -i "%FRAMES_DIR%\%%08d.png"^
21 | -pix_fmt yuv420p^
22 | -vcodec libx264^
23 | -tune animation^
24 | -r %FPS%^
25 | -crf %CRF%^
26 | "%FRAMES_DIR%.mp4"
27 |
28 | popd rem %~dp0animatediff-cli-prompt-travel
29 |
--------------------------------------------------------------------------------
/Generate.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo.
3 | set PATH=%~dp0ffmpeg-master-latest-win64-gpl\bin;%PATH%
4 |
5 | set LENGTH=30
6 | set WIDTH=384
7 | set HEIGHT=512
8 | set CONTEXT=16
9 |
10 | set RIFE_DIV=2
11 | set RIFE_FPS=0
12 | set FFMPEG_FPS=0
13 | set FFMPEG_CRF=20
14 | set TILE_UPSCALE_FPS=10
15 |
16 | set /a REFINER_CONTEXT=%CONTEXT%/2
17 |
18 | set UPSCALE1=
19 | set UPSCALE1_HEIGHT=
20 | set UPSCALE2=
21 | set UPSCALE2_HEIGHT=
22 |
23 | set GENERATE_USE_XFORMERS=
24 | set GENERATE_FORCE_HALF_VAE=
25 | set UPSCALE_USE_XFORMERS=
26 | set UPSCALE_FORCE_HALF_VAE=
27 |
28 | if "%~1" == "" (
29 | echo [ERROR] Drag and drop the configuration .+[OPTIONS].json file.
30 | echo -L[Lenght]-W[Width]-H[Height]-C[Context]-T[TileHeight]
31 | pause & exit /b 1
32 | )
33 |
34 | set CONFIG_PATH=%~f1
35 | set CONFIG_DIR=%~dp1
36 | set CONFIG_TEMP_PATH=temp\%~nx1
37 |
38 | pushd %~dp0animatediff-cli-prompt-travel
39 | call venv\Scripts\activate.bat
40 |
41 | for /f "tokens=2-12 delims=-" %%a in ("%~n1") do (
42 | call :PARSE_ARG %%a
43 | call :PARSE_ARG %%b
44 | call :PARSE_ARG %%c
45 | call :PARSE_ARG %%d
46 | call :PARSE_ARG %%e
47 | call :PARSE_ARG %%f
48 | call :PARSE_ARG %%g
49 | call :PARSE_ARG %%h
50 | call :PARSE_ARG %%i
51 | call :PARSE_ARG %%j
52 | call :PARSE_ARG %%k
53 | )
54 |
55 | if not exist temp\ ( mkdir temp )
56 | copy /Y "%CONFIG_PATH%" "%CONFIG_TEMP_PATH%" > nul
57 |
58 | echo CONFIG_PATH: %CONFIG_PATH%
59 | echo generate -L %LENGTH% -W %WIDTH% -H %HEIGHT% -C %CONTEXT%
60 | if not "%UPSCALE1%" == "" ( echo %UPSCALE1% -H %UPSCALE1_HEIGHT% )
61 | if not "%UPSCALE2%" == "" ( echo %UPSCALE2% -H %UPSCALE2_HEIGHT% )
62 | echo.
63 |
64 | echo animatediff generate -L %LENGTH% -W %WIDTH% -H %HEIGHT% -C %CONTEXT% -c "%CONFIG_TEMP_PATH%" %GENERATE_USE_XFORMERS% %GENERATE_FORCE_HALF_VAE%
65 | animatediff generate -L %LENGTH% -W %WIDTH% -H %HEIGHT% -C %CONTEXT% -c "%CONFIG_TEMP_PATH%" %GENERATE_USE_XFORMERS% %GENERATE_FORCE_HALF_VAE%
66 | if %ERRORLEVEL% neq 0 ( goto :ERROR_EXIT )
67 |
68 | call :FIND_NEW_DIR output
69 | set GENERATE_DIR=output\%NEW_DIR%
70 | echo Generate: %GENERATE_DIR%
71 |
72 | call :FIND_NEW_DIR %GENERATE_DIR%
73 | set GENERATE_FRAMES_DIR=%GENERATE_DIR%\%NEW_DIR%
74 | echo GenerateFrames: %GENERATE_FRAMES_DIR%
75 |
76 | setlocal enabledelayedexpansion
77 |
78 | if "%UPSCALE1%" == "" (
79 | copy /Y !GENERATE_DIR!\*.mp4 "%CONFIG_DIR%" > nul
80 | goto :END
81 | ) else if "%UPSCALE1%" == "tile-upscale" (
82 | echo animatediff %UPSCALE1% -H %UPSCALE1_HEIGHT% %GENERATE_FRAMES_DIR% %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
83 | animatediff %UPSCALE1% -H %UPSCALE1_HEIGHT% %GENERATE_FRAMES_DIR% %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
84 | if %ERRORLEVEL% neq 0 ( endlocal & goto :ERROR_EXIT )
85 |
86 | call :FIND_NEW_DIR upscaled
87 | set UPSCALE1_DIR=upscaled\!NEW_DIR!
88 |
89 | call :FIND_NEW_DIR !UPSCALE1_DIR!
90 | set UPSCALE1_FRAMES_DIR=!UPSCALE1_DIR!\!NEW_DIR!
91 |
92 | @REM echo Frames2Mp4.bat !UPSCALE1_FRAMES_DIR! %TILE_UPSCALE_FPS% %FFMPEG_CRF%
93 | @REM call ..\Frames2Mp4.bat "!UPSCALE1_FRAMES_DIR!" %TILE_UPSCALE_FPS% %FFMPEG_CRF%
94 | ) else if "%UPSCALE1%" == "refine" (
95 | echo animatediff %UPSCALE1% -H %UPSCALE1_HEIGHT% -C %REFINER_CONTEXT% %GENERATE_FRAMES_DIR% %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
96 | animatediff %UPSCALE1% -H %UPSCALE1_HEIGHT% -C %REFINER_CONTEXT% %GENERATE_FRAMES_DIR% %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
97 | if %ERRORLEVEL% neq 0 ( endlocal & goto :ERROR_EXIT )
98 |
99 | call :FIND_NEW_DIR refine
100 | set UPSCALE1_DIR=refine\!NEW_DIR!
101 | call :FIND_NEW_DIR !UPSCALE1_DIR!
102 | set UPSCALE1_DIR=!UPSCALE1_DIR!\!NEW_DIR!
103 |
104 | call :FIND_NEW_DIR !UPSCALE1_DIR!
105 | set UPSCALE1_FRAMES_DIR=!UPSCALE1_DIR!\!NEW_DIR!
106 | ) else (
107 | echo [ERROR] Unknown upscale type: %UPSCALE1%
108 | pause
109 | goto :END
110 | )
111 |
112 | echo UPSCALE1_DIR: !UPSCALE1_DIR!
113 | echo UPSCALE1_FRAMES_DIR: !UPSCALE1_FRAMES_DIR!
114 | echo UPSCALE2: !UPSCALE2!
115 |
116 | if "%UPSCALE2%" == "" (
117 | copy /Y !UPSCALE1_DIR!\*.mp4 "%CONFIG_DIR%" > nul
118 | goto :END
119 | ) else if "%UPSCALE2%" == "tile-upscale" (
120 | echo animatediff %UPSCALE2% -H %UPSCALE2_HEIGHT% !UPSCALE1_FRAMES_DIR! %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
121 | animatediff %UPSCALE2% -H %UPSCALE2_HEIGHT% !UPSCALE1_FRAMES_DIR! %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
122 | if %ERRORLEVEL% neq 0 ( endlocal & goto :ERROR_EXIT )
123 |
124 | call :FIND_NEW_DIR upscaled
125 | set UPSCALE2_DIR=upscaled\!NEW_DIR!
126 |
127 | call :FIND_NEW_DIR !UPSCALE2_DIR!
128 | set UPSCALE2_FRAMES_DIR=!UPSCALE2_DIR!\!NEW_DIR!
129 |
130 | @REM echo Frames2Mp4.bat !UPSCALE2_FRAMES_DIR! %TILE_UPSCALE_FPS% %FFMPEG_CRF%
131 | @REM call ..\Frames2Mp4.bat "!UPSCALE2_FRAMES_DIR!" %TILE_UPSCALE_FPS% %FFMPEG_CRF%
132 | ) else if "%UPSCALE2%" == "refine" (
133 | echo animatediff %UPSCALE2% -H %UPSCALE2_HEIGHT% -C %REFINER_CONTEXT% !UPSCALE1_FRAMES_DIR! %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
134 | animatediff %UPSCALE2% -H %UPSCALE2_HEIGHT% -C %REFINER_CONTEXT% !UPSCALE1_FRAMES_DIR! %UPSCALE_USE_XFORMERS% %UPSCALE_FORCE_HALF_VAE%
135 | if %ERRORLEVEL% neq 0 ( endlocal & goto :ERROR_EXIT )
136 |
137 | call :FIND_NEW_DIR refine
138 | set UPSCALE2_DIR=refine\!NEW_DIR!
139 | call :FIND_NEW_DIR !UPSCALE2_DIR!
140 | set UPSCALE2_DIR=!UPSCALE2_DIR!\!NEW_DIR!
141 |
142 | call :FIND_NEW_DIR !UPSCALE2_DIR!
143 | set UPSCALE2_FRAMES_DIR=!UPSCALE2_DIR!\!NEW_DIR!
144 | ) else (
145 | echo [ERROR] Unknown upscale type: %UPSCALE1%
146 | pause
147 | goto :END
148 | )
149 |
150 | echo UPSCALE2_DIR: !UPSCALE2_DIR!
151 | echo UPSCALE2_FRAMES_DIR: !UPSCALE2_FRAMES_DIR!
152 | copy /Y !UPSCALE2_DIR!\*.mp4 "%CONFIG_DIR%" > nul
153 |
154 | :END
155 | endlocal
156 | call venv\Scripts\deactivate.bat
157 | popd rem %~dp0animatediff-cli-prompt-travel
158 |
159 | set ZERO_PADDING_TIME=%TIME: =0%
160 | for /f "tokens=2-7 delims=/:. " %%a in ("echo %DATE% %ZERO_PADDING_TIME%") do (
161 | set MMDD_HHMM_SS=%%b%%c_%%d%%e_%%f
162 | )
163 | call :FIND_NEW_MP4 "%CONFIG_DIR%"
164 | rename "%CONFIG_DIR%\%NEW_MP4%" "%MMDD_HHMM_SS%-%NEW_MP4:~3%"
165 |
166 | if "%RIFE_DIV%" == "0" ( exit /b 0 )
167 | call :FIND_NEW_MP4 "%CONFIG_DIR%"
168 |
169 | echo FpsX4.bat "%CONFIG_DIR%\%NEW_MP4%" %RIFE_DIV% %RIFE_FPS% %FFMPEG_FPS% %FFMPEG_CRF%
170 | call %~dp0FpsX4.bat "%CONFIG_DIR%\%NEW_MP4%" %RIFE_DIV% %RIFE_FPS% %FFMPEG_FPS% %FFMPEG_CRF%
171 |
172 | exit /b 0
173 |
174 | :ERROR_EXIT
175 | call venv\Scripts\deactivate.bat
176 | popd rem %~dp0animatediff-cli-prompt-travel
177 | echo [ERROR EXIT]
178 | exit /b 1
179 |
180 | :PARSE_ARG
181 | if "%~1" == "" ( exit /b 0 )
182 | set ARG=%1
183 | set ARG_KEY=%ARG:~0,1%
184 | set ARG_VALUE=%ARG:~1%
185 |
186 | if "%ARG_KEY%" == "L" (
187 | set LENGTH=%ARG_VALUE%
188 | ) else if "%ARG_KEY%" == "W" (
189 | set WIDTH=%ARG_VALUE%
190 | ) else if "%ARG_KEY%" == "H" (
191 | set HEIGHT=%ARG_VALUE%
192 | ) else if "%ARG_KEY%" == "C" (
193 | set CONTEXT=%ARG_VALUE%
194 | ) else if "%ARG_KEY%" == "T" (
195 | if "%UPSCALE1%" == "" (
196 | set UPSCALE1=tile-upscale
197 | set UPSCALE1_HEIGHT=%ARG_VALUE%
198 | ) else if "%UPSCALE2%" == "" (
199 | set UPSCALE2=tile-upscale
200 | set UPSCALE2_HEIGHT=%ARG_VALUE%
201 | )
202 | ) else if "%ARG_KEY%" == "R" (
203 | if "%UPSCALE1%" == "" (
204 | set UPSCALE1=refine
205 | set UPSCALE1_HEIGHT=%ARG_VALUE%
206 | ) else if "%UPSCALE2%" == "" (
207 | set UPSCALE2=refine
208 | set UPSCALE2_HEIGHT=%ARG_VALUE%
209 | )
210 | ) else if "%ARG_KEY%" == "D" (
211 | set RIFE_DIV=%ARG_VALUE%
212 | ) else if "%ARG_KEY%" == "I" (
213 | set RIFE_FPS=%ARG_VALUE%
214 | ) else if "%ARG_KEY%" == "F" (
215 | set FFMPEG_FPS=%ARG_VALUE%
216 | ) else if "%ARG_KEY%" == "M" (
217 | set FFMPEG_CRF=%ARG_VALUE%
218 | ) else if "%ARG_KEY%" == "U" (
219 | set TILE_UPSCALE_FPS=%ARG_VALUE%
220 | ) else if "%ARG_KEY%" == "x" (
221 | set GENERATE_USE_XFORMERS=--xformers
222 | ) else if "%ARG_KEY%" == "v" (
223 | set GENERATE_FORCE_HALF_VAE=--half-vae
224 | ) else if "%ARG_KEY%" == "X" (
225 | set UPSCALE_USE_XFORMERS=--xformers
226 | ) else if "%ARG_KEY%" == "V" (
227 | set UPSCALE_FORCE_HALF_VAE=--half-vae
228 | ) else (
229 | echo [ERROR] Unknown argument: %ARG%
230 | pause
231 | )
232 | exit /b 0
233 |
234 | :FIND_NEW_DIR
235 | set NEW_DIR=
236 | set DIR_COMMAND=dir /b /ad /o-d /tw %1
237 | for /f %%d in ('%DIR_COMMAND%') do (
238 | set NEW_DIR=%%d
239 | exit /b 0
240 | )
241 | exit /b 0
242 |
243 | :FIND_NEW_MP4
244 | set NEW_MP4=
245 | set DIR_COMMAND=dir /b /a-d /o-d /tw "%~1*.mp4"
246 | for /f %%f in ('%DIR_COMMAND%') do (
247 | set NEW_MP4=%%f
248 | exit /b 0
249 | )
250 | exit /b 0
251 |
--------------------------------------------------------------------------------
/GenerateFolder.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" ( goto ERROR_EXIT )
3 | if not exist "%~1\*" ( goto ERROR_EXIT )
4 | if not exist "%~1\*.json" ( goto ERROR_EXIT )
5 |
6 | for /f %%a in ('dir /b "%~1\*.json"') do (
7 | call "%~dp0Generate.bat" "%~f1\%%a"
8 | @REM if !ERRORLEVEL! neq 0 ( pause & exit /b 1 )
9 | )
10 | exit /b 0
11 |
12 | :ERROR_EXIT
13 | echo [ERROR] Drag and drop the FOLDER containing the configuration .+[OPTIONS].json files.
14 | echo -L[Lenght]-W[Width]-H[Height]-C[Context]-T[TileHeight]-R[RefineHeight]
15 | pause
16 | exit /b 1
17 |
--------------------------------------------------------------------------------
/GenerateFolderForever.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | :BEGIN
3 | call "%~dp0GenerateFolder.bat" "%~1"
4 | if %ERRORLEVEL% neq 0 ( exit /b 1 )
5 | timeout /t 3 /nobreak >nul
6 | goto BEGIN
7 |
--------------------------------------------------------------------------------
/GenerateForever.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | :BEGIN
3 | call "%~dp0Generate.bat" "%~1"
4 | if %ERRORLEVEL% neq 0 ( exit /b 1 )
5 | goto BEGIN
6 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Zuntan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Mp4Crf.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the mp4 file.
4 | pause & exit /b 1
5 | )
6 |
7 | set PATH=%~dp0ffmpeg-master-latest-win64-gpl\bin;%PATH%
8 |
9 | set CRF=20
10 | if not "%~2" == "" if not "%~2" == "0" (
11 | set CRF=%~2
12 | )
13 |
14 | set SRC_PATH="%~f1"
15 | set DEST_PATH="%~dpn1-crf%CRF%.mp4"
16 |
17 | pushd %~dp0animatediff-cli-prompt-travel
18 |
19 | echo ffmpeg.exe -y -i %SRC_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation -crf %CRF% %DEST_PATH%
20 | ffmpeg.exe -y -i %SRC_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation -crf %CRF% %DEST_PATH%
21 |
22 | popd rem %~dp0animatediff-cli-prompt-travel
23 |
--------------------------------------------------------------------------------
/Mp4Crf26.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0Mp4Crf.bat" "%~1" 26
3 |
--------------------------------------------------------------------------------
/Mp4Crf32.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0Mp4Crf.bat" "%~1" 32
3 |
--------------------------------------------------------------------------------
/Mp4Crf38.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0Mp4Crf.bat" "%~1" 38
3 |
--------------------------------------------------------------------------------
/OpenColabEditor.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | start https://colab.research.google.com/drive/1XeVRMmw-dyALMacKU-_Xj2nMboZL_TM3
3 |
--------------------------------------------------------------------------------
/Update.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0
4 | git pull
5 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
6 | popd rem %~dp0
7 |
8 | call "%~dp0src\Update.bat"
9 |
--------------------------------------------------------------------------------
/XMp4.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%~1" == "" (
3 | echo [ERROR] Drag and drop the mp4 file.
4 | pause & exit /b 1
5 | )
6 |
7 | set PATH=%~dp0ffmpeg-master-latest-win64-gpl\bin;%PATH%
8 |
9 | set SRC_PATH="%~f1"
10 | set DEST_PATH="%~dpn1x.mp4"
11 |
12 | set VF_SCALE=
13 | if not "%~2" == "" if not "%~2" == "0" (
14 | set VF_SCALE=-vf scale=%~2
15 | )
16 |
17 | set FPS=-r 40
18 | if not "%~3" == "" (
19 | if "%~3" == "0" (
20 | set FPS=
21 | ) else (
22 | set FPS=-r %~3
23 | )
24 | )
25 |
26 | set BPS=25M
27 | if not "%~4" == "" if not "%~4" == "0" (
28 | set BPS=%~4
29 | )
30 |
31 | pushd %~dp0animatediff-cli-prompt-travel
32 |
33 | echo ffmpeg.exe -y -i %SRC_PATH% -pix_fmt yuv420p -vcodec libx264 -tune animation %FPS% -vb %BPS% -maxrate %BPS% -bufsize %BPS% %VF_SCALE% %DEST_PATH%
34 |
35 | ffmpeg.exe -y^
36 | -i %SRC_PATH%^
37 | -pix_fmt yuv420p^
38 | -vcodec libx264^
39 | -tune animation^
40 | %FPS%^
41 | -vb %BPS%^
42 | -maxrate %BPS%^
43 | -bufsize %BPS%^
44 | %VF_SCALE%^
45 | %DEST_PATH%
46 |
47 | popd rem %~dp0animatediff-cli-prompt-travel
48 |
49 |
--------------------------------------------------------------------------------
/XMp4H1200.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0XMp4.bat" "%~1" -1:1200 %2 %3
3 |
--------------------------------------------------------------------------------
/XMp4H1900.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0XMp4.bat" "%~1" -1:1900 %2 %3
3 |
--------------------------------------------------------------------------------
/XMp4W1200.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0XMp4.bat" "%~1" 1200:-1 %2 %3
3 |
--------------------------------------------------------------------------------
/XMp4W1920.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0XMp4.bat" "%~1" 1920:-1 %2 %3
3 |
--------------------------------------------------------------------------------
/doc/img/title.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zuntan03/EasyPromptAnime/a23c46674f2308b25705bfac5720418afe2eb036/doc/img/title.webp
--------------------------------------------------------------------------------
/editor/DownloadMenu.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "featurelessMix_v2020237Clearvae.safetensors": {
4 | "url": "https://civitai.com/api/download/models/168615",
5 | "info": "https://civitai.com/models/65202"
6 | },
7 | "featurelessCuteMix_v10Clearvae.safetensors": {
8 | "url": "https://civitai.com/api/download/models/168584",
9 | "info": "https://civitai.com/models/149907"
10 | },
11 | "featurelessFlat2DMix_v10.safetensors": {
12 | "url": "https://civitai.com/api/download/models/180234",
13 | "info": "https://civitai.com/models/160209"
14 | },
15 | "featurelessJRPG3DMix_v10.safetensors": {
16 | "url": "https://civitai.com/api/download/models/174474",
17 | "info": "https://civitai.com/models/155538"
18 | },
19 | "featureless25DMix_v10.safetensors": {
20 | "url": "https://civitai.com/api/download/models/201448",
21 | "info": "https://civitai.com/models/179512"
22 | },
23 | "manmaruMix_v30.safetensors": {
24 | "url": "https://civitai.com/api/download/models/188171",
25 | "info": "https://civitai.com/models/86277"
26 | },
27 | "onigiriMix_v10Clearvae.safetensors": {
28 | "url": "https://civitai.com/api/download/models/168600",
29 | "info": "https://civitai.com/models/146434"
30 | },
31 | "nadenadesitai_v10.safetensors": {
32 | "url": "https://civitai.com/api/download/models/84669",
33 | "info": "https://civitai.com/models/79846"
34 | },
35 | "coffeemix_v20.safetensors": {
36 | "url": "https://civitai.com/api/download/models/53475",
37 | "info": "https://civitai.com/models/40630"
38 | },
39 | "aziibpixelmix_v10.safetensors": {
40 | "url": "https://civitai.com/api/download/models/220049",
41 | "info": "https://civitai.com/models/195730",
42 | "tag": "pixel"
43 | },
44 | "1990s2000s_v10.safetensors": {
45 | "url": "https://civitai.com/api/download/models/179913",
46 | "info": "https://civitai.com/models/159947",
47 | "tag": "manga"
48 | },
49 | "aniverse_thxEd14Pruned.safetensors": {
50 | "url": "https://civitai.com/api/download/models/165868",
51 | "info": "https://civitai.com/models/107842",
52 | "tag": "semi-real"
53 | },
54 | "realcartoon3d_v8.safetensors": {
55 | "url": "https://civitai.com/api/download/models/159751",
56 | "info": "https://civitai.com/models/94809",
57 | "tag": "semi-real"
58 | },
59 | "xxmix9realistic_v40.safetensors": {
60 | "url": "https://civitai.com/api/download/models/102222",
61 | "info": "https://civitai.com/models/47274",
62 | "tag": "real"
63 | },
64 | "majicmixRealistic_v7.safetensors": {
65 | "url": "https://civitai.com/api/download/models/176425",
66 | "info": "https://civitai.com/models/43331",
67 | "tag": "real"
68 | },
69 | "real_model_N.safetensors": {
70 | "url": "https://huggingface.co/fcski/real_model_L/resolve/main/real_model_N.safetensors",
71 | "info": "https://huggingface.co/fcski/real_model_L",
72 | "tag": "real"
73 | }
74 | },
75 | "lora": {
76 | "flat2.safetensors": {
77 | "url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/release/flat2.safetensors",
78 | "info": "https://huggingface.co/2vXpSwA7/iroiro-lora"
79 | },
80 | "3dSlider_v2.safetensors": {
81 | "url": "https://civitai.com/api/download/models/178752",
82 | "info": "https://civitai.com/models/154017"
83 | },
84 | "HeightRatioSlider_v2.safetensors": {
85 | "url": "https://civitai.com/api/download/models/173158",
86 | "info": "https://civitai.com/models/153226"
87 | },
88 | "flatBG.safetensors": {
89 | "url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/release/flatBG.safetensors",
90 | "info": "https://huggingface.co/2vXpSwA7/iroiro-lora"
91 | }
92 | },
93 | "motion_module": {
94 | "mm_sd_v15_v2.ckpt": {
95 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v15_v2.ckpt",
96 | "info": "https://huggingface.co/guoyww/animatediff"
97 | },
98 | "missionarymotion.safetensors": {
99 | "url": "https://civitai.com/api/download/models/134809",
100 | "info": "https://civitai.com/models/123612",
101 | "tag": "NSFW",
102 | "isZip": true
103 | },
104 | "improvedHumansMotion_refinedHumanMovement.ckpt": {
105 | "url": "https://civitai.com/api/download/models/174464",
106 | "info": "https://civitai.com/models/155528"
107 | },
108 | "temporaldiffMotion_v10.ckpt": {
109 | "url": "https://civitai.com/api/download/models/160418",
110 | "info": "https://civitai.com/models/144934"
111 | },
112 | "mm-Stabilized_high.pth": {
113 | "url": "https://huggingface.co/manshoety/AD_Stabilized_Motion/resolve/main/mm-Stabilized_high.pth",
114 | "info": "https://huggingface.co/manshoety/AD_Stabilized_Motion"
115 | },
116 | "mm-Stabilized_mid.pth": {
117 | "url": "https://huggingface.co/manshoety/AD_Stabilized_Motion/resolve/main/mm-Stabilized_mid.pth",
118 | "info": "https://huggingface.co/manshoety/AD_Stabilized_Motion"
119 | },
120 | "mm_sd_v15.ckpt": {
121 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v15.ckpt",
122 | "info": "https://huggingface.co/guoyww/animatediff"
123 | },
124 | "mm_sd_v14.ckpt": {
125 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v14.ckpt",
126 | "info": "https://huggingface.co/guoyww/animatediff"
127 | },
128 | "yoinkoorlabsNSFWMotion_godmodev20.ckpt": {
129 | "url": "https://civitai.com/api/download/models/177016",
130 | "info": "https://civitai.com/models/144354",
131 | "tag": "NSFW"
132 | }
133 | },
134 | "motion_lora": {
135 | "v2_lora_PanLeft.ckpt": {
136 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_PanLeft.ckpt",
137 | "info": "https://huggingface.co/guoyww/animatediff"
138 | },
139 | "v2_lora_PanRight.ckpt": {
140 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_PanRight.ckpt",
141 | "info": "https://huggingface.co/guoyww/animatediff"
142 | },
143 | "v2_lora_RollingAnticlockwise.ckpt": {
144 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_RollingAnticlockwise.ckpt",
145 | "info": "https://huggingface.co/guoyww/animatediff"
146 | },
147 | "v2_lora_RollingClockwise.ckpt": {
148 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_RollingClockwise.ckpt",
149 | "info": "https://huggingface.co/guoyww/animatediff"
150 | },
151 | "v2_lora_TiltDown.ckpt": {
152 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_TiltDown.ckpt",
153 | "info": "https://huggingface.co/guoyww/animatediff"
154 | },
155 | "v2_lora_TiltUp.ckpt": {
156 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_TiltUp.ckpt",
157 | "info": "https://huggingface.co/guoyww/animatediff"
158 | },
159 | "v2_lora_ZoomIn.ckpt": {
160 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_ZoomIn.ckpt",
161 | "info": "https://huggingface.co/guoyww/animatediff"
162 | },
163 | "v2_lora_ZoomOut.ckpt": {
164 | "url": "https://huggingface.co/guoyww/animatediff/resolve/main/v2_lora_ZoomOut.ckpt",
165 | "info": "https://huggingface.co/guoyww/animatediff"
166 | }
167 | },
168 | "vae": {
169 | "kl-f8-anime2.ckpt": {
170 | "url": "https://huggingface.co/hakurei/waifu-diffusion-v1-4/resolve/main/vae/kl-f8-anime2.ckpt",
171 | "info": "https://huggingface.co/hakurei/waifu-diffusion-v1-4"
172 | },
173 | "orangemix.vae.pt": {
174 | "url": "https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt",
175 | "info": "https://huggingface.co/WarriorMama777/OrangeMixs"
176 | },
177 | "vae-ft-ema-560000-ema-pruned.safetensors": {
178 | "url": "https://huggingface.co/stabilityai/sd-vae-ft-ema-original/resolve/main/vae-ft-ema-560000-ema-pruned.safetensors",
179 | "info": "https://huggingface.co/stabilityai/sd-vae-ft-ema-original"
180 | },
181 | "vae-ft-mse-840000-ema-pruned.safetensors": {
182 | "url": "https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors",
183 | "info": "https://huggingface.co/stabilityai/sd-vae-ft-mse-original"
184 | },
185 | "clearvae_v22.safetensors": {
186 | "url": "https://civitai.com/api/download/models/80518",
187 | "info": "https://civitai.com/models/22354"
188 | },
189 | "liquid111vae_v10.safetensors": {
190 | "url": "https://civitai.com/api/download/models/90456",
191 | "info": "https://civitai.com/models/85106"
192 | }
193 | },
194 | "embedding": {
195 | "EasyNegative.safetensors": {
196 | "url": "https://huggingface.co/datasets/gsdf/EasyNegative/resolve/main/EasyNegative.safetensors",
197 | "info": "https://huggingface.co/datasets/gsdf/EasyNegative"
198 | },
199 | "EasyNegativeV2.safetensors": {
200 | "url": "https://huggingface.co/gsdf/Counterfeit-V3.0/resolve/main/embedding/EasyNegativeV2.safetensors",
201 | "info": "https://huggingface.co/gsdf/Counterfeit-V3.0"
202 | }
203 | }
204 | }
--------------------------------------------------------------------------------
/editor/bat/refine.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo.
3 | set PATH=%~dp0..\..\ffmpeg-master-latest-win64-gpl\bin;%PATH%
4 |
5 | set RIFE_DIV=3
6 | set RIFE_FPS=0
7 | set FFMPEG_FPS=0
8 | set FFMPEG_CRF=20
9 |
10 | pushd %~dp0..\..\animatediff-cli-prompt-travel
11 | call venv\Scripts\activate.bat
12 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
13 |
14 | echo animatediff refine %*
15 | animatediff refine %*
16 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
17 |
18 | setlocal enabledelayedexpansion
19 |
20 | call :FIND_NEW_DIR refine
21 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
22 | set REFINE_DIR=refine\!NEW_DIR!
23 | echo REFINE_DIR: %REFINE_DIR%
24 |
25 | call :FIND_NEW_DIR %REFINE_DIR%
26 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
27 | set UPSCALE_DIR=%REFINE_DIR%\!NEW_DIR!
28 | echo UPSCALE_DIR: %UPSCALE_DIR%
29 |
30 | call :FIND_NEW_MP4 "%UPSCALE_DIR%\"
31 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
32 |
33 | set ZERO_PADDING_TIME=%TIME: =0%
34 | for /f "tokens=2-7 delims=/:. " %%a in ("echo %DATE% %ZERO_PADDING_TIME%") do (
35 | set YYYY_MMDD_HHMM_SS=%%a_%%b%%c_%%d%%e_%%f
36 | )
37 |
38 | rename %UPSCALE_DIR%\!NEW_MP4! %YYYY_MMDD_HHMM_SS%-!NEW_MP4:~3!
39 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
40 |
41 | set UPSCALE_MP4=%UPSCALE_DIR%\%YYYY_MMDD_HHMM_SS%-!NEW_MP4:~3!
42 | echo UPSCALE_MP4: %UPSCALE_MP4%
43 |
44 | echo FpsX4.bat "%UPSCALE_MP4%" %RIFE_DIV% %RIFE_FPS% %FFMPEG_FPS% %FFMPEG_CRF%
45 | call "%~dp0..\..\FpsX4.bat" "%UPSCALE_MP4%" %RIFE_DIV% %RIFE_FPS% %FFMPEG_FPS% %FFMPEG_CRF%
46 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
47 |
48 | endlocal
49 | popd rem %~dp0..\..\animatediff-cli-prompt-travel
50 | exit /b 0
51 |
52 | :FIND_NEW_DIR
53 | set NEW_DIR=
54 | set DIR_COMMAND=dir /b /ad /o-d /tw %1
55 | for /f %%d in ('%DIR_COMMAND%') do (
56 | set NEW_DIR=%%d
57 | exit /b 0
58 | )
59 | exit /b 0
60 |
61 | :FIND_NEW_MP4
62 | set NEW_MP4=
63 | set DIR_COMMAND=dir /b /a-d /o-d /tw "%~1*.mp4"
64 | for /f %%f in ('%DIR_COMMAND%') do (
65 | set NEW_MP4=%%f
66 | exit /b 0
67 | )
68 | exit /b 0
69 |
--------------------------------------------------------------------------------
/editor/bat/tile-upscale.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo.
3 | set PATH=%~dp0..\..\ffmpeg-master-latest-win64-gpl\bin;%PATH%
4 |
5 | set RIFE_DIV=3
6 | set RIFE_FPS=0
7 | set FFMPEG_FPS=0
8 | set FFMPEG_CRF=20
9 |
10 | pushd %~dp0..\..\animatediff-cli-prompt-travel
11 | call venv\Scripts\activate.bat
12 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
13 |
14 | echo animatediff tile-upscale %*
15 | animatediff tile-upscale %*
16 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
17 |
18 | setlocal enabledelayedexpansion
19 |
20 | call :FIND_NEW_DIR upscaled
21 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
22 | set UPSCALE_DIR=upscaled\!NEW_DIR!
23 | echo UPSCALE_DIR: %UPSCALE_DIR%
24 |
25 | set ZERO_PADDING_TIME=%TIME: =0%
26 | for /f "tokens=2-7 delims=/:. " %%a in ("echo %DATE% %ZERO_PADDING_TIME%") do (
27 | set YYYY_MMDD_HHMM_SS=%%a_%%b%%c_%%d%%e_%%f
28 | )
29 |
30 | call :FIND_NEW_MP4 "%UPSCALE_DIR%\"
31 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
32 |
33 | rename %UPSCALE_DIR%\!NEW_MP4! %YYYY_MMDD_HHMM_SS%-!NEW_MP4:~3!
34 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
35 |
36 | set UPSCALE_MP4=%UPSCALE_DIR%\%YYYY_MMDD_HHMM_SS%-!NEW_MP4:~3!
37 | echo UPSCALE_MP4: %UPSCALE_MP4%
38 |
39 | echo FpsX4.bat "%UPSCALE_MP4%" %RIFE_DIV% %RIFE_FPS% %FFMPEG_FPS% %FFMPEG_CRF%
40 | call "%~dp0..\..\FpsX4.bat" "%UPSCALE_MP4%" %RIFE_DIV% %RIFE_FPS% %FFMPEG_FPS% %FFMPEG_CRF%
41 | if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
42 |
43 | endlocal
44 | popd rem %~dp0..\..\animatediff-cli-prompt-travel
45 | exit /b 0
46 |
47 | :FIND_NEW_DIR
48 | set NEW_DIR=
49 | set DIR_COMMAND=dir /b /ad /o-d /tw %1
50 | for /f %%d in ('%DIR_COMMAND%') do (
51 | set NEW_DIR=%%d
52 | exit /b 0
53 | )
54 | exit /b 0
55 |
56 | :FIND_NEW_MP4
57 | set NEW_MP4=
58 | set DIR_COMMAND=dir /b /a-d /o-d /tw "%~1*.mp4"
59 | for /f %%f in ('%DIR_COMMAND%') do (
60 | set NEW_MP4=%%f
61 | exit /b 0
62 | )
63 | exit /b 0
64 |
--------------------------------------------------------------------------------
/editor/src/EasyPromptAnimeEditor.py:
--------------------------------------------------------------------------------
1 | import os, time
2 | from const import Path
3 | from config import Config
4 | from l10n import L10n
5 | from log import Log
6 | from ui import Form
7 | from model import Model
8 | from controller import Controller
9 | from task import Task
10 |
11 | initStartTime = time.perf_counter()
12 |
13 | Config.load(
14 | Path.ini,
15 | ["default", "default_controlnet", "ui", "ui_color", "ui_preview", "ui_size"],
16 | )
17 |
18 | L10n.loadConfig()
19 |
20 |
21 | class EasyPromptAnimeEditor:
22 | def __init__(self):
23 | Path.cwd = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
24 | os.chdir(Path.cwd)
25 |
26 | ffmpegBinPath = os.path.join(Path.cwd, "ffmpeg-master-latest-win64-gpl", "bin")
27 | os.environ["PATH"] = f'{ffmpegBinPath};{os.environ["PATH"]}'
28 |
29 | Form.loadConfig()
30 | self.form = Form()
31 |
32 | Model.loadConfig()
33 | self.model = Model()
34 |
35 | Task.initialize(self.form.win)
36 |
37 | self.controller = Controller(self.form, self.model)
38 |
39 |
40 | editor = EasyPromptAnimeEditor()
41 |
42 | Log.user(L10n.format("log_initialization_end", time.perf_counter() - initStartTime))
43 | Log.user(L10n.get("log_see_prompt_help"))
44 |
45 | editor.form.run()
46 |
--------------------------------------------------------------------------------
/editor/src/config.py:
--------------------------------------------------------------------------------
1 | from configparser import ConfigParser
2 |
3 |
4 | class Config:
5 | @classmethod
6 | def get(cls, cateroty, key, fallback):
7 | return cls._config.get(cateroty, key, fallback=str(fallback))
8 |
9 | @classmethod
10 | def getBool(cls, cateroty, key, fallback):
11 | return cls._config.getboolean(cateroty, key, fallback=fallback)
12 |
13 | @classmethod
14 | def getInt(cls, cateroty, key, fallback):
15 | return cls._config.getint(cateroty, key, fallback=fallback)
16 |
17 | @classmethod
18 | def getFloat(cls, cateroty, key, fallback):
19 | return cls._config.getfloat(cateroty, key, fallback=fallback)
20 |
21 | @classmethod
22 | def set(cls, cateroty, key, value):
23 | cls._config[cateroty][key] = str(value)
24 |
25 | @classmethod
26 | def load(cls, path, sections):
27 | cls._config = ConfigParser(interpolation=None)
28 | [cls._config.add_section(section) for section in sections]
29 | cls._config.read(path, encoding="utf-8-sig")
30 |
31 | @classmethod
32 | def save(cls, path):
33 | with open(path, "w", encoding="utf-8-sig") as cfgfile:
34 | cls._config.write(cfgfile)
35 |
--------------------------------------------------------------------------------
/editor/src/const.py:
--------------------------------------------------------------------------------
1 | import os, datetime
2 | from enum import Enum
3 |
4 |
5 | class Const:
6 | tile = "tile-upscale"
7 | refine = "refine"
8 |
9 | schedulerValues = [
10 | "k_dpmpp_2m_sde",
11 | "k_dpmpp_sde",
12 | "k_dpmpp_2m",
13 | "ddim",
14 | "euler",
15 | "euler_a",
16 | "lcm",
17 | # "dpm_2_a",
18 | # "k_dpm_2_a",
19 | ]
20 | schedulerNames = [
21 | "DPM++ 2M SDE Karras",
22 | "DPM++ SDE Karras",
23 | "DPM++ 2M Karras",
24 | "DDIM",
25 | "Euler",
26 | "Euler a",
27 | "LCM",
28 | # "DPM2 a",
29 | # "DPM2 a Karras",
30 | ]
31 |
32 | @classmethod
33 | def getSchedulerValue(cls, name):
34 | return cls.schedulerValues[cls.schedulerNames.index(name)]
35 |
36 | @classmethod
37 | def getSchedulerName(cls, value):
38 | return cls.schedulerNames[cls.schedulerValues.index(value)]
39 |
40 |
41 | class ControlNetType(Enum):
42 | canny = 0
43 | depth = 1
44 | inpaint = 2
45 | ip2p = 3
46 | lineart = 4
47 | lineart_anime = 5
48 | mlsd = 6
49 | normalbae = 7
50 | openpose = 8
51 | scribble = 9
52 | seg = 10
53 | shuffle = 11
54 | softedge = 12
55 | tile = 13
56 |
57 |
58 | class Path:
59 | cwd = os.getcwd()
60 | ini = "EasyPromptAnimeEditor.ini"
61 | output = "output"
62 | save = "save"
63 |
64 | promptTravel = "animatediff-cli-prompt-travel"
65 | data = os.path.join(promptTravel, "data")
66 | model = os.path.join(data, "models", "sd")
67 | motionModule = os.path.join(data, "models", "motion-module")
68 | motionLora = os.path.join(data, "models", "motion_lora")
69 | vae = os.path.join(data, "vae")
70 | lora = os.path.join(data, "lora")
71 | embeddings = os.path.join(data, "embeddings")
72 | wildcard = os.path.join(promptTravel, "wildcards")
73 | promptTravelOutput = os.path.join(promptTravel, "output")
74 | promptTravelUpscaled = os.path.join(promptTravel, "upscaled")
75 | promptTravelRefined = os.path.join(promptTravel, "refine")
76 | controlNet = os.path.join(data, "controlnet_image")
77 | ipAdapter = os.path.join(data, "ip_adapter_image")
78 |
79 | editor = "editor"
80 | temp = os.path.join(editor, "temp")
81 | log = os.path.join(editor, "log")
82 | defaultPrompt = os.path.join(editor, "DefaultPrompt-0_1_0.txt")
83 | promptTravelTemplate = os.path.join(editor, "PromptTravelTemplate-0_6_2.txt")
84 | downloadMenu = os.path.join(editor, "DownloadMenu.json")
85 |
86 | @classmethod
87 | def getYYYYMMDDHHMMSS(cls):
88 | return datetime.datetime.now().strftime("%Y_%m%d_%H%M_%S")
89 |
90 | @classmethod
91 | def getYYYYMMDD(cls):
92 | return datetime.datetime.now().strftime("%Y_%m%d")
93 |
94 | @classmethod
95 | def getControlNet(cls, dirName, type):
96 | return os.path.join(cls.controlNet, dirName, f"controlnet_{type.name}")
97 |
--------------------------------------------------------------------------------
/editor/src/controller.py:
--------------------------------------------------------------------------------
1 | from const import Path
2 | from config import Config
3 | from l10n import L10n
4 | from ctr_file import FileController
5 | from ctr_menu import MenuController
6 | from ctr_input import InputController
7 | from ctr_preview import PreviewController
8 | from ctr_basic import BasicController
9 | from ctr_generate import GenerateController
10 | from ctr_control_net import ControlNetController
11 | from ctr_ip_adapter import IpAdapterController
12 | from ctr_upscale import UpscaleController
13 | from ctr_mosaic import MosaicController
14 | from ctr_output import OutputController
15 | from prompt_travel import PromptTravel
16 |
17 |
18 | class Controller:
19 | def __init__(self, form, model):
20 | self.form = form
21 | self.model = model
22 |
23 | self.file = FileController(form, model)
24 |
25 | self.menu = MenuController(self, form, model)
26 | self.input = InputController(form, model)
27 | self.preview = PreviewController(form, model)
28 | self.basic = BasicController(form, model)
29 | self.generate = GenerateController(form, model)
30 | self.controlNet = ControlNetController(form, model)
31 | self.ipAdapter = IpAdapterController(form, model)
32 | self.upscale = UpscaleController(form, model)
33 | self.mosaic = MosaicController(form, model)
34 | self.output = OutputController(form, model)
35 |
36 | self.menu.initEvents()
37 | self.input.initEvents()
38 | self.preview.initEvents()
39 | self.controlNet.initEvents()
40 | self.mosaic.initEvents()
41 |
42 | self.model.prompt.loadDefaultPrompt()
43 | lastFrame = self.model.prompt.getLastPrompt()[0] + 10
44 | if lastFrame > self.model.generate.length:
45 | self.model.generate.length = lastFrame
46 | self.model.notifyAll()
47 | self.file.resetChanged()
48 |
49 | PromptTravel.loadTemplate()
50 | self.form.win.after(50, lambda: self.saveConfig() or self.file.resetChanged())
51 | self.form.win.protocol("WM_DELETE_WINDOW", self.onWinClose)
52 |
53 | def onWinClose(self):
54 | if not self.file.askSave():
55 | return
56 | self.saveConfig()
57 | self.form.win.destroy()
58 |
59 | def updateConfig(self):
60 | self.model.updateConfig()
61 |
62 | def saveConfig(self):
63 | L10n.storeConfig()
64 | self.model.storeConfig()
65 | self.form.storeConfig()
66 | Config.save(Path.ini)
67 |
--------------------------------------------------------------------------------
/editor/src/ctr_basic.py:
--------------------------------------------------------------------------------
1 | from const import Const
2 | from log import Log
3 | import tkinter as tk
4 |
5 |
6 | class BasicController:
7 | def __init__(self, form, model):
8 | self.form = form
9 | self.model = model
10 |
11 | mGenerate = self.model.generate
12 | vBasic = self.form.basic
13 | mGenerate.bind("length", vBasic.varLength)
14 | mGenerate.bind("model", vBasic.varModel)
15 | mGenerate.bind("vae", vBasic.varVae)
16 | mGenerate.bind("seed", vBasic.varSeed)
17 | mGenerate.bind("width", vBasic.varWidth)
18 | mGenerate.bind("height", vBasic.varHeight)
19 |
20 | mGenerate.bind("upscale1Enabled", vBasic.varUpscale1Enabled)
21 | mGenerate.bind("upscale1Mode", vBasic.varUpscale1Mode)
22 | mGenerate.bind("upscale1Scale", vBasic.varUpscale1Scale)
23 |
24 | mGenerate.bind("upscale2Enabled", vBasic.varUpscale2Enabled)
25 | mGenerate.bind("upscale2Mode", vBasic.varUpscale2Mode)
26 | mGenerate.bind("upscale2Scale", vBasic.varUpscale2Scale)
27 |
--------------------------------------------------------------------------------
/editor/src/ctr_control_net.py:
--------------------------------------------------------------------------------
1 | import os, subprocess, shutil
2 | import tkinter as tk
3 | from tkinter import filedialog
4 | from const import Path, ControlNetType
5 | from log import Log
6 | from l10n import L10n
7 |
8 |
9 | class ControlNetController:
10 | def __init__(self, form, model):
11 | self.form = form
12 | self.model = model
13 | vControlNet = self.form.controlNet
14 |
15 | mGenerate = self.model.generate
16 | mGenerate.bind("controlNetDir", vControlNet.varControlNetDir)
17 | mGenerate.bind("controlNetLoop", vControlNet.varControlNetLoop)
18 |
19 | mEditor = self.model.editor
20 | mEditor.bind("importSpeed", vControlNet.varImportSpeed)
21 | mEditor.bind("importStart", vControlNet.varImportStart)
22 | mEditor.bind("importLength", vControlNet.varImportLength)
23 | mEditor.bind("importIndex", vControlNet.varImportIndex)
24 |
25 | for cnType in ControlNetType:
26 | mGenerate.bind(
27 | f"cnEnable_{cnType.name}",
28 | getattr(vControlNet, f"varEnable_{cnType.name}"),
29 | )
30 | mGenerate.bind(
31 | f"cnUsePreprocessor_{cnType.name}",
32 | getattr(vControlNet, f"varUsePreprocessor_{cnType.name}"),
33 | )
34 | mGenerate.bind(
35 | f"cnGuessMode_{cnType.name}",
36 | getattr(vControlNet, f"varGuessMode_{cnType.name}"),
37 | )
38 | mGenerate.bind(
39 | f"cnScale_{cnType.name}",
40 | getattr(vControlNet, f"varScale_{cnType.name}"),
41 | )
42 | mGenerate.bind(
43 | f"cnScaleList_{cnType.name}",
44 | getattr(vControlNet, f"varScaleList_{cnType.name}"),
45 | )
46 | mGenerate.bind(
47 | f"cnStart_{cnType.name}",
48 | getattr(vControlNet, f"varStart_{cnType.name}"),
49 | )
50 | mGenerate.bind(
51 | f"cnEnd_{cnType.name}",
52 | getattr(vControlNet, f"varEnd_{cnType.name}"),
53 | )
54 |
55 | def initEvents(self):
56 | mGenerate = self.model.generate
57 | mGenerate.subsc("controlNetDir", self.selectControlNetDir)
58 | for cnType in ControlNetType:
59 | mGenerate.subsc(
60 | f"cnEnable_{cnType.name}",
61 | lambda *args, name=cnType.name: self.updateEnable(name, args[2]),
62 | )
63 |
64 | vControlNet = self.form.controlNet
65 | vControlNet.btnImport.configure(command=self.importMovie)
66 |
67 | def selectControlNetDir(self, *args):
68 | basePath = os.path.join(Path.controlNet, args[2])
69 | for cnType in ControlNetType:
70 | os.makedirs(
71 | os.path.join(basePath, f"controlnet_{cnType.name}"), exist_ok=True
72 | )
73 |
74 | def updateEnable(self, name, isEnabled):
75 | vControlNet = self.form.controlNet
76 | frm = getattr(vControlNet, f"frm_{name}")
77 | vControlNet.ntb.tab(frm, text="* " + name if isEnabled else name)
78 |
79 | def importMovie(self):
80 | srcPath = filedialog.askopenfilename(
81 | title=L10n.get("dlg_control_net_import"), filetypes=[("MP4", "*.mp4")]
82 | )
83 | mEditor = self.model.editor
84 | speed = mEditor.importSpeed
85 | start = mEditor.importStart
86 | length = mEditor.importLength
87 | index = mEditor.importIndex
88 | basePath = os.path.join(Path.controlNet, self.model.generate.controlNetDir)
89 |
90 | cmd = ["ffmpeg", "-i", srcPath]
91 | vf = "fps=10,scale='if(gt(a,1),-1,512)':'if(gt(a,1),512,-1)'"
92 | if speed != "":
93 | vf = f"setpts=PTS/{speed},{vf}"
94 | cmd += ["-vf", vf]
95 | if start != "":
96 | cmd += ["-ss", start]
97 | if length != "":
98 | cmd += ["-t", length]
99 | if index != "":
100 | cmd += ["-start_number", index]
101 | outDir = os.path.join(basePath, f"controlnet_canny") + os.path.sep
102 | cmd += [f"{outDir}%08d.png"]
103 |
104 | Log.system(cmd)
105 | result = subprocess.run(cmd, shell=True)
106 | if result.returncode != 0:
107 | return
108 | for cnType in ControlNetType:
109 | if cnType == ControlNetType.canny:
110 | continue
111 | shutil.copytree(
112 | os.path.join(basePath, f"controlnet_canny"),
113 | os.path.join(basePath, f"controlnet_{cnType.name}"),
114 | dirs_exist_ok=True,
115 | )
116 |
--------------------------------------------------------------------------------
/editor/src/ctr_file.py:
--------------------------------------------------------------------------------
1 | import os
2 | from tkinter import filedialog, messagebox
3 | import json
4 | from const import Path
5 | from l10n import L10n
6 | from log import Log
7 | from serializer import Serializer
8 |
9 |
10 | class FileController:
11 | def __init__(self, form, model):
12 | self.form = form
13 | self.model = model
14 |
15 | self.form.win.bind("", lambda e: self.new())
16 | self.form.win.bind("", lambda e: self.open())
17 | self.form.win.bind("", lambda e: self.save())
18 |
19 | # TODO: new
20 | def new(self):
21 | if not self.askSave():
22 | return False
23 | self.model.reset()
24 | self.model.notifyAll()
25 | self.form.win.after(0, self.resetChanged)
26 | self.form.win.title(L10n.get("title"))
27 |
28 | Log.user(L10n.get("log_file_new"))
29 | return True
30 |
31 | def open(self):
32 | if not self.askSave():
33 | return False
34 | os.path.exists(Path.save) or os.makedirs(Path.save)
35 | initDir = Path.save
36 | if self.model.editor.savePath != "":
37 | initDir = os.path.dirname(self.model.editor.savePath)
38 | openPath = filedialog.askopenfilename(
39 | filetypes=[(L10n.get("title"), "*.json")], # TODO: promptTravelConfig
40 | initialdir=initDir,
41 | )
42 | if openPath == "":
43 | return False
44 | data = None
45 | with open(openPath, "r", encoding="utf-8-sig") as f:
46 | data = json.load(f)
47 | if Serializer.versionKey in data:
48 | if Serializer.deserialize(self.model, data):
49 | self.model.editor.set("savePath", openPath)
50 | self.form.win.title(L10n.get("title") + openPath)
51 | self.resetChanged()
52 | Log.user(L10n.format("log_file_open", openPath))
53 | return True
54 | else:
55 | Log.user(L10n.format("log_file_open_failed", openPath))
56 | return False
57 | else:
58 | return False # TODO: import prompt travel config
59 |
60 | def save(self):
61 | if self.model.editor.savePath == "":
62 | return self.saveAs()
63 | data = Serializer.serialize(self.model)
64 | with open(self.model.editor.savePath, "w", encoding="utf-8-sig") as f:
65 | json.dump(data, f, indent=4)
66 | self.resetChanged()
67 | Log.user(L10n.format("log_file_save", self.model.editor.savePath))
68 | return True
69 |
70 | def saveAs(self):
71 | os.path.exists(Path.save) or os.makedirs(Path.save)
72 | initDir = Path.save
73 | if self.model.editor.savePath != "":
74 | initDir = os.path.dirname(self.model.editor.savePath)
75 | saveAsPath = filedialog.asksaveasfilename(
76 | filetypes=[(L10n.get("title"), "*.json")], initialdir=initDir
77 | )
78 | if saveAsPath == "":
79 | return False
80 | if not saveAsPath.endswith(".json"):
81 | saveAsPath += ".json"
82 | data = Serializer.serialize(self.model)
83 | with open(saveAsPath, "w", encoding="utf-8-sig") as f:
84 | json.dump(data, f, indent=4)
85 | self.model.editor.set("savePath", saveAsPath)
86 | self.form.win.title(L10n.get("title") + saveAsPath)
87 | self.resetChanged()
88 | Log.user(L10n.format("log_file_save_as", saveAsPath))
89 | return True
90 |
91 | def askSave(self):
92 | if not self.isChanged():
93 | return True
94 | result = messagebox.askyesnocancel(L10n.get("title"), L10n.get("dlg_ask_save"))
95 | if result is None:
96 | return False
97 | if result:
98 | return self.save()
99 | return True
100 |
101 | def isChanged(self):
102 | return (
103 | self.model.prompt.isChanged
104 | or self.model.generate.isChanged
105 | or self.model.editor.isChanged
106 | )
107 |
108 | def resetChanged(self):
109 | self.model.prompt.isChanged = False
110 | self.model.generate.isChanged = False
111 | self.model.editor.isChanged = False
112 |
--------------------------------------------------------------------------------
/editor/src/ctr_generate.py:
--------------------------------------------------------------------------------
1 | from log import Log
2 | import tkinter as tk
3 |
4 |
5 | class GenerateController:
6 | def __init__(self, form, model):
7 | self.form = form
8 | self.model = model
9 |
10 | mGenerate = self.model.generate
11 | vGenerate = self.form.generate
12 |
13 | mGenerate.bind("motionModule", vGenerate.varMotionModule)
14 | mGenerate.bind("context", vGenerate.varContext)
15 | mGenerate.bind("scheduler", vGenerate.varScheduler)
16 | mGenerate.bind("steps", vGenerate.varSteps)
17 | mGenerate.bind("guidanceScale", vGenerate.varGuidanceScale)
18 | mGenerate.bind("clipSkip", vGenerate.varClipSkip)
19 | mGenerate.bind("promptFixedRatio", vGenerate.varPromptFixedRatio)
20 | mGenerate.bind("useLcm", vGenerate.varUseLcm)
21 | mGenerate.bind("useHighresFix", vGenerate.varUseHighresFix)
22 | mGenerate.bind("useHalfVae", vGenerate.varUseHalfVae)
23 | mGenerate.bind("useXFormers", vGenerate.varUseXFormers)
24 |
--------------------------------------------------------------------------------
/editor/src/ctr_input.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 | from l10n import L10n
3 | from task import Task
4 | from tsk_generate import GenerateTask
5 |
6 |
7 | class InputController:
8 | def __init__(self, form, model):
9 | self.form = form
10 | self.model = model
11 |
12 | vInput = self.form.input
13 | self.model.prompt.subsc("text", self.setTxtInput)
14 | vInput.txtInput.bind("<>", self.modifiedTxtInput)
15 | self.bindKeyboardShortcuts()
16 |
17 | mEdit = self.model.editor
18 | mEdit.bind("previewUpscale", vInput.varUpscale)
19 | # mEdit.bind("previewInterpolation", vInput.varInterpolation)
20 | mEdit.bind("previewStart", vInput.varStart)
21 | mEdit.bind("previewLength", vInput.varLength)
22 |
23 | mEdit.bind("taskForever", vInput.varForever)
24 | mEdit.bind("taskPauseByError", vInput.varPauseByError)
25 |
26 | vInput.btnGenerate.configure(command=self.onGenerate)
27 | vInput.btnSeedGacha.configure(command=self.onSeedGacha)
28 | vInput.btnPreview.configure(command=self.onPreview)
29 |
30 | def setTxtInput(self, *args):
31 | vTxtInput = self.form.input.txtInput
32 | mOriginal = self.model.prompt.text
33 | if mOriginal == vTxtInput.get("1.0", tk.END):
34 | return
35 | vTxtInput.delete("1.0", tk.END)
36 | vTxtInput.insert(tk.END, mOriginal)
37 |
38 | def modifiedTxtInput(self, *args):
39 | vTxtInput = self.form.input.txtInput
40 | self.model.prompt.set("text", vTxtInput.get("1.0", tk.END))
41 | vTxtInput.edit_modified(False)
42 |
43 | def bindKeyboardShortcuts(self):
44 | pass # TODO: txtInput shortcuts
45 |
46 | def onGenerate(self):
47 | GenerateTask.generate(self.model)
48 |
49 | def onSeedGacha(self):
50 | GenerateTask.seedGacha(self.model)
51 |
52 | def onPreview(self):
53 | GenerateTask.preview(self.model)
54 |
55 | def initEvents(self):
56 | self.model.editor.subsc("previewStart", self.onPreviewStartChanged)
57 | self.model.editor.subsc("previewLength", self.onPreviewLengthChanged)
58 | self.model.generate.subsc("length", self.onLengthChanged)
59 |
60 | def onPreviewStartChanged(self, *args):
61 | lengthTo = self.model.generate.length - self.model.editor.previewStart
62 | self.form.input.sliLength.configure(to=lengthTo)
63 | self.updatePreviewLengthLbl()
64 |
65 | def onPreviewLengthChanged(self, *args):
66 | startTo = self.model.generate.length - self.model.editor.previewLength
67 | self.form.input.sliStart.configure(to=startTo)
68 | self.updatePreviewLengthLbl()
69 |
70 | def onLengthChanged(self, *args):
71 | startTo = self.model.generate.length - self.model.editor.previewLength
72 | self.form.input.sliStart.configure(to=startTo)
73 | lengthTo = self.model.generate.length - self.model.editor.previewStart
74 | self.form.input.sliLength.configure(to=lengthTo)
75 | self.updatePreviewLengthLbl()
76 |
77 | def updatePreviewLengthLbl(self):
78 | start = int(self.model.editor.previewStart / 10)
79 | end = int(self.model.getPreviewEnd() / 10)
80 | lbl = f"{L10n.get('preview_length')}{start}-{end}{L10n.get('sec')}"
81 | self.form.input.lblLength.configure(text=lbl)
82 |
--------------------------------------------------------------------------------
/editor/src/ctr_ip_adapter.py:
--------------------------------------------------------------------------------
1 | from log import Log
2 | import tkinter as tk
3 |
4 |
5 | class IpAdapterController:
6 | def __init__(self, form, model):
7 | self.form = form
8 | self.model = model
9 | mGenerate = self.model.generate
10 | vIpAdapter = self.form.ipAdapter
11 |
12 | mGenerate.bind("useIpAdapter", vIpAdapter.varUse)
13 | mGenerate.bind("useIpAdapterPlus", vIpAdapter.varUsePlus)
14 | mGenerate.bind("useIpAdapterPlusFace", vIpAdapter.varUsePlusFace)
15 | mGenerate.bind("ipAdapterScale", vIpAdapter.varScale)
16 | mGenerate.bind("ipAdapterImageDir", vIpAdapter.varImageDir)
17 |
--------------------------------------------------------------------------------
/editor/src/ctr_menu.py:
--------------------------------------------------------------------------------
1 | import os, subprocess
2 | import tkinter as tk
3 | from tkinter import filedialog
4 | from const import Path
5 | from l10n import L10n
6 | from log import Log
7 | from tsk_generate import GenerateTask
8 | from tsk_upscale import UpscaleTask
9 | from ui import Form
10 |
11 |
12 | class MenuController:
13 | def __init__(self, parent, form, model):
14 | self.parent = parent
15 | self.form = form
16 | self.model = model
17 |
18 | def initEvents(self):
19 | self.initFileEvents()
20 | self.initAnimeEvents()
21 | self.initFolderEvents()
22 | self.initToolEvents()
23 | self.initDownloadEvents()
24 | self.initSettingEvents()
25 | self.initHelpEvents()
26 |
27 | def initFileEvents(self):
28 | sfl = self.form.menu.fileMenu
29 | sfl.entryconfig(L10n.get("m_file_new"), command=self.parent.file.new)
30 | sfl.entryconfig(L10n.get("m_file_open"), command=self.parent.file.open)
31 | sfl.entryconfig(L10n.get("m_file_save"), command=self.parent.file.save)
32 | sfl.entryconfig(L10n.get("m_file_save_as"), command=self.parent.file.saveAs)
33 | sfl.entryconfig(L10n.get("m_file_exit"), command=self.parent.onWinClose)
34 |
35 | def initAnimeEvents(self):
36 | anime = self.form.menu.animeMenu
37 | anime.entryconfig(
38 | L10n.get("m_anime_preview"),
39 | command=lambda: GenerateTask.preview(self.model),
40 | )
41 | anime.entryconfig(
42 | L10n.get("m_anime_seed_gacha"),
43 | command=lambda: GenerateTask.seedGacha(self.model),
44 | )
45 | anime.entryconfig(
46 | L10n.get("m_anime_generate"),
47 | command=lambda: GenerateTask.generate(self.model),
48 | )
49 | anime.entryconfig(
50 | L10n.get("m_anime_upscale"), command=lambda: self.upscale(False)
51 | )
52 | anime.entryconfig(
53 | L10n.get("m_anime_upscale_config"), command=lambda: self.upscale(True)
54 | )
55 |
56 | def upscale(self, configOverride):
57 | Log.user(
58 | L10n.get("log_upscale_config_override" if configOverride else "log_upscale")
59 | )
60 | upscaleDir = filedialog.askdirectory(
61 | title=L10n.get("dlg_upscale_title"), initialdir=Path.promptTravelOutput
62 | )
63 |
64 | if upscaleDir == "":
65 | return
66 | if configOverride:
67 | UpscaleTask.upscaleWithConfigOverride(self.model, upscaleDir)
68 | else:
69 | UpscaleTask.upscale(self.model, upscaleDir)
70 |
71 | def initFolderEvents(self):
72 | fld = self.form.menu.folderMenu
73 | self.openFolder(fld, "m_output_folder", Path.output)
74 |
75 | ptf = self.form.menu.promptTravelFolderMenu
76 | self.openFolder(ptf, "m_sd_model_folder", Path.model)
77 | self.openFolder(ptf, "m_motion_module_folder", Path.motionModule)
78 | self.openFolder(ptf, "m_lora_folder", Path.lora)
79 | self.openFolder(ptf, "m_embeddings_folder", Path.embeddings)
80 | self.openFolder(ptf, "m_wildcard_folder", Path.wildcard)
81 |
82 | ptof = self.form.menu.promptTravelOutputFolderMenu
83 | self.openFolder(
84 | ptof, "m_prompt_travel_generate_folder", Path.promptTravelOutput
85 | )
86 | self.openFolder(ptof, "m_prompt_travel_tile_folder", Path.promptTravelUpscaled)
87 | self.openFolder(ptof, "m_prompt_travel_refine_folder", Path.promptTravelRefined)
88 |
89 | self.openFolder(fld, "m_temp_folder", Path.temp)
90 | self.openFolder(fld, "m_log_folder", Path.log)
91 |
92 | def initToolEvents(self):
93 | tool = self.form.menu.toolMenu
94 | tool.entryconfig(
95 | L10n.get("m_tool_convert_lora"), command=lambda: self.convertLora()
96 | )
97 |
98 | self.openUrl(tool, "m_tool_easy_leco", L10n.get("m_tool_easy_leco_url"))
99 |
100 | def convertLora(self):
101 | srcPath = filedialog.askopenfilename(
102 | title=L10n.get("dlg_convert_lora_title"),
103 | filetypes=[("C3Lier LoRA or LoCon LoRA", "*.safetensors")],
104 | initialdir=Path.lora,
105 | )
106 | if srcPath is None or srcPath == "":
107 | return
108 | srcPath = os.path.abspath(srcPath)
109 | modelPath = os.path.abspath(os.path.join(Path.model, self.model.generate.model))
110 | dim = 32
111 | dstPath = os.path.abspath(os.path.join(Path.lora, os.path.basename(srcPath)))
112 | Log.user(L10n.format("log_convert_lora", dim, srcPath, modelPath, dstPath))
113 | subprocess.run(
114 | ["start", "cmd", "/c", "ConvertLora.bat", srcPath, f"{dim}", modelPath],
115 | shell=True,
116 | )
117 |
118 | def initDownloadEvents(self):
119 | self.categoryDirs = {
120 | "model": Path.model,
121 | "lora": Path.lora,
122 | "motion_module": Path.motionModule,
123 | "motion_lora": Path.motionLora,
124 | "vae": Path.vae,
125 | "embedding": Path.embeddings,
126 | }
127 | dlMenu = self.form.menu.downloadMenu
128 | dlMenu.configure(postcommand=self.updateDownloadMenu)
129 |
130 | for category, categoryData in self.form.menu.downloadMenuData.items():
131 | for name, data in categoryData.items():
132 | label = f'({data["tag"]}) {name}' if "tag" in data else name
133 | data["menu"].entryconfig(
134 | label,
135 | command=lambda c=category, n=name, d=data: self.download(c, n, d),
136 | )
137 |
138 | def updateDownloadMenu(self):
139 | for category, categoryData in self.form.menu.downloadMenuData.items():
140 | for name, data in categoryData.items():
141 | dstPath = os.path.join(self.categoryDirs[category], name)
142 | data["checked"].set(os.path.exists(dstPath))
143 |
144 | def download(self, category, name, data):
145 | dstPath = os.path.join(self.categoryDirs[category], name)
146 | unzipDir = ""
147 | if "isZip" in data and data["isZip"]:
148 | unzipDir = os.path.dirname(dstPath)
149 | dstPath = os.path.join(os.path.dirname(dstPath), "temp.zip")
150 |
151 | Log.user(L10n.format("log_start_download", name, data["info"]))
152 | cmd = f'echo {data["info"]} & curl -Lo {dstPath} {data["url"]} || pause'
153 | if unzipDir != "":
154 | cmd += f" & PowerShell -Version 5.1 -ExecutionPolicy Bypass Expand-Archive -Path {dstPath} -DestinationPath {unzipDir} -Force || pause & del {dstPath}"
155 | subprocess.run(["start", "cmd", "/c", cmd], shell=True)
156 |
157 | def initSettingEvents(self):
158 | stm = self.form.menu.settingMenu
159 |
160 | stm.entryconfig(
161 | L10n.get("m_set_default_prompt"),
162 | command=lambda: self.model.prompt.saveDefaultPrompt(),
163 | )
164 | stm.entryconfig(
165 | L10n.get("m_set_default_setting"),
166 | command=lambda: self.parent.updateConfig() or self.parent.saveConfig(),
167 | )
168 | stm.entryconfig(
169 | L10n.get("m_set_change_light_dark"),
170 | command=lambda: setattr(Form, "invOsCol", not Form.invOsCol),
171 | )
172 | self.openFile(stm, "m_set_open_ini_file", Path.ini)
173 | self.openFile(stm, "m_set_open_default_prompt_file", Path.defaultPrompt)
174 | self.openFile(
175 | stm, "m_set_open_prompt_travel_template_file", Path.promptTravelTemplate
176 | )
177 |
178 | def initHelpEvents(self):
179 | hlp = self.form.menu.helpMenu
180 | hlp.entryconfig(
181 | L10n.get("m_prompt_help"),
182 | command=lambda: self.form.input.txtInput.insert(
183 | tk.END, L10n.get("hlp_prompt")
184 | ),
185 | )
186 |
187 | self.openUrl(hlp, "m_github", "https://github.com/Zuntan03/EasyPromptAnime")
188 |
189 | ref = self.form.menu.referenceMenu
190 | self.openUrlNoL10n(
191 | ref,
192 | "AnimateDiff prompt travel",
193 | "https://github.com/s9roll7/animatediff-cli-prompt-travel",
194 | )
195 | self.openUrlNoL10n(ref, "sd-scripts", "https://github.com/kohya-ss/sd-scripts")
196 | self.openUrlNoL10n(ref, "Codex FFmpeg", "https://github.com/GyanD/codexFFmpeg")
197 | self.openUrlNoL10n(
198 | ref, "Practical-RIFE", "https://github.com/hzwer/Practical-RIFE"
199 | )
200 | self.openUrlNoL10n(ref, "NudeNet", "https://github.com/notAI-tech/NudeNet")
201 |
202 | def openFile(self, menu, name, path):
203 | menu.entryconfig(
204 | L10n.get(name),
205 | command=lambda: subprocess.run(["start", path], shell=True),
206 | )
207 |
208 | def openFolder(self, menu, name, path):
209 | menu.entryconfig(
210 | L10n.get(name),
211 | command=lambda: subprocess.run(["explorer", path]),
212 | )
213 |
214 | def openUrl(self, menu, name, url):
215 | menu.entryconfig(
216 | L10n.get(name), command=lambda: subprocess.run(["start", url], shell=True)
217 | )
218 |
219 | def openUrlNoL10n(self, menu, name, url):
220 | menu.entryconfig(
221 | name, command=lambda: subprocess.run(["start", url], shell=True)
222 | )
223 |
--------------------------------------------------------------------------------
/editor/src/ctr_mosaic.py:
--------------------------------------------------------------------------------
1 | import os, shutil, subprocess
2 | import tkinter as tk
3 | from tkinter import filedialog
4 | from const import Path
5 | from log import Log
6 | from l10n import L10n
7 | from mosaic import Mosaic
8 |
9 |
10 | class MosaicController:
11 | def __init__(self, form, model):
12 | self.form = form
13 | self.model = model
14 | mGenerate = self.model.generate
15 | vMosaic = self.form.mosaic
16 |
17 | mGenerate.bind("mosaicEnabled", vMosaic.varMosaicEnabled)
18 | mGenerate.bind("mosaicThreshold", vMosaic.varMosaicThreshold)
19 | mGenerate.bind("temporalMosaic", vMosaic.varTemporalMosaic)
20 | mGenerate.bind("ellipseMosaic", vMosaic.varEllipseMosaic)
21 | mGenerate.bind("mosaicMaskBlur", vMosaic.varMosaicMaskBlur)
22 | mGenerate.bind("mosaicFemFace", vMosaic.varFemFace)
23 | mGenerate.bind("mosaicFemBrst", vMosaic.varFemBrst)
24 | mGenerate.bind("mosaicFemBrstCov", vMosaic.varFemBrstCov)
25 | mGenerate.bind("mosaicFemGntl", vMosaic.varFemGntl)
26 | mGenerate.bind("mosaicFemGntlCov", vMosaic.varFemGntlCov)
27 | mGenerate.bind("mosaicMaleFace", vMosaic.varMaleFace)
28 | mGenerate.bind("mosaicMaleBrst", vMosaic.varMaleBrst)
29 | mGenerate.bind("mosaicMaleGntl", vMosaic.varMaleGntl)
30 | mGenerate.bind("mosaicArmpit", vMosaic.varArmpit)
31 | mGenerate.bind("mosaicArmpitCov", vMosaic.varArmpitCov)
32 | mGenerate.bind("mosaicBelly", vMosaic.varBelly)
33 | mGenerate.bind("mosaicBellyCov", vMosaic.varBellyCov)
34 | mGenerate.bind("mosaicHip", vMosaic.varHip)
35 | mGenerate.bind("mosaicHipCov", vMosaic.varHipCov)
36 | mGenerate.bind("mosaicAns", vMosaic.varAns)
37 | mGenerate.bind("mosaicAnsCov", vMosaic.varAnsCov)
38 | mGenerate.bind("mosaicFeet", vMosaic.varFeet)
39 | mGenerate.bind("mosaicFeetCov", vMosaic.varFeetCov)
40 | mGenerate.bind("mosaicScaleTop", vMosaic.varMosaicScaleTop)
41 | mGenerate.bind("mosaicScaleBottom", vMosaic.varMosaicScaleBottom)
42 | mGenerate.bind("mosaicScaleLeft", vMosaic.varMosaicScaleLeft)
43 | mGenerate.bind("mosaicScaleRight", vMosaic.varMosaicScaleRight)
44 | mGenerate.bind("mosaicIgnoreTop", vMosaic.varMosaicIgnoreTop)
45 | mGenerate.bind("mosaicIgnoreBottom", vMosaic.varMosaicIgnoreBottom)
46 | mGenerate.bind("mosaicIgnoreLeft", vMosaic.varMosaicIgnoreLeft)
47 | mGenerate.bind("mosaicIgnoreRight", vMosaic.varMosaicIgnoreRight)
48 |
49 | def initEvents(self):
50 | self.form.mosaic.btnMosaicPngDir.configure(command=self.onMosaicPngDir)
51 |
52 | def onMosaicPngDir(self):
53 | Log.user(L10n.get("log_please_wait"))
54 | srcDir = filedialog.askdirectory(
55 | title=L10n.get("dlg_mosaic_title"), initialdir=Path.promptTravelUpscaled
56 | )
57 | if srcDir == "":
58 | return
59 | mosaicDir = Mosaic.mosaicPngDir(self.model.generate, srcDir)
60 | if mosaicDir == "":
61 | Log.system(f"[FAILED] Mosaic.mosaicPngDir()")
62 | return
63 |
64 | frames2Mp4Result = subprocess.run(["Frames2Mp4.bat", mosaicDir])
65 | if frames2Mp4Result.returncode != 0:
66 | Log.system(f"[FAILED] Frames2Mp4.bat: {frames2Mp4Result.returncode}")
67 | return
68 |
69 | fpsX6Result = subprocess.run(["FpsX6.bat", mosaicDir + ".mp4"])
70 | if fpsX6Result.returncode != 0:
71 | Log.system(f"[FAILED] FpsX6.bat: {fpsX6Result.returncode}")
72 | return
73 |
74 | outputDir = os.path.join(Path.output, Path.getYYYYMMDD())
75 | os.makedirs(outputDir, exist_ok=True)
76 | dstName = f"{Path.getYYYYMMDDHHMMSS()}{os.path.basename(mosaicDir)[2:]}.mp4"
77 | outputPath = os.path.join(outputDir, dstName)
78 | shutil.copy(mosaicDir + "-M6e.mp4", outputPath)
79 |
80 | subprocess.Popen(f"{self.model.editor.ffplayCmd} {outputPath}", shell=True)
81 |
--------------------------------------------------------------------------------
/editor/src/ctr_output.py:
--------------------------------------------------------------------------------
1 | from log import Log
2 | import tkinter as tk
3 |
4 |
5 | class OutputController:
6 | def __init__(self, form, model):
7 | self.form = form
8 | self.model = model
9 | Log.systemLogEvent.append(self.onSystemLog)
10 | Log.userLogEvent.append(self.onUserLog)
11 |
12 | def onSystemLog(self, msg):
13 | op = self.form.output
14 | op.txtSys.configure(state=tk.NORMAL)
15 | op.txtSys.insert(tk.END, msg)
16 | op.txtSys.see(tk.END)
17 | op.txtSys.insert(tk.END, "\n")
18 | op.txtSys.configure(state=tk.DISABLED)
19 |
20 | def onUserLog(self, msg):
21 | op = self.form.output
22 | op.txtUser.configure(state=tk.NORMAL)
23 | op.txtUser.insert(tk.END, msg)
24 | op.txtUser.see(tk.END)
25 | op.txtUser.insert(tk.END, "\n")
26 | op.txtUser.configure(state=tk.DISABLED)
27 |
--------------------------------------------------------------------------------
/editor/src/ctr_preview.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 |
3 |
4 | class PreviewController:
5 | def __init__(self, form, model):
6 | self.form = form
7 | self.model = model
8 |
9 | mEdit = self.model.editor
10 | vPrev = self.form.preview
11 | mEdit.bind("previewShowKeyframe", vPrev.varShowKeyframe)
12 | mEdit.bind("previewShowHeaderFooter", vPrev.varShowHeaderFooter)
13 | mEdit.bind("previewShowAnime", vPrev.varShowAnime)
14 |
15 | def initEvents(self):
16 | self.initPreview()
17 |
18 | def initPreview(self):
19 | self.model.prompt.subsc("text", self.updatePerview)
20 | self.model.generate.subsc("length", self.updatePerview)
21 |
22 | mEdit = self.model.editor
23 | mEdit.subsc("previewShowKeyframe", self.updatePerview)
24 | mEdit.subsc("previewShowHeaderFooter", self.updatePerview)
25 | mEdit.subsc("previewShowAnime", self.updatePerview)
26 | mEdit.subsc("previewStart", self.updatePerview)
27 | mEdit.subsc("previewLength", self.updatePerview)
28 |
29 | def updatePerview(self, *args):
30 | mEdit = self.model.editor
31 | prevTxt = self.model.prompt.getPreview(
32 | mEdit.previewStart,
33 | self.model.getPreviewLength(),
34 | mEdit.previewShowKeyframe,
35 | mEdit.previewShowHeaderFooter,
36 | mEdit.previewShowAnime,
37 | )
38 |
39 | vTxtPrev = self.form.preview.txtPrev
40 | vTxtPrev.configure(state=tk.NORMAL)
41 | vTxtPrev.delete("1.0", tk.END)
42 | vTxtPrev.insert(tk.END, prevTxt)
43 | vTxtPrev.configure(state=tk.DISABLED)
44 |
--------------------------------------------------------------------------------
/editor/src/ctr_upscale.py:
--------------------------------------------------------------------------------
1 | from log import Log
2 | import tkinter as tk
3 |
4 |
5 | class UpscaleController:
6 | def __init__(self, form, model):
7 | self.form = form
8 | self.model = model
9 | mGenerate = self.model.generate
10 | vUpscale = self.form.upscale
11 |
12 | mGenerate.bind("upscaleScheduler", vUpscale.varScheduler)
13 | mGenerate.bind("upscaleSteps", vUpscale.varSteps)
14 | mGenerate.bind("upscaleGuidanceScale", vUpscale.varGuidanceScale)
15 | mGenerate.bind("upscaleStrength", vUpscale.varDenoise)
16 | mGenerate.bind("upscaleTileEnable", vUpscale.varTileEnable)
17 | mGenerate.bind("upscaleTileScale", vUpscale.varTileScale)
18 | mGenerate.bind("upscaleTileStart", vUpscale.varTileStart)
19 | mGenerate.bind("upscaleTileEnd", vUpscale.varTileEnd)
20 | mGenerate.bind("upscaleIp2pEnable", vUpscale.varIp2pEnable)
21 | mGenerate.bind("upscaleIp2pScale", vUpscale.varIp2pScale)
22 | mGenerate.bind("upscaleIp2pStart", vUpscale.varIp2pStart)
23 | mGenerate.bind("upscaleIp2pEnd", vUpscale.varIp2pEnd)
24 | mGenerate.bind("upscaleLineAnimeEnable", vUpscale.varLineAnimeEnable)
25 | mGenerate.bind("upscaleLineAnimeScale", vUpscale.varLineAnimeScale)
26 | mGenerate.bind("upscaleLineAnimeStart", vUpscale.varLineAnimeStart)
27 | mGenerate.bind("upscaleLineAnimeEnd", vUpscale.varLineAnimeEnd)
28 | mGenerate.bind("upscaleUseHalfVae", vUpscale.varUseHalfVae)
29 | mGenerate.bind("upscaleUseXFormers", vUpscale.varUseXFormers)
30 |
--------------------------------------------------------------------------------
/editor/src/l10n.py:
--------------------------------------------------------------------------------
1 | from config import Config
2 | from l10n_en import l10nEn
3 | from l10n_ja import l10nJa
4 | import locale
5 |
6 |
7 | class L10n:
8 | @classmethod
9 | def get(cls, key):
10 | return cls._l10n[key]
11 |
12 | @classmethod
13 | def format(cls, key, *args):
14 | return cls._l10n[key].format(*args)
15 |
16 | @classmethod
17 | def loadConfig(cls):
18 | lang = Config.get("ui", "lang", fallback=locale.getdefaultlocale()[0][:2])
19 | cls._l10n = l10nEn
20 | if lang == "ja":
21 | cls._l10n = l10nJa
22 |
23 | @classmethod
24 | def storeConfig(cls):
25 | Config.set("ui", "lang", cls.get("lang"))
26 |
--------------------------------------------------------------------------------
/editor/src/l10n_ja.py:
--------------------------------------------------------------------------------
1 | l10nJa = {
2 | "lang": "ja",
3 | "error": "エラー",
4 | "sec": "秒",
5 | "title": "簡単プロンプトアニメ ",
6 | "clear": "クリア",
7 | "top": "上",
8 | "bottom": "下",
9 | "left": "左",
10 | "right": "右",
11 | "negative_prompt": "ネガティブプロンプト",
12 | "swap": "入替",
13 | "generate_anime": "アニメ生成",
14 | "upscale": "アップスケール",
15 | "seed_gacha": "シードガチャ",
16 | "preview": "プレビュー",
17 | "preview_upscale": "アップスケール",
18 | # "preview_interpolation": "補間",
19 | "preview_start_frame": "プレビュー\n開始",
20 | "preview_length": "長さ \n",
21 | "task_forever": "連続実行",
22 | "task_pause_by_error": "エラーで一時停止",
23 | "preview_show_keyframe": "キーフレームを表示",
24 | "preview_show_header_footer": "ヘッダとフッタを表示",
25 | "preview_show_anime": "アニメを表示",
26 | "generate_length": "フレーム数\n{0:>8}秒",
27 | "generate_seed": "シード",
28 | "generate_model": "モデル",
29 | "generate_vae": "VAE",
30 | "generate_width": "幅",
31 | "generate_height": "高さ",
32 | "generate_motion_module": "モーション\nモジュール",
33 | "generate_context": "コンテキスト",
34 | "generate_scheduler": "スケジューラ",
35 | "generate_steps": "ステップ数",
36 | "generate_guidance_scale": "ガイダンススケール\n(CFG スケール)",
37 | "generate_clip_skip": "クリップ\nスキップ",
38 | "generate_prompt_fixed_ratio": "プロンプト\n固定割合",
39 | "generate_use_lcm": "LCM を使用",
40 | "generate_use_highres_fix": "HiresFix を使用",
41 | "generate_use_half_vae": "Half VAE を使用",
42 | "generate_use_x_formers": "xFormers を使用",
43 | "control_net_dir": "コントロールネット用のフォルダ",
44 | "control_net_loop": "ループを考慮して適用",
45 | "control_net_info": "コントロールネット用のフォルダのサブフォルダに、各コントロールネットで使う png 画像を置きます。\npng 画像のファイル名は 000.png (0フレーム) や 010.png (10フレーム、1秒) のように、コントロールネットを使用するフレーム数を指定します。\nコントロールネット用のフォルダを選択すれば、必要なサブフォルダは自動的に作成します。",
46 | "control_net_import": "MP4 画像取込",
47 | "control_net_import_speed": "再生速度",
48 | "control_net_import_start": "開始(秒)",
49 | "control_net_import_length": "長さ(秒)",
50 | "control_net_import_index": "開始番号",
51 | "control_net_enable": "{0} を使用",
52 | "control_net_open_dir": "{0} の画像フォルダを開く",
53 | "control_net_preprocessor": "前処理を使用",
54 | "control_net_guess_mode": "{0} を重視",
55 | "control_net_scale": "{0} スケール",
56 | "control_net_scale_list": "隣接フレームのスケールリスト",
57 | "control_net_start": "{0} 開始",
58 | "control_net_end": "{0} 終了",
59 | "ip_adapter_use": "IP(イメージプロンプト)アダプタを使用",
60 | "ip_adapter_use_plus": "IPアダプタプラスを使用",
61 | "ip_adapter_use_plus_face": "IPアダプタプラスフェイスを使用",
62 | "ip_adapter_scale": "イメージプロンプト比率\n(1.0でも要プロンプト指定)",
63 | "ip_adapter_image_dir": "IP用の画像フォルダ",
64 | "ip_adapter_image_dir_explorer": "エクスプローラで開く",
65 | "ip_adapter_info": "[エクスプローラで開く] の ip_adapter_image/ にフォルダを作成して、IPアダプタ用の png 画像を置きます。\npng 画像のファイル名は 000.png (0フレーム) や 010.png (10フレーム、1秒) のように、イメージプロンプトとして使用するフレーム数を指定します。",
66 | "upscale1": "初回アップスケール",
67 | "upscale2": "二重アップスケール",
68 | "upscale_scale": "倍率",
69 | "upscale_scheduler": "スケジューラ",
70 | "upscale_steps": "ステップ数",
71 | "upscale_strength": "ノイズ除去強度",
72 | "upscale_guidance_scale": "ガイダンススケール\n(CFG スケール)",
73 | "upscale_tile_enable": "tile 使用",
74 | "upscale_tile_scale": "強度",
75 | "upscale_tile_start": "開始",
76 | "upscale_tile_end": "終了",
77 | "upscale_ip2p_enable": "ip2p 使用",
78 | "upscale_ip2p_scale": "強度",
79 | "upscale_ip2p_start": "開始",
80 | "upscale_ip2p_end": "終了",
81 | "upscale_line_anime_enable": "line_anime 使用",
82 | "upscale_line_anime_scale": "強度",
83 | "upscale_line_anime_start": "開始",
84 | "upscale_line_anime_end": "終了",
85 | "upscale_use_half_vae": "Half VAE を使用(黒画面出力の抑制事例あり)",
86 | "upscale_use_x_formers": "xFormers を使用",
87 | "mosaic_covered_info": "■: 衣服越し部位。",
88 | "mosaic_png_dir": "画像フォルダを\nモザイク",
89 | "mosaic_enabled": "モザイク\n使用",
90 | "mosaic_threshold": "モザイク\nしきい値",
91 | "mosaic_temporal": "時間的\nモザイク",
92 | "mosaic_ellipse": "楕円形\nモザイク",
93 | "mosaic_mask_blur": "マスク\nぼかし",
94 | "mosaic_male": "男性",
95 | "mosaic_male_face": ":-)",
96 | "mosaic_male_brst": "[. ][ .]",
97 | "mosaic_male_gntl": "(U)",
98 | "mosaic_fem": "女性",
99 | "mosaic_fem_face": ";-)",
100 | "mosaic_fem_brst": "(o )( o)",
101 | "mosaic_fem_brst_cov": "(■)(■)",
102 | "mosaic_fem_gntl": "(|)",
103 | "mosaic_fem_gntl_cov": "(■)",
104 | "mosaic_armpit": "脇",
105 | "mosaic_armpit_cov": "■ 脇",
106 | "mosaic_belly": "腹",
107 | "mosaic_belly_cov": "■ 腹",
108 | "mosaic_hip": "尻",
109 | "mosaic_hip_cov": "■ 尻",
110 | "mosaic_ans": ")x(",
111 | "mosaic_ans_cov": ")■(",
112 | "mosaic_feet": "足",
113 | "mosaic_feet_cov": "■ 足",
114 | "mosaic_scale": "モザイク\nスケール",
115 | "mosaic_ignore": "モザイク\n無効範囲",
116 | "preview_tab": "プレビュー",
117 | "basic_tab": "基本",
118 | "generate_tab": "アニメ生成",
119 | "control_net_tab": "コントロールネット",
120 | "ip_adapter_tab": "IPアダプタ",
121 | "upscale_tab": "アップスケール",
122 | "mosaic_tab": "モザイク",
123 | "user_log_tab": "ユーザーログ",
124 | "system_log_tab": "システムログ",
125 | "m_file": "ファイル",
126 | "m_file_new": "新規作成 (Ctrl+N)",
127 | "m_file_open": "開く (Ctrl+O)",
128 | "m_file_save": "上書き保存 (Ctrl+S)",
129 | "m_file_save_as": "名前を付けて保存",
130 | "m_file_exit": "終了 (Alt+F4)",
131 | "m_anime": "アニメ",
132 | "m_anime_preview": "プレビュー",
133 | "m_anime_seed_gacha": "シードガチャ",
134 | "m_anime_generate": "アニメ生成",
135 | "m_anime_upscale": "生成時の設定で初回アップスケール",
136 | "m_anime_upscale_config": "現在の設定で初回アップスケール",
137 | "m_folder": "フォルダ",
138 | "m_output_folder": "アニメ出力先",
139 | "m_prompt_travel_folder": "プロンプトトラベル",
140 | "m_sd_model_folder": "モデル",
141 | "m_motion_module_folder": "モーションモジュール",
142 | "m_lora_folder": "LoRA",
143 | "m_embeddings_folder": "TI (Embedding)",
144 | "m_wildcard_folder": "ワイルドカード",
145 | "m_prompt_travel_output_folder": "プロンプトトラベル出力",
146 | "m_prompt_travel_generate_folder": "アニメ生成",
147 | "m_prompt_travel_tile_folder": "タイルアップスケール",
148 | "m_prompt_travel_refine_folder": "リファインアップスケール",
149 | "m_temp_folder": "テンポラリ",
150 | "m_log_folder": "ログ",
151 | "m_tool": "ツール",
152 | "m_tool_convert_lora": "使えない LoRA を使えるように変換",
153 | "m_tool_easy_leco": "簡単LECO でプロンプトから LoRA を作成",
154 | "m_tool_easy_leco_url": "https://colab.research.google.com/drive/1LYi2kvZV1XN05MATqhqWs79cNdTkb33P",
155 | "m_download": "ダウンロード",
156 | "m_dl_model": "モデル",
157 | "m_dl_lora": "LoRA",
158 | "m_dl_motion_module": "モーションモジュール",
159 | "m_dl_motion_lora": "モーション LoRA",
160 | "m_dl_vae": "VAE",
161 | "m_dl_embedding": "TI (Embedding)",
162 | "m_setting": "設定",
163 | "m_set_default_prompt": "現在のプロンプトをデフォルトプロンプトにする",
164 | "m_set_default_setting": "現在の設定をデフォルト設定にする",
165 | "m_set_change_light_dark": "ライト/ダークモードの切り替え(再起動後に適用)",
166 | "m_set_open_ini_file": "簡単プロンプトアニメの設定 ini ファイルを開く",
167 | "m_set_open_default_prompt_file": "デフォルトのプロンプトファイルを開く",
168 | "m_set_open_prompt_travel_template_file": "プロンプトトラベルのテンプレートファイルを開く",
169 | "m_help": "ヘルプ",
170 | "m_prompt_help": "プロンプトの書き方",
171 | "m_github": "簡単プロンプトアニメ GitHub",
172 | "m_reference": "参照",
173 | "dlg_ask_save": "保存していない変更があります。変更内容を保存しますか?",
174 | "dlg_convert_lora_title": "変換する LoRA を選択してください。",
175 | "dlg_upscale_title": "アップスケールする連番 png の入ったフォルダ(00-シード値~)を選択してください。",
176 | "dlg_control_net_import": "取り込む MP4 ファイルを選択してください。",
177 | "dlg_mosaic_title": "モザイクをかける連番 png の入ったフォルダ(00-シード値~)を選択してください。",
178 | "log_initialization_end": "起動に掛かった時間: {0:.2f}秒",
179 | "log_see_prompt_help": "プロンプトの書き方は [ヘルプ] メニューの [プロンプトの書き方] を参照してください。",
180 | "log_please_wait": "しばらくお待ちください。",
181 | "log_file_new": "ファイルを新規作成しました。",
182 | "log_file_open": "ファイルを開きました。{0}",
183 | "log_file_open_failed": "ファイルを開くのに失敗しました。{0}",
184 | "log_file_save": "ファイルを上書き保存しました。{0}",
185 | "log_file_save_as": "ファイルに名前を付けて保存しました。{0}",
186 | "log_task_start": "{0} を開始しました。",
187 | "log_task_success": "{0} に成功しました。 {1:.2f}秒",
188 | "log_task_failed": "{0} に失敗しました。 {1:.2f}秒\n[エラーで一時停止] を有効にして、エラーの内容を確認してください。",
189 | "log_ffplay": "シード値 {0} を再生します。\n再生設定は EasyPromptAnimeEditor.ini の ffplay_cmd で変更できます。",
190 | "log_upscale": "現在の初回アップスケールの方式と高さの設定で、連番pngをアップスケールします。",
191 | "log_upscale_config_override": "現在の初回アップスケールの全設定(モデルなども含む)で、連番pngをアップスケールします。",
192 | "log_convert_lora": "LoRA を dim:{0} で変換します。\n変換元 LoRA: {1}\n抽出用マージモデル: {2}\n出力先 LoRA: {3}",
193 | "log_start_download": "{0} のダウンロードを開始します。\n{1}",
194 | "err_inv_line": "行の表記が正しくありません。 {0}",
195 | "err_inv_frame_num": "フレームの値が正しくありません。{0}: {1}",
196 | "err_inv_lora_def": 'LoRA の定義が正しくありません。"{0}" {1}',
197 | "err_inv_motion_lora_def": 'モーション LoRA の定義が正しくありません。"{0}" {1}',
198 | "hlp_prompt": """
199 | # フレーム数+コロン(:)+プロンプトで、アニメのキーフレームにプロンプトを指定します。
200 | # フレーム数+コロンではなく、「H:」「F:」「N:」「L:」の場合はアニメ全体に特別な設定をします。
201 | # シャープ(#)以降はコメントです。
202 |
203 | H: crowds, akihabara
204 | # 「H:」か「h:」で始まる行はヘッダプロンプトの指定で、アニメのキーフレームのプロンプト先頭にヘッダプロンプトを足します。
205 |
206 | 0: standing, upset
207 | 10: waving at viewer, surprised
208 | 20: waving at viewer, smile
209 | # アニメの 0フレームに「standing, upset」、10フレームに「waving at viewer, surprised」、20フレームに「waving at viewer, smile」のプロンプトを使用します。
210 | # 1秒間に 10枚の画像が表示され(10 Frame per second)で、キーフレームの間は補間されます。
211 |
212 | F: 1girl, maid outfit
213 | # 「F:」か「f:」で始まる行はフッタプロンプトの指定で、アニメのキーフレームのプロンプト末尾にフッタプロンプトを足します。
214 |
215 | # 上記の指定で3秒間(30フレーム)アニメ生成した場合の、キーフレームのプロンプトは以下になります。
216 | # 0フレーム「crowds, akihabara, standing, upset, 1girl, maid outfit」
217 | # 10フレーム「crowds, akihabara, waving at viewer, surprised, 1girl, maid outfit」
218 | # 20フレーム「crowds, akihabara, waving at viewer, smile, 1girl, maid outfit」
219 | # 30フレーム「crowds, akihabara, standing, upset, 1girl, maid outfit」
220 |
221 | # 細々とキーフレームを設定しなくとも、0フレームに「dancing」のような動きを表す言葉を入れておけば、AI が勝手に動かしてくれます。
222 |
223 | N: (worst quality, low quality:1.4)
224 | #「N:」か「n:」で始まる行は、アニメの全フレームで使用するネガティブプロンプトを指定します。
225 |
226 | # L: explosion:-0.2 # 行頭の # でコメントになっています。LoRA を使う場合は先頭の # を消してください。
227 | #「L:」か「l:」で始まる行は、LoRA のファイル名+コロン(:)+強度を、カンマ(,)区切りで複数指定できます。
228 | # LoRAを使いたい場合は、メニューの [フォルダ - プロンプトトラベル - LoRA] に *.safetensors 形式の LoRA を置いてください。
229 | # 使える LoRA は通常の LierLa 形式です。C3Lier(Locon) 形式はメニューの [ツール - LoRA 変換] で LierLa 形式に変換して利用できます。
230 |
231 | # M: v2_lora_ZoomIn: 1.0 # LoRA と同様に M でモーション LoRA を指定できます。
232 | """,
233 | }
234 |
--------------------------------------------------------------------------------
/editor/src/log.py:
--------------------------------------------------------------------------------
1 | class Log:
2 | userLogEvent = []
3 | systemLogEvent = []
4 |
5 | @classmethod
6 | def user(cls, msg):
7 | # msgNl = msg if msg.endswith("\n") else msg + "\n"
8 | # msgNoNewLine = (msg[:-1] if msg.endswith("\n") else msg)
9 |
10 | [hadler(msg) for hadler in cls.userLogEvent]
11 | cls.system(msg)
12 |
13 | @classmethod
14 | def system(cls, msg):
15 | # msgNl = msg if msg.endswith("\n") else msg + "\n"
16 | # msgNoNewLine = (msg[:-1] if msg.endswith("\n") else msg)
17 | [hadler(msg) for hadler in cls.systemLogEvent]
18 | print(msg)
19 |
20 | # @classmethod
21 | # def _system_impl(cls, msg):
22 | # [hadler(msg) for hadler in cls.systemLogEvent]
23 | # print(msg)
24 |
--------------------------------------------------------------------------------
/editor/src/mdl_editor.py:
--------------------------------------------------------------------------------
1 | from config import Config
2 | from mdl_notifier import Notifier
3 |
4 |
5 | class Editor(Notifier):
6 | defPreviewUpscale = True
7 | # defPreviewInterpolation = True
8 | defPreviewStart = 0
9 | defPreviewLength = 0
10 |
11 | defPreviewShowKeyframe = True
12 | defPreviewShowHeaderFooter = True
13 | defPreviewShowAnime = True
14 |
15 | defTaskForever = False
16 | defTaskPauseByError = True
17 |
18 | defImportSpeed = "1"
19 | defImportStart = ""
20 | defImportLength = ""
21 | defImportIndex = "0"
22 |
23 | defFfplayCmd = "ffplay.exe -left 160 -top 80 -loop 5 -autoexit -loglevel error"
24 |
25 | def __init__(self):
26 | super().__init__()
27 | self.reset()
28 |
29 | def reset(self):
30 | self.savePath = ""
31 |
32 | self.previewUpscale = Editor.defPreviewUpscale
33 | # self.previewInterpolation = Editor.defPreviewInterpolation
34 | self.previewStart = Editor.defPreviewStart
35 | self.previewLength = Editor.defPreviewLength
36 |
37 | self.previewShowKeyframe = Editor.defPreviewShowKeyframe
38 | self.previewShowHeaderFooter = Editor.defPreviewShowHeaderFooter
39 | self.previewShowAnime = Editor.defPreviewShowAnime
40 |
41 | self.taskForever = Editor.defTaskForever
42 | self.taskPauseByError = Editor.defTaskPauseByError
43 |
44 | self.importSpeed = Editor.defImportSpeed
45 | self.importStart = Editor.defImportStart
46 | self.importLength = Editor.defImportLength
47 | self.importIndex = Editor.defImportIndex
48 |
49 | self.ffplayCmd = Editor.defFfplayCmd
50 |
51 | self.isChanged = True
52 |
53 | def set(self, memberName, value):
54 | result = super().set(memberName, value)
55 | if result:
56 | self.isChanged = True
57 | return result
58 |
59 | @classmethod
60 | def loadConfig(cls):
61 | Editor.defPreviewUpscale = Config.getBool(
62 | "ui_preview", "upscale", Editor.defPreviewUpscale
63 | )
64 | # Editor.defPreviewInterpolation = Config.getBool(
65 | # "ui_preview", "interpolation", Editor.defPreviewInterpolation
66 | # )
67 | Editor.defPreviewStart = Config.getInt(
68 | "ui_preview", "start", Editor.defPreviewStart
69 | )
70 | Editor.defPreviewLength = Config.getInt(
71 | "ui_preview", "length", Editor.defPreviewLength
72 | )
73 |
74 | Editor.defPreviewShowKeyframe = Config.getBool(
75 | "ui_preview", "show_keyframe", Editor.defPreviewShowKeyframe
76 | )
77 | Editor.defPreviewShowHeaderFooter = Config.getBool(
78 | "ui_preview", "show_header_footer", Editor.defPreviewShowHeaderFooter
79 | )
80 | Editor.defPreviewShowAnime = Config.getBool(
81 | "ui_preview", "show_anime", Editor.defPreviewShowAnime
82 | )
83 |
84 | Editor.defTaskForever = Config.getBool("ui", "forever", Editor.defTaskForever)
85 | Editor.defTaskPauseByError = Config.getBool(
86 | "ui", "pause_by_error", Editor.defTaskPauseByError
87 | )
88 |
89 | Editor.defImportSpeed = Config.get("ui", "import_speed", Editor.defImportSpeed)
90 | Editor.defImportStart = Config.get("ui", "import_start", Editor.defImportStart)
91 | Editor.defImportLength = Config.get(
92 | "ui", "import_length", Editor.defImportLength
93 | )
94 | Editor.defImportIndex = Config.get("ui", "import_index", Editor.defImportIndex)
95 |
96 | Editor.defFfplayCmd = Config.get("ui", "ffplay_cmd", Editor.defFfplayCmd)
97 |
98 | def updateConfig(self):
99 | Editor.defPreviewUpscale = self.previewUpscale
100 | # Editor.defPreviewInterpolation = self.previewInterpolation
101 | Editor.defPreviewStart = self.previewStart
102 | Editor.defPreviewLength = self.previewLength
103 |
104 | Editor.defPreviewShowKeyframe = self.previewShowKeyframe
105 | Editor.defPreviewShowHeaderFooter = self.previewShowHeaderFooter
106 | Editor.defPreviewShowAnime = self.previewShowAnime
107 |
108 | Editor.defTaskForever = self.taskForever
109 | Editor.defTaskPauseByError = self.taskPauseByError
110 |
111 | Editor.defImportSpeed = self.importSpeed
112 | Editor.defImportStart = self.importStart
113 | Editor.defImportLength = self.importLength
114 | Editor.defImportIndex = self.importIndex
115 |
116 | # Editor.defFfplayCmd = self.ffplayCmd
117 |
118 | def storeConfig(self):
119 | Config.set("ui_preview", "upscale", Editor.defPreviewUpscale)
120 | # Config.set("ui_preview", "interpolation", Editor.defPreviewInterpolation)
121 | Config.set("ui_preview", "start", Editor.defPreviewStart)
122 | Config.set("ui_preview", "length", Editor.defPreviewLength)
123 |
124 | Config.set("ui_preview", "show_keyframe", Editor.defPreviewShowKeyframe)
125 | Config.set(
126 | "ui_preview", "show_header_footer", Editor.defPreviewShowHeaderFooter
127 | )
128 | Config.set("ui_preview", "show_anime", Editor.defPreviewShowAnime)
129 |
130 | Config.set("ui", "forever", Editor.defTaskForever)
131 | Config.set("ui", "pause_by_error", Editor.defTaskPauseByError)
132 |
133 | Config.set("ui", "import_speed", Editor.defImportSpeed)
134 | Config.set("ui", "import_start", Editor.defImportStart)
135 | Config.set("ui", "import_length", Editor.defImportLength)
136 | Config.set("ui", "import_index", Editor.defImportIndex)
137 |
138 | Config.set("ui", "ffplay_cmd", Editor.defFfplayCmd)
139 |
--------------------------------------------------------------------------------
/editor/src/mdl_notifier.py:
--------------------------------------------------------------------------------
1 | class Notifier:
2 | def __init__(self):
3 | self._subscribers = {}
4 | pass
5 |
6 | def subsc(self, memberName, func):
7 | if self._subscribers.get(memberName) is None:
8 | self._subscribers[memberName] = []
9 | self._subscribers[memberName].append(func)
10 |
11 | def unsubsc(self, memberName, func):
12 | if self._subscribers.get(memberName) is None:
13 | return
14 | self._subscribers[memberName].remove(func)
15 | if len(self._subscribers[memberName]) == 0:
16 | del self._subscribers[memberName]
17 |
18 | def notify(self, memberName, value):
19 | if self._subscribers.get(memberName) is None:
20 | return
21 | for func in self._subscribers[memberName]:
22 | func(self, memberName, value)
23 |
24 | def notifyAll(self):
25 | for memberName in self._subscribers.keys():
26 | self.notify(memberName, getattr(self, memberName))
27 |
28 | def set(self, memberName, value):
29 | if getattr(self, memberName) == value:
30 | return False
31 | setattr(self, memberName, value)
32 | self.notify(memberName, value)
33 | return True
34 |
35 | def trace_add(self, memberName, var):
36 | return var.trace_add("write", lambda *args: self.set(memberName, var.get()))
37 |
38 | def trace_remove(self, var, tid):
39 | var.trace_remove("write", tid)
40 |
41 | def bind(self, memberName, var):
42 | self.subsc(memberName, lambda *args: var.set(getattr(self, memberName)))
43 | self.trace_add(memberName, var)
44 |
--------------------------------------------------------------------------------
/editor/src/mdl_prompt.py:
--------------------------------------------------------------------------------
1 | from const import Path
2 | from mdl_notifier import Notifier
3 | import os
4 | from prompt_perser import PromptPerser
5 |
6 |
7 | class Prompt(Notifier):
8 | defaultPrompt = """H: crowds, akihabara
9 | 0: standing, upset
10 | 10: waving at viewer, surprised
11 | 20: waving at viewer, smile
12 | F: 1girl, maid outfit
13 | N:(worst quality, low quality:1.2)
14 | """
15 |
16 | def __init__(self):
17 | super().__init__()
18 | self.text = ""
19 | self.data = None
20 | # self.data = {
21 | # "header": "",
22 | # "footer": "",
23 | # "negative": "",
24 | # "lora_map": {},
25 | # "prompt_map": {},
26 | # "errors": [],
27 | # }
28 | self.subsc("text", self.updatePrompts)
29 | self.reset()
30 |
31 | def updatePrompts(self, *args):
32 | self.data = PromptPerser.persePrompt(self.text)
33 | self.isChanged = True
34 |
35 | def reset(self):
36 | self.loadDefaultPrompt()
37 |
38 | def getPreview(self, stert, length, showKeyframe, showHeaderFooter, showAnime):
39 | return PromptPerser.persePreview(
40 | self.data, stert, length, showKeyframe, showHeaderFooter, showAnime
41 | )
42 |
43 | def loadDefaultPrompt(self):
44 | if os.path.exists(Path.defaultPrompt):
45 | with open(Path.defaultPrompt, "r", encoding="utf-8-sig") as f:
46 | self.set("text", f.read())
47 | else:
48 | with open(Path.defaultPrompt, "w", encoding="utf-8-sig") as f:
49 | f.write(Prompt.defaultPrompt)
50 | self.set("text", Prompt.defaultPrompt)
51 |
52 | def saveDefaultPrompt(self):
53 | Prompt.defaultPrompt = self.text
54 | with open(Path.defaultPrompt, "w", encoding="utf-8-sig") as f:
55 | f.write(Prompt.defaultPrompt.strip() + "\n")
56 |
57 | def getLastPrompt(self):
58 | frames = sorted(self.data["prompt_map"].keys())
59 | length = len(frames)
60 | if length == 0:
61 | return (0, "")
62 | maxFrame = frames[length - 1]
63 | return (maxFrame, self.data["prompt_map"][maxFrame])
64 |
--------------------------------------------------------------------------------
/editor/src/model.py:
--------------------------------------------------------------------------------
1 | from mdl_prompt import Prompt
2 | from mdl_generate import Generate
3 | from mdl_editor import Editor
4 |
5 |
6 | class Model:
7 | def __init__(self):
8 | self.prompt = Prompt()
9 | self.generate = Generate()
10 | self.editor = Editor()
11 |
12 | def reset(self):
13 | self.prompt.reset()
14 | self.generate.reset()
15 | self.editor.reset()
16 |
17 | def notifyAll(self):
18 | self.prompt.notifyAll()
19 | self.generate.notifyAll()
20 | self.editor.notifyAll()
21 |
22 | @classmethod
23 | def loadConfig(cls):
24 | Generate.loadConfig()
25 | Editor.loadConfig()
26 |
27 | def updateConfig(self):
28 | self.generate.updateConfig()
29 | self.editor.updateConfig()
30 |
31 | def storeConfig(self):
32 | self.generate.storeConfig()
33 | self.editor.storeConfig()
34 |
35 | def getPreviewLength(self):
36 | if self.editor.previewLength == 0:
37 | return self.generate.length - self.editor.previewStart
38 | else:
39 | return self.editor.previewLength
40 |
41 | def getPreviewEnd(self):
42 | if self.editor.previewLength == 0:
43 | return self.generate.length
44 | else:
45 | return self.editor.previewStart + self.editor.previewLength
46 |
--------------------------------------------------------------------------------
/editor/src/mosaic.py:
--------------------------------------------------------------------------------
1 | import os, glob, cv2
2 | import numpy as np
3 | from nudenet import NudeDetector
4 | from l10n import L10n
5 | from collections import OrderedDict
6 | from log import Log
7 |
8 |
9 | class Mosaic:
10 | partsTable = {
11 | "FACE_FEM" + "ALE": "mosaicFemFace",
12 | "FEM" + "ALE_BRE" + "AST_EXPOSED": "mosaicFemBrst",
13 | "FEM" + "ALE_BRE" + "AST_COVERED": "mosaicFemBrstCov",
14 | "FEM" + "ALE_GENI" + "TALIA_EXPOSED": "mosaicFemGntl",
15 | "FEM" + "ALE_GENI" + "TALIA_COVERED": "mosaicFemGntlCov",
16 | "FACE_MALE": "mosaicMaleFace",
17 | "MALE_BRE" + "AST_EXPOSED": "mosaicMaleBrst",
18 | "MALE_GENI" + "TALIA_EXPOSED": "mosaicMaleGntl",
19 | "ARMPITS_EXPOSED": "mosaicArmpit",
20 | "ARMPITS_COVERED": "mosaicArmpitCov",
21 | "BELLY_EXPOSED": "mosaicBelly",
22 | "BELLY_COVERED": "mosaicBellyCov",
23 | "BUT" + "TOCKS_EXPOSED": "mosaicHip",
24 | "BUT" + "TOCKS_COVERED": "mosaicHipCov",
25 | "AN" + "US_EXPOSED": "mosaicAns",
26 | "AN" + "US_COVERED": "mosaicAnsCov",
27 | "FEET_EXPOSED": "mosaicFeet",
28 | "FEET_COVERED": "mosaicFeetCov",
29 | }
30 |
31 | @classmethod
32 | def mosaicPngDir(cls, generate, pngDir):
33 | pngPaths = sorted(glob.glob(os.path.join(pngDir, "*.png")))
34 | if len(pngPaths) == 0:
35 | return ""
36 |
37 | mosaics = cls.detectMosaic(generate, pngPaths)
38 |
39 | if generate.temporalMosaic:
40 | mosaics = cls.temporalMosaic(mosaics)
41 |
42 | dstDirName = os.path.basename(pngDir) + "_mosaic"
43 | dstDir = os.path.join(os.path.dirname(pngDir), dstDirName)
44 | os.makedirs(dstDir, exist_ok=True)
45 | for mosaic in mosaics:
46 | cls.mosaic(generate, mosaic, dstDir)
47 |
48 | return dstDir
49 |
50 | @classmethod
51 | def detectMosaic(cls, generate, pngPaths):
52 | nude_detector = NudeDetector()
53 | results = []
54 | for pngPath in pngPaths:
55 | detections = []
56 | for detection in nude_detector.detect(pngPath):
57 | if detection["score"] < generate.mosaicThreshold:
58 | continue
59 | if detection["class"] not in cls.partsTable:
60 | Log.system(f'Unknown detection class: {detection["class"]}.')
61 | continue
62 | if not getattr(generate, cls.partsTable[detection["class"]]):
63 | continue
64 | detections.append(detection)
65 | results.append({"path": pngPath, "detections": detections})
66 | return results
67 |
68 | @classmethod
69 | def temporalMosaic(cls, mosaics):
70 | temporalMosaics = []
71 | mosaicsNum = len(mosaics)
72 | for i in range(mosaicsNum):
73 | temporalMosaic = {
74 | "path": mosaics[i]["path"],
75 | "detections": [],
76 | }
77 |
78 | for detection in mosaics[i]["detections"]:
79 | temporalMosaic["detections"].append(detection)
80 | counter = 1
81 | while True:
82 | if len(temporalMosaic["detections"]) >= 1: # TODO:
83 | break
84 | if i >= counter:
85 | for detection in mosaics[i - counter]["detections"]:
86 | temporalMosaic["detections"].append(detection)
87 | if i < mosaicsNum - counter:
88 | for detection in mosaics[i + counter]["detections"]:
89 | temporalMosaic["detections"].append(detection)
90 | counter += 1
91 | if counter >= mosaicsNum:
92 | break
93 | temporalMosaics.append(temporalMosaic)
94 | return temporalMosaics
95 |
96 | @classmethod
97 | def mosaic(cls, generate, mosaic, dstDir):
98 | srcImg = cv2.imread(mosaic["path"])
99 | srcH, srcW = srcImg.shape[:2]
100 | mosaicScale = 100 / srcW
101 | maskBlur = int(generate.mosaicMaskBlur * srcW)
102 | if srcH > srcW:
103 | mosaicScale = 100 / srcH
104 | maskBlur = int(generate.mosaicMaskBlur * srcH)
105 |
106 | mosaicImg = cv2.resize(srcImg, None, fx=mosaicScale, fy=mosaicScale)
107 | mosaicImg = cv2.resize(mosaicImg, (srcW, srcH), interpolation=cv2.INTER_NEAREST)
108 |
109 | mosaicMask = np.zeros((srcH, srcW), dtype=np.uint8)
110 |
111 | for detection in mosaic["detections"]:
112 | box = detection["box"]
113 | cx = box[0] + (box[2] * 0.5)
114 | cy = box[1] + (box[3] * 0.5)
115 | hw = box[2] * 0.5
116 | hh = box[3] * 0.5
117 | st = hh * generate.mosaicScaleTop
118 | sb = hh * generate.mosaicScaleBottom
119 | sl = hw * generate.mosaicScaleLeft
120 | sr = hw * generate.mosaicScaleRight
121 | if generate.ellipseMosaic:
122 | cx = int((sr - sl) * 0.5 + cx)
123 | cy = int((sb - st) * 0.5 + cy)
124 | rx = int((sl + sr) / 2)
125 | ry = int((st + sb) / 2)
126 | cv2.ellipse(mosaicMask, ((cx, cy), (rx, ry), 0), 255, -1)
127 | else:
128 | cv2.rectangle(
129 | mosaicMask,
130 | (int(cx - sl), int(cy - st)),
131 | (int(cx + sr), int(cy + sb)),
132 | 255,
133 | -1,
134 | )
135 |
136 | if generate.mosaicIgnoreTop > 0:
137 | top = int(srcH * generate.mosaicIgnoreTop)
138 | cv2.rectangle(mosaicMask, (0, 0), (srcW, top), 0, -1)
139 | if generate.mosaicIgnoreBottom > 0:
140 | bottom = int(srcH * (1 - generate.mosaicIgnoreBottom))
141 | cv2.rectangle(mosaicMask, (0, bottom), (srcW, srcH), 0, -1)
142 | if generate.mosaicIgnoreLeft > 0:
143 | left = int(srcW * generate.mosaicIgnoreLeft)
144 | cv2.rectangle(mosaicMask, (0, 0), (left, srcH), 0, -1)
145 | if generate.mosaicIgnoreRight > 0:
146 | right = int(srcW * (1 - generate.mosaicIgnoreRight))
147 | cv2.rectangle(mosaicMask, (right, 0), (srcW, srcH), 0, -1)
148 |
149 | if maskBlur > 0:
150 | maskBlur = maskBlur if (maskBlur % 2) == 1 else maskBlur + 1
151 | mosaicMask = cv2.blur(mosaicMask, (maskBlur, maskBlur))
152 |
153 | srcImg = np.array(srcImg)
154 | mosaicImg = np.array(mosaicImg)
155 | mosaicAlpha = np.dstack((mosaicMask,) * 3) / 255
156 | blendImg = srcImg * (1 - mosaicAlpha) + mosaicImg * mosaicAlpha
157 |
158 | dstPath = os.path.join(dstDir, os.path.basename(mosaic["path"]))
159 | cv2.imwrite(dstPath, blendImg.astype(np.uint8))
160 | # cv2.imwrite(dstPath, mosaicMask)
161 |
--------------------------------------------------------------------------------
/editor/src/prompt_perser.py:
--------------------------------------------------------------------------------
1 | from l10n import L10n
2 | from collections import OrderedDict
3 |
4 |
5 | class PromptPerser:
6 | @classmethod
7 | def persePrompt(cls, txt):
8 | data = {
9 | "header": "",
10 | "footer": "",
11 | "negative": "",
12 | "lora_map": {},
13 | "motion_lora_map": {},
14 | "prompt_map": {},
15 | "errors": [],
16 | }
17 |
18 | for line in txt.splitlines():
19 | try:
20 | cls.perseLine(data, line)
21 | except Exception as e:
22 | data["errors"].append(str(e))
23 |
24 | # TODO: apply range prompt
25 | return data
26 |
27 | @classmethod
28 | def perseLine(cls, data, line):
29 | commentRemoved = line.split("#", 1)[0].strip()
30 | if commentRemoved == "":
31 | return
32 | commentRemovedKvp = commentRemoved.split(":", 1)
33 | stripedKey = commentRemovedKvp[0].strip()
34 | if len(commentRemovedKvp) < 2:
35 | cls.addPrompt(data, 0, stripedKey) # 0 frame prompt
36 | return
37 | val = commentRemovedKvp[1].strip()
38 | if stripedKey == "":
39 | cls.addPrompt(data, 0, val) # 0 frame prompt
40 | return
41 | key = stripedKey.lower()
42 | if key == "h":
43 | if data["header"] != "":
44 | data["header"] += ", "
45 | data["header"] += val
46 | elif key == "f":
47 | if data["footer"] != "":
48 | data["footer"] += ", "
49 | data["footer"] += val
50 | elif key == "n":
51 | if data["negative"] != "":
52 | data["negative"] += ", "
53 | data["negative"] += val
54 | elif key == "l":
55 | for lora_def in val.split(","):
56 | lora_kvp = lora_def.split(":", 1)
57 | if len(lora_kvp) < 2:
58 | data["errors"].append(
59 | L10n.format("err_inv_lora_def", lora_def, val)
60 | )
61 | return
62 | lora_key = lora_kvp[0].strip()
63 | lora_weight = lora_kvp[1].strip()
64 | try:
65 | lora_weight = float(lora_weight)
66 | data["lora_map"][lora_key] = lora_weight
67 | except ValueError:
68 | data["errors"].append(
69 | L10n.format("err_inv_lora_def", lora_def, val)
70 | )
71 | return
72 | elif key == "m":
73 | for motion_lora_def in val.split(","):
74 | motion_lora_kvp = motion_lora_def.split(":", 1)
75 | if len(motion_lora_kvp) < 2:
76 | data["errors"].append(
77 | L10n.format("err_inv_motion_lora_def", motion_lora_def, val)
78 | )
79 | return
80 | motion_lora_key = motion_lora_kvp[0].strip()
81 | motion_lora_weight = motion_lora_kvp[1].strip()
82 | try:
83 | motion_lora_weight = float(motion_lora_weight)
84 | data["motion_lora_map"][motion_lora_key] = motion_lora_weight
85 | except ValueError:
86 | data["errors"].append(
87 | L10n.format("err_inv_motion_lora_def", motion_lora_def, val)
88 | )
89 | return
90 | elif key.isdigit():
91 | cls.addPrompt(data, int(key), val)
92 | else:
93 | # TODO: parse range prompt
94 | data["errors"].append(L10n.format("err_inv_line", line))
95 |
96 | @classmethod
97 | def addPrompt(self, data, frame, prompt):
98 | if not isinstance(frame, int) or frame < 0:
99 | data["errors"].append(L10n.format("err_inv_frame_num", frame, prompt))
100 | return
101 |
102 | promptMap = data["prompt_map"]
103 | if frame in promptMap:
104 | promptMap[frame] = f"{promptMap[frame]}, {prompt}"
105 | else:
106 | promptMap[frame] = prompt
107 |
108 | @classmethod
109 | def persePreview(
110 | cls, data, start, length, showKeyframe, showHeaderFooter, showAnime
111 | ):
112 | preview = []
113 | header = data["header"]
114 | header = (header + ", ") if header != "" else header
115 |
116 | footer = data["footer"]
117 | footer = (", " + footer) if footer != "" else footer
118 |
119 | end = start + length
120 | firstPrompt = ""
121 | preFrame = -1
122 | prePrompt = ""
123 | for frame in range(start, end + 1):
124 | if frame in data["prompt_map"]:
125 | prompt = data["prompt_map"][frame]
126 | if firstPrompt == "":
127 | firstPrompt = prompt
128 | else:
129 | if showAnime:
130 | preview.append(
131 | f"{preFrame:>3}-{frame:>2}: ({cls.diffPrompt(prePrompt, prompt)}: 0.5)"
132 | )
133 | if showKeyframe:
134 | if showHeaderFooter:
135 | preview.append(f"{frame:>3}: {header}{prompt}{footer}")
136 | else:
137 | preview.append(f"{frame:>3}: {prompt}")
138 | preFrame = frame
139 | prePrompt = prompt
140 | if firstPrompt != "" and showAnime:
141 | preview.append(
142 | f"{preFrame:>3}-{start:>2}: ({cls.diffPrompt(prePrompt, firstPrompt)}: 0.5)"
143 | )
144 |
145 | negative = data["negative"]
146 | if negative != "":
147 | preview.append(f"{L10n.get('negative_prompt')}: {negative}")
148 |
149 | loras = data["lora_map"].keys()
150 | if len(loras) > 0:
151 | loraDefs = "LoRA: "
152 | for lora in loras:
153 | loraDefs += f" "
154 | preview.append(loraDefs)
155 |
156 | motion_loras = data["motion_lora_map"].keys()
157 | if len(motion_loras) > 0:
158 | motion_loraDefs = "Motion LoRA: "
159 | for motion_lora in motion_loras:
160 | motion_loraDefs += f" "
161 | preview.append(motion_loraDefs)
162 |
163 | for err in data["errors"]:
164 | preview.append(f"{L10n.get('error')}: {err}")
165 |
166 | return "\n".join(preview)
167 |
168 | @classmethod
169 | def diffPrompt(cls, prePrompt, postPrompt):
170 | preSet = set(map(str.strip, prePrompt.split(",")))
171 | postSet = set(map(str.strip, postPrompt.split(",")))
172 | uniqueSet = preSet.symmetric_difference(postSet)
173 | return ", ".join(uniqueSet) # + f" | {prePrompt} | {postPrompt}"
174 |
--------------------------------------------------------------------------------
/editor/src/task.py:
--------------------------------------------------------------------------------
1 | import time
2 | from l10n import L10n
3 | from log import Log
4 | from const import Path
5 |
6 |
7 | class Task:
8 | win = None
9 | queue = []
10 | queueInfoChangedEvent = []
11 | pollTime = 100
12 |
13 | @classmethod
14 | def initialize(cls, win):
15 | Task.win = win
16 |
17 | @classmethod
18 | def queueInfoChanged(cls):
19 | [handler(cls.queue) for handler in cls.queueInfoChangedEvent]
20 |
21 | @classmethod
22 | def enqueue(cls, task):
23 | task.resetState()
24 | cls.queue.append(task)
25 | cls.queueInfoChanged()
26 | if len(cls.queue) == 1:
27 | cls.win.after(cls.pollTime, cls.runQueue)
28 |
29 | @classmethod
30 | def dequeue(cls):
31 | task = cls.queue[0]
32 | cls.queue.remove(task)
33 | cls.queueInfoChanged()
34 | return task
35 |
36 | @classmethod
37 | def runQueue(cls):
38 | if len(cls.queue) == 0:
39 | return
40 |
41 | if cls.queue[0].isComleted:
42 | endTask = cls.dequeue()
43 | endTask.time = time.perf_counter() - endTask.startTime
44 | endTask.logEnd()
45 |
46 | if endTask.reRun():
47 | cls.enqueue(endTask)
48 | if len(cls.queue) == 0:
49 | return
50 |
51 | startNow = not cls.queue[0].isRunning
52 | if startNow:
53 | cls.queue[0].startTime = time.perf_counter()
54 | cls.queue[0].timeStr = Path.getYYYYMMDDHHMMSS()
55 | cls.queue[0].isRunning = True
56 | cls.queue[0].logStart()
57 |
58 | if cls.queue[0].run() or startNow:
59 | cls.queueInfoChanged()
60 |
61 | if len(cls.queue) > 0:
62 | cls.win.after(cls.pollTime, cls.runQueue)
63 |
64 | def __init__(self):
65 | self.resetState()
66 |
67 | def resetState(self):
68 | self.startTime = 0.0
69 | self.time = 0.0
70 | self.timeStr = ""
71 | self.isRunning = False
72 | self.isComleted = False
73 | self.isFailed = False
74 |
75 | def run(self):
76 | return False
77 |
78 | def reRun(self):
79 | return False
80 |
81 | def logStart(self):
82 | Log.user(L10n.format("log_task_start", self))
83 |
84 | def logEnd(self):
85 | if self.isFailed:
86 | Log.user(L10n.format("log_task_failed", self, self.time))
87 | else:
88 | Log.user(L10n.format("log_task_success", self, self.time))
89 |
90 | def __str__(self):
91 | if self.isComleted:
92 | if self.isFailed:
93 | return "[F]"
94 | else:
95 | return "[S]"
96 | else:
97 | if self.isRunning:
98 | return "[R]"
99 | else:
100 | return "[_]"
101 |
--------------------------------------------------------------------------------
/editor/src/tsk_command_prompt.py:
--------------------------------------------------------------------------------
1 | import os, subprocess, threading
2 | from const import Path
3 | from l10n import L10n
4 | from task import Task
5 |
6 |
7 | class CommandPromptTask(Task):
8 | def __init__(self, editor, taskMode):
9 | super().__init__()
10 | self.editor = editor
11 | self.taskMode = taskMode
12 | self.taskName = L10n.get(taskMode)
13 |
14 | def resetState(self):
15 | super().resetState()
16 | self.tempDir = ""
17 | self.errorPath = ""
18 |
19 | self.command = None
20 | self.process = None
21 | self.log = ""
22 | self.thread = None
23 |
24 | def run(self):
25 | if self.thread is None:
26 | self.startThread()
27 | return self.pollThread()
28 |
29 | def startThread(self):
30 | self.tempDir = os.path.join(Path.temp, self.timeStr)
31 | os.path.exists(self.tempDir) or os.makedirs(self.tempDir)
32 |
33 | self.errorPath = os.path.join(self.tempDir, f"{self.timeStr}-Error.txt")
34 |
35 | cmd = self.createCommand()
36 | if self.editor.taskPauseByError:
37 | cmd += f" || (echo %errorlevel% > {self.errorPath} & pause)"
38 | else:
39 | cmd += f" || (echo %errorlevel% > {self.errorPath})"
40 | self.command = ["start", "/wait", "cmd", "/c", cmd]
41 |
42 | self.log = ""
43 | self.process = subprocess.Popen(
44 | self.command,
45 | stdout=subprocess.PIPE,
46 | stderr=subprocess.STDOUT,
47 | shell=True,
48 | encoding="utf-8",
49 | )
50 | self.thread = threading.Thread(target=self.threadMain)
51 | self.thread.start()
52 |
53 | def createCommand(self):
54 | return ""
55 |
56 | def threadMain(self):
57 | self.log = self.process.communicate()[0]
58 |
59 | def pollThread(self):
60 | if self.thread.is_alive():
61 | return False
62 |
63 | self.isComleted = True
64 | self.isFailed = (
65 | (self.process.returncode != 0)
66 | or (os.path.exists(self.errorPath))
67 | or (self.log == "^C")
68 | )
69 |
70 | self.thread = None
71 | self.process = None
72 | self.isFailed or self.postProcess()
73 | # Log.system(self.log) # "^C" or ""
74 | return True
75 |
76 | def postProcess(self):
77 | pass
78 |
79 | def reRun(self):
80 | if self.isFailed:
81 | return False
82 | return self.editor.taskForever
83 |
84 | def __str__(self):
85 | return f"{super().__str__()} {self.taskName} {self.timeStr}"
86 |
--------------------------------------------------------------------------------
/editor/src/tsk_generate.py:
--------------------------------------------------------------------------------
1 | import os, copy, shutil, subprocess, json
2 | from const import Path
3 | from l10n import L10n
4 | from log import Log
5 | from tsk_command_prompt import CommandPromptTask
6 | from prompt_travel import PromptTravel
7 | from serializer import Serializer
8 |
9 |
10 | class GenerateTask(CommandPromptTask):
11 | @classmethod
12 | def generate(cls, model):
13 | gen = model.generate
14 | config = PromptTravel.getConfig(
15 | gen, gen.model, gen.upscaleStrength, model.prompt.data, 10
16 | )
17 | configSuffix = PromptTravel.GetConfigFileSuffix(
18 | gen.length,
19 | gen.context,
20 | gen.width,
21 | gen.height,
22 | gen.useHalfVae,
23 | gen.useXFormers,
24 | gen.upscaleUseHalfVae,
25 | gen.upscaleUseXFormers,
26 | gen.upscale1Enabled,
27 | gen.upscale1Mode,
28 | gen.getUpscale1Height(),
29 | gen.upscale2Enabled,
30 | gen.upscale2Mode,
31 | gen.getUpscale2Height(),
32 | )
33 | configSuffix = configSuffix + "-D3"
34 | data = Serializer.serialize(model)
35 | task = GenerateTask(model.editor, "generate_anime", config, configSuffix, data)
36 | cls.enqueue(task)
37 |
38 | @classmethod
39 | def seedGacha(cls, model):
40 | gen = model.generate
41 | config = PromptTravel.getConfig(
42 | gen, gen.model, gen.upscaleStrength, model.prompt.data, 10
43 | )
44 | configSuffix = PromptTravel.GetConfigFileSuffix(
45 | gen.length,
46 | gen.context,
47 | gen.width,
48 | gen.height,
49 | gen.useHalfVae,
50 | gen.useXFormers,
51 | gen.upscaleUseHalfVae,
52 | gen.upscaleUseXFormers,
53 | gen.upscale1Enabled,
54 | gen.upscale1Mode,
55 | (int)(gen.height * 1.5),
56 | )
57 | data = Serializer.serialize(model)
58 | task = GenerateTask(model.editor, "seed_gacha", config, configSuffix, data)
59 | cls.enqueue(task)
60 |
61 | @classmethod
62 | def preview(cls, model):
63 | halfFps = False # Not good
64 |
65 | edt = model.editor
66 | start = edt.previewStart
67 | length = model.getPreviewLength()
68 | end = model.getPreviewEnd()
69 | promptData = copy.deepcopy(model.prompt.data)
70 | promptMap = promptData["prompt_map"]
71 |
72 | removeKeys = []
73 | for keyFrame in promptMap.keys():
74 | if keyFrame < start or keyFrame > end:
75 | removeKeys.append(keyFrame)
76 | for keyFrame in removeKeys:
77 | del promptMap[keyFrame]
78 |
79 | for keyFrame in sorted(promptMap.keys()):
80 | prompt = promptMap.pop(keyFrame)
81 | frame = keyFrame - start
82 | frame = (int)(frame * 0.5) if halfFps else frame
83 | promptMap[frame] = prompt
84 |
85 | gen = model.generate
86 | config = PromptTravel.getConfig(
87 | gen, gen.model, gen.upscaleStrength, promptData, 5 if halfFps else 10
88 | )
89 | configSuffix = PromptTravel.GetConfigFileSuffix(
90 | (int)(length * 0.5) if halfFps else length,
91 | (int)(gen.context * 0.5)
92 | if halfFps
93 | else gen.context, # halfFps & gen.context Not good
94 | gen.width,
95 | gen.height,
96 | gen.useHalfVae,
97 | gen.useXFormers,
98 | gen.upscaleUseHalfVae,
99 | gen.upscaleUseXFormers,
100 | gen.upscale1Enabled and edt.previewUpscale,
101 | gen.upscale1Mode,
102 | (int)(gen.height * 1.5),
103 | )
104 | configSuffix = (configSuffix + "-U5") if halfFps else configSuffix
105 | data = Serializer.serialize(model)
106 | task = GenerateTask(model.editor, "preview", config, configSuffix, data)
107 | cls.enqueue(task)
108 |
109 | # コンストラクタ
110 | def __init__(self, editor, taskMode, config, configSuffix, data):
111 | super().__init__(editor, taskMode)
112 | self.config = config
113 | self.configSuffix = configSuffix
114 | self.data = data
115 |
116 | def resetState(self):
117 | super().resetState()
118 | self.configName = ""
119 | self.configPath = ""
120 |
121 | def createCommand(self):
122 | self.configName = f"{self.timeStr}{self.configSuffix}.json"
123 | self.configPath = os.path.join(self.tempDir, self.configName)
124 | with open(self.configPath, "w", encoding="utf-8") as f:
125 | f.write(self.config)
126 | return f"Generate.bat {self.configPath}"
127 |
128 | def postProcess(self):
129 | latestTime = 0
130 | latestMp4FileName = ""
131 | latestMp4Path = ""
132 |
133 | for root, dirs, files in os.walk(self.tempDir):
134 | for file in files:
135 | if file.endswith(".mp4"):
136 | path = os.path.join(root, file)
137 | time = os.path.getmtime(path)
138 | if time > latestTime:
139 | latestTime = time
140 | latestMp4FileName = file
141 | latestMp4Path = path
142 |
143 | outputDir = os.path.join(Path.output, Path.getYYYYMMDD())
144 | os.makedirs(outputDir, exist_ok=True)
145 | outputPath = os.path.join(outputDir, latestMp4FileName)
146 | shutil.copy(latestMp4Path, outputPath)
147 |
148 | basePath, _ = os.path.splitext(outputPath)
149 | with open(basePath + ".json", "w", encoding="utf-8-sig") as f:
150 | json.dump(self.data, f, indent=4)
151 |
152 | seed = ""
153 | tokens = latestMp4FileName.split("-", 2)
154 | if len(tokens) == 3:
155 | seed = tokens[1]
156 | seed = seed.split("_", 1)[0]
157 |
158 | subprocess.Popen(f"{self.editor.ffplayCmd} {outputPath}", shell=True)
159 | Log.user(L10n.format("log_ffplay", seed))
160 |
--------------------------------------------------------------------------------
/editor/src/tsk_upscale.py:
--------------------------------------------------------------------------------
1 | import os, glob, shutil, subprocess, json
2 | from const import Const, Path
3 | from l10n import L10n
4 | from log import Log
5 | from tsk_command_prompt import CommandPromptTask
6 | from prompt_travel import PromptTravel
7 | from serializer import Serializer
8 |
9 |
10 | class UpscaleTask(CommandPromptTask):
11 | @classmethod
12 | def upscaleWithConfigOverride(cls, model, upscaleDir):
13 | gen = model.generate
14 | config = PromptTravel.getConfig(
15 | gen, gen.model, gen.upscaleStrength, model.prompt.data, 10
16 | )
17 | cls.upscale(model, upscaleDir, config)
18 |
19 | @classmethod
20 | def upscale(cls, model, upscaleDir, config=None):
21 | gen = model.generate
22 | data = Serializer.serialize(model)
23 | task = UpscaleTask(
24 | model.editor,
25 | "upscale",
26 | upscaleDir,
27 | gen.upscale1Mode,
28 | gen.getUpscale1Height(),
29 | gen.context,
30 | gen.upscaleUseHalfVae,
31 | gen.upscaleUseXFormers,
32 | config,
33 | data,
34 | )
35 | cls.enqueue(task)
36 |
37 | def __init__(
38 | self,
39 | editor,
40 | taskMode,
41 | upscaleDir,
42 | mode,
43 | height,
44 | context,
45 | useHalfVae,
46 | useXFormers,
47 | config,
48 | data,
49 | ):
50 | super().__init__(editor, taskMode)
51 | self.upscaleDir = upscaleDir
52 | self.mode = mode
53 | self.height = height
54 | self.context = context
55 | self.useHalfVae = useHalfVae
56 | self.useXFormers = useXFormers
57 | self.config = config
58 | self.data = data
59 |
60 | def resetState(self):
61 | super().resetState()
62 | self.configName = ""
63 | self.configPath = ""
64 |
65 | def createCommand(self):
66 | cmd = os.path.join("editor", "bat", self.mode + ".bat")
67 | cmd = f"{cmd} -H {self.height}"
68 | if self.mode == Const.refine:
69 | cmd += f" -C {(int)(self.context / 2)}"
70 | if self.useHalfVae:
71 | cmd += " --half-vae"
72 | if self.useXFormers:
73 | cmd += " --xformers"
74 |
75 | if self.config is not None:
76 | self.configName = f"{self.timeStr}.json"
77 | configDir = os.path.abspath(os.path.join(self.upscaleDir, os.pardir))
78 | self.configPath = os.path.join(configDir, self.configName)
79 | with open(self.configPath, "w", encoding="utf-8") as f:
80 | f.write(self.config)
81 | cmd += f" -c {self.configPath}" # dont ""
82 | cmd += f" {self.upscaleDir}" # dont ""
83 | return cmd
84 |
85 | def postProcess(self):
86 | outputDir = ""
87 | if self.mode == Const.tile:
88 | outputDir = self.findNewDir(Path.promptTravelUpscaled)
89 | elif self.mode == Const.refine:
90 | outputDir = self.findNewDir(Path.promptTravelRefined)
91 | if outputDir is not None:
92 | outputDir = self.findNewDir(outputDir)
93 | if outputDir is None:
94 | return
95 | mp4Paths = glob.glob(os.path.join(outputDir, "*.mp4"))
96 | if len(mp4Paths) == 0:
97 | return
98 | mp4Path = max(mp4Paths, key=os.path.getmtime)
99 | mp4Name = os.path.basename(mp4Path)
100 |
101 | outputDir = os.path.join(Path.output, Path.getYYYYMMDD())
102 | os.makedirs(outputDir, exist_ok=True)
103 | outputPath = os.path.join(outputDir, mp4Name)
104 | shutil.copy(mp4Path, outputPath)
105 |
106 | basePath, _ = os.path.splitext(outputPath)
107 | with open(basePath + ".json", "w", encoding="utf-8-sig") as f:
108 | json.dump(self.data, f, indent=4)
109 |
110 | seed = ""
111 | tokens = mp4Name.split(r"[_-]", 2)
112 | if len(tokens) == 3:
113 | seed = tokens[1]
114 |
115 | subprocess.Popen(f"{self.editor.ffplayCmd} {outputPath}", shell=True)
116 | Log.user(L10n.format("log_ffplay", seed))
117 |
118 | def findNewDir(self, dirPath):
119 | subDirs = [
120 | os.path.join(dirPath, name)
121 | for name in os.listdir(dirPath)
122 | if os.path.isdir(os.path.join(dirPath, name))
123 | ]
124 | newDir = max(subDirs, key=lambda x: os.path.getmtime(x), default=None) # None
125 | return newDir
126 |
--------------------------------------------------------------------------------
/editor/src/ui.py:
--------------------------------------------------------------------------------
1 | from config import Config
2 | from l10n import L10n
3 | from ui_menu import Menu
4 | from ui_input import InputForm
5 | from ui_preview import PreviewForm
6 | from ui_basic import BasicForm
7 | from ui_generate import GenerateForm
8 | from ui_control_net import ControlNetForm
9 | from ui_ip_adapter import IpAdapterForm
10 | from ui_upscale import UpscaleForm
11 | from ui_mosaic import MosaicForm
12 | from ui_output import OutputForm
13 | import tkinter as tk
14 | import tkinter.ttk as ttk
15 |
16 | # from tkinterdnd2 import DND_FILES, TkinterDnD
17 | import darkdetect
18 |
19 |
20 | class Form:
21 | winMinW = 860
22 | winMinH = 750
23 | winInitW = 900
24 | winInitH = 900
25 | winInitX = "None"
26 | winInitY = "40"
27 |
28 | ntbFuncInitH = 400
29 | ntbFuncMinH = 400
30 |
31 | def __init__(self):
32 | self.win = tk.Tk()
33 | self.win.title(f'{L10n.get("title")}')
34 | self.win.minsize(Form.winMinW, Form.winMinH)
35 |
36 | winGeom = f"{Form.winInitW}x{Form.winInitH}"
37 | if Form.winInitX != "None":
38 | winGeom += f"+{Form.winInitX}+{Form.winInitY}"
39 | self.win.geometry(winGeom)
40 |
41 | self.menu = Menu(self.win)
42 |
43 | self.paneWin = tk.PanedWindow(self.win, orient=tk.VERTICAL, sashpad=2)
44 | self.input = InputForm(self.paneWin)
45 |
46 | self.ntbFunc = ttk.Notebook(self.paneWin)
47 | self.preview = PreviewForm(self.ntbFunc)
48 | self.basic = BasicForm(self.ntbFunc)
49 | self.generate = GenerateForm(self.ntbFunc)
50 | self.controlNet = ControlNetForm(self.ntbFunc)
51 | self.ipAdapter = IpAdapterForm(self.ntbFunc)
52 | self.upscale = UpscaleForm(self.ntbFunc)
53 | self.mosaic = MosaicForm(self.ntbFunc)
54 | self.paneWin.add(
55 | self.ntbFunc,
56 | height=self.ntbFuncH,
57 | minsize=Form.ntbFuncMinH,
58 | stretch="never",
59 | )
60 | self.ntbFunc.select(1) # Default 1
61 |
62 | self.output = OutputForm(self.paneWin)
63 | self.paneWin.pack(fill=tk.BOTH, expand=True)
64 |
65 | if Form.invOsCol ^ darkdetect.isDark():
66 | self.setDarkTheme()
67 |
68 | @classmethod
69 | def loadConfig(cls):
70 | Form.winInitW = Config.get("ui_size", "win_width", Form.winInitW)
71 | Form.winInitH = Config.get("ui_size", "win_height", Form.winInitH)
72 | Form.winInitX = Config.get("ui_size", "win_x", Form.winInitX)
73 | Form.winInitY = Config.get("ui_size", "win_y", Form.winInitY)
74 |
75 | InputForm.loadConfig()
76 | Form.ntbFuncH = Config.get("ui_size", "func_h", Form.ntbFuncInitH)
77 | BasicForm.loadConfig()
78 | GenerateForm.loadConfig()
79 | UpscaleForm.loadConfig()
80 | OutputForm.loadConfig()
81 |
82 | Form.darkColFg = Config.get("ui_color", "dark_col_fg", "#CCCCCC")
83 | Form.darkColFgSel = Config.get("ui_color", "dark_col_fg_sel", "#FFFFFF")
84 | Form.darkColBg = Config.get("ui_color", "dark_col_bg", "#222222")
85 | Form.darkColBgSel = Config.get("ui_color", "dark_col_bg_sel", "#555555")
86 | Form.invOsCol = Config.getBool("ui_color", "inv_os_col", False)
87 |
88 | def storeConfig(self):
89 | Config.set("ui_size", "win_width", self.win.winfo_width())
90 | Config.set("ui_size", "win_height", self.win.winfo_height())
91 | Config.set("ui_size", "win_x", self.win.winfo_x())
92 | Config.set("ui_size", "win_y", self.win.winfo_y())
93 |
94 | self.input.storeConfig()
95 | func_h = self.ntbFunc.winfo_height()
96 | if func_h != 1:
97 | Config.set("ui_size", "func_h", func_h)
98 | self.basic.storeConfig()
99 | self.generate.storeConfig()
100 | self.output.storeConfig()
101 |
102 | Config.set("ui_color", "dark_col_fg", Form.darkColFg)
103 | Config.set("ui_color", "dark_col_fg_sel", Form.darkColFgSel)
104 | Config.set("ui_color", "dark_col_bg", Form.darkColBg)
105 | Config.set("ui_color", "dark_col_bg_sel", Form.darkColBgSel)
106 | Config.set("ui_color", "inv_os_col", Form.invOsCol)
107 |
108 | def setDarkTheme(self):
109 | colFgBg = {"fg": Form.darkColFg, "bg": Form.darkColBg}
110 | colSel = colFgBg.copy()
111 | colSel["selectforeground"] = Form.darkColFgSel
112 | colSel["selectbackground"] = Form.darkColBgSel
113 | colSelIns = colSel.copy()
114 | colSelIns["insertbackground"] = Form.darkColFgSel
115 | colSelHigh = colSel.copy()
116 | colSelHigh["highlightbackground"] = Form.darkColBgSel
117 |
118 | colAct = colFgBg.copy()
119 | colAct["activeforeground"] = Form.darkColFgSel
120 | colAct["activebackground"] = Form.darkColBgSel
121 | colActChk = colAct.copy()
122 | colActChk["selectcolor"] = Form.darkColBgSel
123 |
124 | sli = colFgBg.copy()
125 | sli["activebackground"] = Form.darkColBgSel
126 | sli["troughcolor"] = Form.darkColBg
127 | sli["highlightbackground"] = Form.darkColBg
128 |
129 | colors = {
130 | "fg": Form.darkColFg,
131 | "bg": Form.darkColBg,
132 | "fgSel": Form.darkColFgSel,
133 | "bgSel": Form.darkColBgSel,
134 | "fgBg": colFgBg,
135 | "sel": colSel,
136 | "selIns": colSelIns,
137 | "act": colAct,
138 | "actChk": colActChk,
139 | "txt": colSelIns,
140 | "chk": colActChk,
141 | "ent": colSelIns,
142 | "sli": sli,
143 | "lst": colSelHigh,
144 | }
145 |
146 | style = ttk.Style()
147 | style.theme_use("default")
148 | style.configure("TFrame", background=colors["bg"])
149 | style.configure("TLabel", background=colors["bg"], foreground=colors["fg"])
150 |
151 | style.configure(
152 | "TButton",
153 | foreground=colors["fg"],
154 | background=colors["bg"],
155 | )
156 | style.map(
157 | "TButton",
158 | foreground=[("active", colors["fgSel"])],
159 | background=[("active", colors["bgSel"])],
160 | )
161 |
162 | style.configure(
163 | "TCombobox",
164 | foreground=colors["fg"],
165 | )
166 | style.map(
167 | "TCombobox",
168 | fieldbackground=[("readonly", colors["bg"])],
169 | selectbackground=[
170 | ("readonly", colors["bg"]),
171 | ("readonly", "!focus", colors["bg"]),
172 | ],
173 | )
174 |
175 | style.configure("TNotebook", background=colors["bg"])
176 | style.configure("TNotebook.Tab", padding=[6, 8])
177 | style.map(
178 | "TNotebook.Tab",
179 | foreground=[("", colors["fg"]), ("selected", colors["fgSel"])],
180 | background=[("", colors["bg"]), ("selected", colors["bgSel"])],
181 | )
182 |
183 | self.input.setDarkTheme(colors)
184 | self.preview.setDarkTheme(colors)
185 | self.basic.setDarkTheme(colors)
186 | self.generate.setDarkTheme(colors)
187 | self.controlNet.setDarkTheme(colors)
188 | self.ipAdapter.setDarkTheme(colors)
189 | self.upscale.setDarkTheme(colors)
190 | self.mosaic.setDarkTheme(colors)
191 | self.output.setDarkTheme(colors)
192 |
193 | def run(self):
194 | self.win.lift()
195 | self.win.mainloop()
196 |
--------------------------------------------------------------------------------
/editor/src/ui_basic.py:
--------------------------------------------------------------------------------
1 | from const import Const, Path
2 | from l10n import L10n
3 | from config import Config
4 | from ui_const import UiPack
5 | import tkinter as tk
6 | import tkinter.ttk as ttk
7 | import os, glob
8 |
9 |
10 | class BasicForm:
11 | maxLength = 600
12 | sizeResolution = 64
13 |
14 | def __init__(self, parent):
15 | self.frm = ttk.Frame(parent)
16 |
17 | self.initLengthSeed()
18 | self.initModelVae()
19 | self.initSize()
20 | self.initUpsdcale1()
21 | self.initUpsdcale2()
22 |
23 | parent.add(self.frm, text=L10n.get("basic_tab"))
24 |
25 | def initLengthSeed(self):
26 | self.frmLengthSeed = ttk.Frame(self.frm)
27 |
28 | self.varLength = tk.IntVar(value=10)
29 | self.lblLength = ttk.Label(self.frmLengthSeed, text=L10n.get("generate_length"))
30 | self.varLength.trace_add("write", self.updateLength)
31 | self.lblLength.pack(UiPack.lbl)
32 | self.sliLength = tk.Scale(
33 | self.frmLengthSeed,
34 | from_=10,
35 | to=BasicForm.maxLength,
36 | resolution=10,
37 | orient=tk.HORIZONTAL,
38 | variable=self.varLength,
39 | takefocus=True,
40 | )
41 | self.sliLength.pack(UiPack.sli)
42 |
43 | self.lblSeed = ttk.Label(self.frmLengthSeed, text=L10n.get("generate_seed"))
44 | self.lblSeed.pack(UiPack.lbl)
45 |
46 | self.varSeed = tk.StringVar(value="-1")
47 | self.entSeed = tk.Entry(self.frmLengthSeed, textvariable=self.varSeed, width=20)
48 | self.entSeed.pack(UiPack.ent)
49 | self.btnRandomSeed = ttk.Button(
50 | self.frmLengthSeed,
51 | text="🎲",
52 | command=lambda: self.varSeed.set("-1"),
53 | )
54 | self.btnRandomSeed.pack(UiPack.btn)
55 |
56 | self.frmLengthSeed.pack(UiPack.frm)
57 |
58 | def updateLength(self, *args):
59 | self.lblLength.configure(
60 | text=L10n.get("generate_length").format(int(self.varLength.get() / 10))
61 | )
62 |
63 | def initModelVae(self):
64 | self.frmModelVae = ttk.Frame(self.frm)
65 | self.lblModel = ttk.Label(self.frmModelVae, text=L10n.get("generate_model"))
66 | self.lblModel.pack(UiPack.lbl)
67 |
68 | self.varModel = tk.StringVar()
69 | self.cmbModel = ttk.Combobox(
70 | self.frmModelVae,
71 | textvariable=self.varModel,
72 | state="readonly",
73 | postcommand=self.listModeValues,
74 | width=40,
75 | height=23,
76 | )
77 | self.cmbModel.pack(UiPack.cmb, fill=tk.X, expand=True)
78 | self.listModeValues()
79 |
80 | self.lblVae = ttk.Label(self.frmModelVae, text=L10n.get("generate_vae"))
81 | self.lblVae.pack(UiPack.lbl)
82 |
83 | self.varVae = tk.StringVar()
84 | self.cmbVae = ttk.Combobox(
85 | self.frmModelVae,
86 | textvariable=self.varVae,
87 | state="readonly",
88 | postcommand=self.listVaeValues,
89 | width=30,
90 | height=23,
91 | )
92 | self.cmbVae.pack(UiPack.cmb)
93 | self.listVaeValues()
94 |
95 | self.frmModelVae.pack(UiPack.frm)
96 |
97 | def listModeValues(self):
98 | baseQuery = os.path.join(Path.model, "**", "*.")
99 | models = glob.glob(baseQuery + "safetensors", recursive=True)
100 | models += glob.glob(baseQuery + "ckpt", recursive=True)
101 | pfxLen = len(Path.model) + 1
102 | models = [model[pfxLen:] for model in models]
103 | self.cmbModel.configure(values=models)
104 |
105 | def listVaeValues(self):
106 | baseQuery = os.path.join(Path.vae, "**", "*.")
107 | vaes = glob.glob(baseQuery + "safetensors", recursive=True)
108 | vaes += glob.glob(baseQuery + "pt", recursive=True)
109 | vaes += glob.glob(baseQuery + "ckpt", recursive=True)
110 | pfxLen = len(Path.vae) + 1
111 | vaes = [vae[pfxLen:] for vae in vaes]
112 | self.cmbVae.configure(values=vaes)
113 |
114 | def initSize(self):
115 | self.frmSize = ttk.Frame(self.frm)
116 |
117 | self.varWidth = tk.IntVar(value=384)
118 | self.varWidth.trace_add("write", self.updateSize)
119 | self.lblWidth = ttk.Label(self.frmSize, text=L10n.get("generate_width"))
120 | self.lblWidth.pack(UiPack.lbl)
121 | self.sliWidth = tk.Scale(
122 | self.frmSize,
123 | from_=256,
124 | to=1280,
125 | resolution=BasicForm.sizeResolution,
126 | orient=tk.HORIZONTAL,
127 | variable=self.varWidth,
128 | takefocus=True,
129 | )
130 | self.sliWidth.pack(UiPack.sli)
131 |
132 | self.varHeight = tk.IntVar(value=512)
133 | self.varHeight.trace_add("write", self.updateSize)
134 | self.lblHeight = ttk.Label(self.frmSize, text=L10n.get("generate_height"))
135 | self.lblHeight.pack(UiPack.lbl)
136 | self.sliHeight = tk.Scale(
137 | self.frmSize,
138 | from_=256,
139 | to=1280,
140 | resolution=BasicForm.sizeResolution,
141 | orient=tk.HORIZONTAL,
142 | variable=self.varHeight,
143 | takefocus=True,
144 | )
145 |
146 | self.sliHeight.pack(UiPack.sli)
147 |
148 | self.btnSwap = ttk.Button(
149 | self.frmSize, text=L10n.get("swap"), command=self.swapSize
150 | )
151 | self.btnSwap.pack(UiPack.btn)
152 |
153 | self.frmSize.pack(UiPack.frm)
154 |
155 | def swapSize(self):
156 | width = self.varWidth.get()
157 | self.varWidth.set(self.varHeight.get())
158 | self.varHeight.set(width)
159 |
160 | def initUpsdcale1(self):
161 | self.frmUpscale1 = ttk.Frame(self.frm)
162 | self.varUpscale1Enabled = tk.BooleanVar(value=True)
163 | self.chkUpscale1Enabled = tk.Checkbutton(
164 | self.frmUpscale1,
165 | text=L10n.get("upscale1"),
166 | variable=self.varUpscale1Enabled,
167 | )
168 | self.chkUpscale1Enabled.pack(UiPack.chk)
169 |
170 | self.varUpscale1Mode = tk.StringVar()
171 | self.cmbUpscale1Mode = ttk.Combobox(
172 | self.frmUpscale1,
173 | values=(
174 | Const.tile,
175 | Const.refine,
176 | ),
177 | textvariable=self.varUpscale1Mode,
178 | state="readonly",
179 | width=11,
180 | )
181 | self.cmbUpscale1Mode.current(1)
182 | self.cmbUpscale1Mode.pack(UiPack.cmb)
183 |
184 | self.lblUpscale1Scale = ttk.Label(
185 | self.frmUpscale1, text=L10n.get("upscale_scale")
186 | )
187 | self.lblUpscale1Scale.pack(UiPack.lbl)
188 | self.varUpscale1Scale = tk.DoubleVar(value=2.0)
189 | self.varUpscale1Scale.trace_add("write", self.updateSize)
190 | self.sliUpscale1Scale = tk.Scale(
191 | self.frmUpscale1,
192 | from_=1.0,
193 | to=4.0,
194 | resolution=0.5,
195 | orient=tk.HORIZONTAL,
196 | variable=self.varUpscale1Scale,
197 | takefocus=True,
198 | )
199 | self.sliUpscale1Scale.pack(UiPack.sli)
200 |
201 | self.frmUpscale1.pack(UiPack.frm)
202 |
203 | def initUpsdcale2(self):
204 | self.frmUpscale2 = ttk.Frame(self.frm)
205 | self.varUpscale2Enabled = tk.BooleanVar(value=True)
206 | self.chkUpscale2Enabled = tk.Checkbutton(
207 | self.frmUpscale2,
208 | text=L10n.get("upscale2"),
209 | variable=self.varUpscale2Enabled,
210 | )
211 | self.chkUpscale2Enabled.pack(UiPack.chk)
212 |
213 | self.varUpscale2Mode = tk.StringVar()
214 | self.cmbUpscale2Mode = ttk.Combobox(
215 | self.frmUpscale2,
216 | values=(
217 | Const.tile,
218 | Const.refine,
219 | ),
220 | textvariable=self.varUpscale2Mode,
221 | state="readonly",
222 | width=11,
223 | )
224 | self.cmbUpscale2Mode.current(0)
225 | self.cmbUpscale2Mode.pack(UiPack.cmb)
226 |
227 | self.lblUpscale2Scale = ttk.Label(
228 | self.frmUpscale2, text=L10n.get("upscale_scale")
229 | )
230 | self.lblUpscale2Scale.pack(UiPack.lbl)
231 | self.varUpscale2Scale = tk.DoubleVar(value=1.5)
232 | self.varUpscale2Scale.trace_add("write", self.updateSize)
233 | self.sliUpscale2Scale = tk.Scale(
234 | self.frmUpscale2,
235 | from_=1.0,
236 | to=4.0,
237 | resolution=0.5,
238 | orient=tk.HORIZONTAL,
239 | variable=self.varUpscale2Scale,
240 | takefocus=True,
241 | )
242 | self.sliUpscale2Scale.pack(UiPack.sli)
243 |
244 | self.frmUpscale2.pack(UiPack.frm)
245 |
246 | def updateSize(self, *args):
247 | width = self.varWidth.get()
248 | height = self.varHeight.get()
249 | upscale1 = self.varUpscale1Scale.get()
250 | upscale2 = upscale1 * self.varUpscale2Scale.get()
251 |
252 | self.chkUpscale1Enabled.configure(
253 | text=f"{L10n.get('upscale1')}\n{int(width * upscale1):>4} x {int(height * upscale1):>4}"
254 | )
255 | self.chkUpscale2Enabled.configure(
256 | text=f"{L10n.get('upscale2')}\n{int(width * upscale2):>4} x {int(height * upscale2):>4}"
257 | )
258 |
259 | @classmethod
260 | def loadConfig(self):
261 | BasicForm.maxLength = Config.get("ui", "max_length", BasicForm.maxLength)
262 | BasicForm.sizeResolution = Config.get(
263 | "ui", "size_resolution", BasicForm.sizeResolution
264 | )
265 |
266 | def storeConfig(self):
267 | Config.set("ui", "max_length", BasicForm.maxLength)
268 | Config.set("ui", "size_resolution", BasicForm.sizeResolution)
269 |
270 | def setDarkTheme(self, colors):
271 | self.sliLength.configure(colors["sli"])
272 | self.entSeed.configure(colors["ent"])
273 | self.sliWidth.configure(colors["sli"])
274 | self.sliHeight.configure(colors["sli"])
275 | self.chkUpscale1Enabled.configure(colors["chk"])
276 | self.sliUpscale1Scale.configure(colors["sli"])
277 | self.chkUpscale2Enabled.configure(colors["chk"])
278 | self.sliUpscale2Scale.configure(colors["sli"])
279 |
--------------------------------------------------------------------------------
/editor/src/ui_const.py:
--------------------------------------------------------------------------------
1 | import tkinter as tk
2 |
3 |
4 | class Ui:
5 | padX = 4
6 | padY = 2
7 | cfgTxtArea = {"spacing1": 4, "spacing2": 4, "spacing3": 4, "wrap": tk.WORD}
8 |
9 |
10 | class UiPack:
11 | txt = {"side": tk.LEFT, "fill": tk.BOTH, "expand": True}
12 | frm = {
13 | "padx": Ui.padX,
14 | "pady": Ui.padY,
15 | "fill": tk.X,
16 | }
17 | sli = {
18 | "padx": Ui.padX,
19 | "pady": Ui.padY,
20 | "side": tk.LEFT,
21 | "fill": tk.X,
22 | "expand": True,
23 | }
24 | sep = {"padx": Ui.padX, "pady": Ui.padY, "fill": tk.X}
25 | lbl = {"padx": Ui.padX, "pady": Ui.padY, "side": tk.LEFT}
26 | btn = {"padx": Ui.padX, "pady": Ui.padY, "side": tk.LEFT}
27 | chk = {"padx": Ui.padX, "pady": Ui.padY, "side": tk.LEFT}
28 | cmb = {"padx": Ui.padX, "pady": Ui.padY, "side": tk.LEFT}
29 | ent = {"padx": Ui.padX, "pady": Ui.padY, "side": tk.LEFT}
30 |
--------------------------------------------------------------------------------
/editor/src/ui_control_net.py:
--------------------------------------------------------------------------------
1 | import os, subprocess
2 | import tkinter as tk
3 | import tkinter.ttk as ttk
4 | from const import Path, ControlNetType
5 | from l10n import L10n
6 | from config import Config
7 | from ui_const import UiPack
8 |
9 |
10 | class ControlNetForm:
11 | def __init__(self, parent):
12 | self.frm = ttk.Frame(parent)
13 |
14 | self.initControlNetDirLoop()
15 | self.initControlNetInfo()
16 | self.initImport()
17 |
18 | self.ntb = ttk.Notebook(self.frm)
19 | self.ntb.pack(fill=tk.BOTH, expand=True)
20 |
21 | for cnType in ControlNetType:
22 | self.initControlNetTab(self.ntb, cnType)
23 |
24 | parent.add(self.frm, text=L10n.get("control_net_tab"))
25 |
26 | def initControlNetDirLoop(self):
27 | self.frmControlNetDirLoop = ttk.Frame(self.frm)
28 |
29 | self.lblControlNetDir = ttk.Label(
30 | self.frmControlNetDirLoop, text=L10n.get("control_net_dir")
31 | )
32 | self.lblControlNetDir.pack(UiPack.lbl)
33 |
34 | self.varControlNetDir = tk.StringVar()
35 | self.cmbControlNetDir = ttk.Combobox(
36 | self.frmControlNetDirLoop,
37 | textvariable=self.varControlNetDir,
38 | state="readonly",
39 | postcommand=self.listControlNetDirValues,
40 | width=20,
41 | height=20,
42 | )
43 | self.cmbControlNetDir.pack(UiPack.cmb, fill=tk.X, expand=True)
44 |
45 | self.btnControlNetDirExplorer = ttk.Button(
46 | self.frmControlNetDirLoop,
47 | text=L10n.get("ip_adapter_image_dir_explorer"),
48 | command=lambda: subprocess.run(["explorer", Path.controlNet]),
49 | )
50 | self.btnControlNetDirExplorer.pack(UiPack.btn)
51 |
52 | self.varControlNetLoop = tk.BooleanVar(value=True)
53 | self.chkControlNetLoop = tk.Checkbutton(
54 | self.frmControlNetDirLoop,
55 | text=L10n.get("control_net_loop"),
56 | variable=self.varControlNetLoop,
57 | )
58 | self.chkControlNetLoop.pack(UiPack.chk)
59 |
60 | self.frmControlNetDirLoop.pack(UiPack.frm)
61 |
62 | def listControlNetDirValues(self):
63 | subDirs = [f.name for f in os.scandir(Path.controlNet) if f.is_dir()]
64 | self.cmbControlNetDir.configure(values=subDirs)
65 |
66 | def initControlNetInfo(self):
67 | self.frmInfo = ttk.Frame(self.frm)
68 |
69 | self.lblControlNetInfo = ttk.Label(
70 | self.frmInfo, text=L10n.get("control_net_info")
71 | )
72 | self.lblControlNetInfo.pack(UiPack.lbl)
73 |
74 | self.frmInfo.pack(UiPack.frm)
75 |
76 | def initImport(self):
77 | self.frmImport = ttk.Frame(self.frm)
78 |
79 | self.btnImport = ttk.Button(
80 | self.frmImport,
81 | text=L10n.get("control_net_import"),
82 | )
83 | self.btnImport.pack(UiPack.btn)
84 |
85 | self.lblImportSpeed = ttk.Label(
86 | self.frmImport, text=L10n.get("control_net_import_speed")
87 | )
88 | self.lblImportSpeed.pack(UiPack.lbl)
89 | self.varImportSpeed = tk.StringVar(value="1")
90 | self.entImportSpeed = tk.Entry(
91 | self.frmImport, textvariable=self.varImportSpeed, width=4
92 | )
93 | self.entImportSpeed.pack(UiPack.ent)
94 |
95 | self.lblImportStart = ttk.Label(
96 | self.frmImport, text=L10n.get("control_net_import_start")
97 | )
98 | self.lblImportStart.pack(UiPack.lbl)
99 | self.varImportStart = tk.StringVar(value="")
100 | self.entImportStart = tk.Entry(
101 | self.frmImport, textvariable=self.varImportStart, width=11
102 | )
103 | self.entImportStart.pack(UiPack.ent)
104 |
105 | self.lblImportStart = ttk.Label(
106 | self.frmImport, text=L10n.get("control_net_import_length")
107 | )
108 | self.lblImportStart.pack(UiPack.lbl)
109 | self.varImportLength = tk.StringVar(value="")
110 | self.entImportLength = tk.Entry(
111 | self.frmImport, textvariable=self.varImportLength, width=11
112 | )
113 | self.entImportLength.pack(UiPack.ent)
114 |
115 | self.lblImportIndex = ttk.Label(
116 | self.frmImport, text=L10n.get("control_net_import_index")
117 | )
118 | self.lblImportIndex.pack(UiPack.lbl)
119 | self.varImportIndex = tk.StringVar(value="0")
120 | self.entImportIndex = tk.Entry(
121 | self.frmImport, textvariable=self.varImportIndex, width=4
122 | )
123 | self.entImportIndex.pack(UiPack.ent)
124 |
125 | self.frmImport.pack(UiPack.frm)
126 | pass
127 |
128 | def initControlNetTab(self, parent, cnType):
129 | name = cnType.name
130 | frm = ttk.Frame(parent)
131 | setattr(self, f"frm_{name}", frm)
132 |
133 | self.initControlNetFlags(frm, name)
134 | self.initControlNetScaleScaleList(frm, name)
135 | self.initControlNetStartEnd(frm, name)
136 |
137 | parent.add(frm, text=name)
138 |
139 | def initControlNetFlags(self, parent, name):
140 | frm = ttk.Frame(parent)
141 | setattr(self, f"frmFlags_{name}", frm)
142 |
143 | varEnable = tk.BooleanVar()
144 | setattr(self, f"varEnable_{name}", varEnable)
145 | chkEnable = tk.Checkbutton(
146 | frm,
147 | text=L10n.format("control_net_enable", name),
148 | variable=varEnable,
149 | )
150 | setattr(self, f"chkEnable_{name}", chkEnable)
151 | chkEnable.pack(UiPack.chk)
152 |
153 | btnOpenDir = ttk.Button(
154 | frm,
155 | text=L10n.format("control_net_open_dir", name),
156 | command=lambda: self.varControlNetDir.get() == ""
157 | or subprocess.run(
158 | [
159 | "explorer",
160 | os.path.join(
161 | Path.controlNet,
162 | self.varControlNetDir.get(),
163 | f"controlnet_{name}",
164 | ),
165 | ]
166 | ),
167 | )
168 | setattr(self, f"btnOpenDir_{name}", btnOpenDir)
169 | btnOpenDir.pack(UiPack.btn)
170 |
171 | varUsePreprocessor = tk.BooleanVar(value=True)
172 | setattr(self, f"varUsePreprocessor_{name}", varUsePreprocessor)
173 | chkPreprocessor = tk.Checkbutton(
174 | frm,
175 | text=L10n.get("control_net_preprocessor"),
176 | variable=varUsePreprocessor,
177 | )
178 | setattr(self, f"chkPreprocessor_{name}", chkPreprocessor)
179 | chkPreprocessor.pack(UiPack.chk)
180 |
181 | varGuessMode = tk.BooleanVar()
182 | setattr(self, f"varGuessMode_{name}", varGuessMode)
183 | chkGuessMode = tk.Checkbutton(
184 | frm,
185 | text=L10n.format("control_net_guess_mode", name),
186 | variable=varGuessMode,
187 | )
188 | setattr(self, f"chkGuessMode_{name}", chkGuessMode)
189 | chkGuessMode.pack(UiPack.chk)
190 |
191 | frm.pack(UiPack.frm)
192 |
193 | def initControlNetScaleScaleList(self, parent, name):
194 | frm = ttk.Frame(parent)
195 | setattr(self, f"frmScaleScaleList_{name}", frm)
196 |
197 | varScale = tk.DoubleVar(value=1.0)
198 | setattr(self, f"varScale_{name}", varScale)
199 | lblScale = ttk.Label(frm, text=L10n.format("control_net_scale", name))
200 | lblScale.pack(UiPack.lbl)
201 | sliScale = tk.Scale(
202 | frm,
203 | from_=0.0,
204 | to=2.0,
205 | resolution=0.05,
206 | orient=tk.HORIZONTAL,
207 | variable=varScale,
208 | takefocus=True,
209 | )
210 | setattr(self, f"sliScale_{name}", sliScale)
211 | sliScale.pack(UiPack.sli)
212 |
213 | varScaleList = tk.StringVar()
214 | setattr(self, f"varScaleList_{name}", varScaleList)
215 | lblScaleList = ttk.Label(frm, text=L10n.get("control_net_scale_list"))
216 | lblScaleList.pack(UiPack.lbl)
217 | entScaleList = tk.Entry(frm, textvariable=varScaleList)
218 | setattr(self, f"entScaleList_{name}", entScaleList)
219 | entScaleList.pack(UiPack.ent, fill=tk.X, expand=True)
220 |
221 | frm.pack(UiPack.frm)
222 |
223 | def initControlNetStartEnd(self, parent, name):
224 | frm = ttk.Frame(parent)
225 |
226 | varStart = tk.DoubleVar(value=0.0)
227 | setattr(self, f"varStart_{name}", varStart)
228 | lblStart = ttk.Label(frm, text=L10n.format("control_net_start", name))
229 | lblStart.pack(UiPack.lbl)
230 | sliStart = tk.Scale(
231 | frm,
232 | from_=0.0,
233 | to=1.0,
234 | resolution=0.05,
235 | orient=tk.HORIZONTAL,
236 | variable=varStart,
237 | takefocus=True,
238 | )
239 | setattr(self, f"sliStart_{name}", sliStart)
240 | sliStart.pack(UiPack.sli)
241 |
242 | varEnd = tk.DoubleVar(value=1.0)
243 | setattr(self, f"varEnd_{name}", varEnd)
244 | lblEnd = ttk.Label(frm, text=L10n.format("control_net_end", name))
245 | lblEnd.pack(UiPack.lbl)
246 | sliEnd = tk.Scale(
247 | frm,
248 | from_=0.0,
249 | to=1.0,
250 | resolution=0.05,
251 | orient=tk.HORIZONTAL,
252 | variable=varEnd,
253 | takefocus=True,
254 | )
255 | setattr(self, f"sliEnd_{name}", sliEnd)
256 | sliEnd.pack(UiPack.sli)
257 |
258 | frm.pack(UiPack.frm)
259 |
260 | def setDarkTheme(self, colors):
261 | self.chkControlNetLoop.configure(colors["chk"])
262 | self.entImportSpeed.configure(colors["ent"])
263 | self.entImportStart.configure(colors["ent"])
264 | self.entImportLength.configure(colors["ent"])
265 | self.entImportIndex.configure(colors["ent"])
266 | for cnType in ControlNetType:
267 | getattr(self, f"chkEnable_{cnType.name}").configure(colors["chk"])
268 | getattr(self, f"chkPreprocessor_{cnType.name}").configure(colors["chk"])
269 | getattr(self, f"chkGuessMode_{cnType.name}").configure(colors["chk"])
270 | getattr(self, f"sliScale_{cnType.name}").configure(colors["sli"])
271 | getattr(self, f"entScaleList_{cnType.name}").configure(colors["ent"])
272 | getattr(self, f"sliStart_{cnType.name}").configure(colors["sli"])
273 | getattr(self, f"sliEnd_{cnType.name}").configure(colors["sli"])
274 |
--------------------------------------------------------------------------------
/editor/src/ui_generate.py:
--------------------------------------------------------------------------------
1 | from const import Const, Path
2 | from l10n import L10n
3 | from config import Config
4 | from ui_const import UiPack
5 | import tkinter as tk
6 | import tkinter.ttk as ttk
7 | import os, glob
8 |
9 |
10 | class GenerateForm:
11 | schedulerValues = Const.schedulerValues
12 | schedulerNames = Const.schedulerNames
13 | maxSteps = 60
14 |
15 | def __init__(self, parent):
16 | self.frm = ttk.Frame(parent)
17 |
18 | self.initMotionModuleContext()
19 | self.initSchedulerSteps()
20 | self.initGuidanceScaleClipScale()
21 | self.initPromptFixedRatioHalfVaeXFormers()
22 |
23 | parent.add(self.frm, text=L10n.get("generate_tab"))
24 |
25 | def initMotionModuleContext(self):
26 | self.frmMotionModuleContext = ttk.Frame(self.frm)
27 |
28 | self.lblMotionModule = ttk.Label(
29 | self.frmMotionModuleContext, text=L10n.get("generate_motion_module")
30 | )
31 | self.lblMotionModule.pack(UiPack.lbl)
32 |
33 | self.varMotionModule = tk.StringVar()
34 | self.cmbMotionModule = ttk.Combobox(
35 | self.frmMotionModuleContext,
36 | textvariable=self.varMotionModule,
37 | state="readonly",
38 | postcommand=self.listMotionModuleValues,
39 | width=40,
40 | height=23,
41 | )
42 | self.cmbMotionModule.pack(UiPack.cmb)
43 | self.listMotionModuleValues()
44 |
45 | self.lblContext = ttk.Label(
46 | self.frmMotionModuleContext, text=L10n.get("generate_context")
47 | )
48 | self.lblContext.pack(UiPack.lbl)
49 | self.varContext = tk.IntVar(value=14)
50 | self.sliContext = tk.Scale(
51 | self.frmMotionModuleContext,
52 | from_=8,
53 | to=24,
54 | resolution=2,
55 | orient=tk.HORIZONTAL,
56 | variable=self.varContext,
57 | takefocus=True,
58 | )
59 | self.sliContext.pack(UiPack.sli)
60 |
61 | self.frmMotionModuleContext.pack(UiPack.frm)
62 |
63 | def listMotionModuleValues(self):
64 | baseQuery = os.path.join(Path.motionModule, "**", "*.")
65 | motionModules = glob.glob(baseQuery + "ckpt", recursive=True)
66 | motionModules += glob.glob(baseQuery + "safetensors", recursive=True)
67 | motionModules += glob.glob(baseQuery + "pth", recursive=True)
68 | pfxLen = len(Path.motionModule) + 1
69 | motionModules = [motionModule[pfxLen:] for motionModule in motionModules]
70 | self.cmbMotionModule.configure(values=motionModules)
71 |
72 | def initSchedulerSteps(self):
73 | self.frmSchedulerSteps = ttk.Frame(self.frm)
74 |
75 | self.lblScheduler = ttk.Label(
76 | self.frmSchedulerSteps, text=L10n.get("generate_scheduler")
77 | )
78 | self.lblScheduler.pack(UiPack.lbl)
79 |
80 | self.varScheduler = tk.StringVar()
81 | self.cmbScheduler = ttk.Combobox(
82 | self.frmSchedulerSteps,
83 | textvariable=self.varScheduler,
84 | state="readonly",
85 | values=GenerateForm.schedulerNames,
86 | width=21,
87 | height=23,
88 | )
89 | self.cmbScheduler.pack(UiPack.cmb)
90 |
91 | self.lblSteps = ttk.Label(
92 | self.frmSchedulerSteps, text=L10n.get("generate_steps")
93 | )
94 | self.lblSteps.pack(UiPack.lbl)
95 |
96 | self.varSteps = tk.IntVar(value=20)
97 | self.sliSteps = tk.Scale(
98 | self.frmSchedulerSteps,
99 | from_=1,
100 | to=GenerateForm.maxSteps,
101 | resolution=1,
102 | orient=tk.HORIZONTAL,
103 | variable=self.varSteps,
104 | takefocus=True,
105 | )
106 | self.sliSteps.pack(UiPack.sli)
107 |
108 | self.frmSchedulerSteps.pack(UiPack.frm)
109 |
110 | def initGuidanceScaleClipScale(self):
111 | self.frmClipScaleGuidanceScale = ttk.Frame(self.frm)
112 |
113 | self.lblGuidanceScale = ttk.Label(
114 | self.frmClipScaleGuidanceScale, text=L10n.get("generate_guidance_scale")
115 | )
116 | self.lblGuidanceScale.pack(UiPack.lbl)
117 |
118 | self.varGuidanceScale = tk.DoubleVar(value=8.0)
119 | self.sliGuidanceScale = tk.Scale(
120 | self.frmClipScaleGuidanceScale,
121 | from_=1.0,
122 | to=30.0,
123 | resolution=0.5,
124 | orient=tk.HORIZONTAL,
125 | variable=self.varGuidanceScale,
126 | takefocus=True,
127 | )
128 | self.sliGuidanceScale.pack(UiPack.sli)
129 |
130 | self.lblClipSkip = ttk.Label(
131 | self.frmClipScaleGuidanceScale, text=L10n.get("generate_clip_skip")
132 | )
133 | self.lblClipSkip.pack(UiPack.lbl)
134 |
135 | self.varClipSkip = tk.IntVar(value=2)
136 | self.sliClipSkip = tk.Scale(
137 | self.frmClipScaleGuidanceScale,
138 | from_=1,
139 | to=5,
140 | resolution=1,
141 | orient=tk.HORIZONTAL,
142 | variable=self.varClipSkip,
143 | takefocus=True,
144 | )
145 | self.sliClipSkip.pack(UiPack.sli)
146 |
147 | self.frmClipScaleGuidanceScale.pack(UiPack.frm)
148 |
149 | def initPromptFixedRatioHalfVaeXFormers(self):
150 | self.frmPromptFixedRatioHalfVaeXFormers = ttk.Frame(self.frm)
151 |
152 | self.lblPromptFixedRatio = ttk.Label(
153 | self.frmPromptFixedRatioHalfVaeXFormers,
154 | text=L10n.get("generate_prompt_fixed_ratio"),
155 | )
156 | self.lblPromptFixedRatio.pack(UiPack.lbl)
157 |
158 | self.varPromptFixedRatio = tk.DoubleVar(value=0.5)
159 | self.sliPromptFixedRatio = tk.Scale(
160 | self.frmPromptFixedRatioHalfVaeXFormers,
161 | from_=0.0,
162 | to=1.0,
163 | resolution=0.1,
164 | orient=tk.HORIZONTAL,
165 | variable=self.varPromptFixedRatio,
166 | takefocus=True,
167 | )
168 | self.sliPromptFixedRatio.pack(UiPack.sli)
169 |
170 | self.varUseLcm = tk.BooleanVar(value=False)
171 | self.chkUseLcm = tk.Checkbutton(
172 | self.frmPromptFixedRatioHalfVaeXFormers,
173 | text=L10n.get("generate_use_lcm"),
174 | variable=self.varUseLcm,
175 | )
176 | self.chkUseLcm.pack(UiPack.chk)
177 |
178 | self.varUseHighresFix = tk.BooleanVar(value=False)
179 | self.chkUseHighresFix = tk.Checkbutton(
180 | self.frmPromptFixedRatioHalfVaeXFormers,
181 | text=L10n.get("generate_use_highres_fix"),
182 | variable=self.varUseHighresFix,
183 | )
184 | self.chkUseHighresFix.pack(UiPack.chk)
185 |
186 | self.varUseHalfVae = tk.BooleanVar(value=False)
187 | self.chkUseHalfVae = tk.Checkbutton(
188 | self.frmPromptFixedRatioHalfVaeXFormers,
189 | text=L10n.get("generate_use_half_vae"),
190 | variable=self.varUseHalfVae,
191 | )
192 | self.chkUseHalfVae.pack(UiPack.chk)
193 |
194 | self.varUseXFormers = tk.BooleanVar(value=False)
195 | self.chkUseXFormers = tk.Checkbutton(
196 | self.frmPromptFixedRatioHalfVaeXFormers,
197 | text=L10n.get("generate_use_x_formers"),
198 | variable=self.varUseXFormers,
199 | )
200 | self.chkUseXFormers.pack(UiPack.chk)
201 |
202 | self.frmPromptFixedRatioHalfVaeXFormers.pack(UiPack.frm)
203 |
204 | @classmethod
205 | def loadConfig(self):
206 | values = ",".join(GenerateForm.schedulerValues)
207 | values = Config.get("ui", "scheduler_values", values)
208 | GenerateForm.schedulerValues = [s.strip() for s in values.split(",")]
209 |
210 | names = ",".join(GenerateForm.schedulerNames)
211 | names = Config.get("ui", "scheduler_names", names)
212 | GenerateForm.schedulerNames = [s.strip() for s in names.split(",")]
213 |
214 | GenerateForm.maxSteps = Config.getInt("ui", "max_steps", GenerateForm.maxSteps)
215 |
216 | def storeConfig(self):
217 | Config.set("ui", "scheduler_values", ",".join(GenerateForm.schedulerValues))
218 | Config.set("ui", "scheduler_names", ",".join(GenerateForm.schedulerNames))
219 | Config.set("ui", "max_steps", GenerateForm.maxSteps)
220 |
221 | def setDarkTheme(self, colors):
222 | self.sliContext.configure(colors["sli"])
223 | self.sliSteps.configure(colors["sli"])
224 | self.sliClipSkip.configure(colors["sli"])
225 | self.sliGuidanceScale.configure(colors["sli"])
226 | self.sliPromptFixedRatio.configure(colors["sli"])
227 | self.chkUseLcm.configure(colors["chk"])
228 | self.chkUseHighresFix.configure(colors["chk"])
229 | self.chkUseHalfVae.configure(colors["chk"])
230 | self.chkUseXFormers.configure(colors["chk"])
231 |
--------------------------------------------------------------------------------
/editor/src/ui_input.py:
--------------------------------------------------------------------------------
1 | from config import Config
2 | from l10n import L10n
3 | import tkinter as tk
4 | import tkinter.ttk as ttk
5 | from tkinter import scrolledtext
6 | from ui_const import Ui, UiPack
7 | from task import Task
8 |
9 |
10 | class InputForm:
11 | initH = 300
12 | minH = 250
13 |
14 | def __init__(self, parent):
15 | self.frm = ttk.Frame(parent)
16 |
17 | self.frmTool = ttk.Frame(self.frm)
18 |
19 | self.initGenerate(self.frmTool)
20 | self.sep0 = ttk.Separator(self.frmTool)
21 | self.sep0.pack(UiPack.sep)
22 |
23 | self.initPreview(self.frmTool)
24 | self.sep1 = ttk.Separator(self.frmTool)
25 | self.sep1.pack(UiPack.sep)
26 |
27 | self.initTask(self.frmTool)
28 |
29 | self.frmTool.pack(fill=tk.Y, side=tk.RIGHT)
30 |
31 | self.txtInput = scrolledtext.ScrolledText(self.frm, undo=True, maxundo=-1)
32 | self.txtInput.configure(Ui.cfgTxtArea)
33 | self.txtInput.pack(UiPack.txt)
34 |
35 | self.frm.pack(fill=tk.BOTH, expand=True)
36 |
37 | parent.add(
38 | self.frm, height=InputForm.initH, minsize=InputForm.minH, stretch="always"
39 | )
40 |
41 | self.initCtxMenu()
42 |
43 | def initGenerate(self, parent):
44 | self.frmGenerate = ttk.Frame(parent)
45 | self.btnGenerate = ttk.Button(self.frmGenerate, text=L10n.get("generate_anime"))
46 | self.btnGenerate.pack(UiPack.btn, fill=tk.X, expand=True)
47 |
48 | self.btnSeedGacha = ttk.Button(self.frmGenerate, text=L10n.get("seed_gacha"))
49 | self.btnSeedGacha.pack(UiPack.btn, fill=tk.X, expand=True)
50 |
51 | self.frmGenerate.pack(UiPack.frm)
52 |
53 | def initPreview(self, parent):
54 | self.frmPreview = ttk.Frame(parent)
55 | self.btnPreview = ttk.Button(self.frmPreview, text=L10n.get("preview"))
56 | self.btnPreview.pack(UiPack.btn)
57 |
58 | self.varUpscale = tk.BooleanVar(value=True)
59 | self.chkUpscale = tk.Checkbutton(
60 | self.frmPreview, text=L10n.get("preview_upscale"), variable=self.varUpscale
61 | )
62 | self.chkUpscale.pack(UiPack.chk)
63 |
64 | # self.varInterpolation = tk.BooleanVar(value=True)
65 | # self.chkInterpolation = tk.Checkbutton(
66 | # self.frmPreview,
67 | # text=L10n.get("preview_interpolation"),
68 | # variable=self.varInterpolation,
69 | # )
70 | # self.chkInterpolation.pack(UiPack.chk)
71 |
72 | self.frmPreview.pack(UiPack.frm)
73 |
74 | self.frmStart = ttk.Frame(parent)
75 | self.varStart = tk.IntVar(value=0)
76 | self.lblStart = ttk.Label(self.frmStart, text=L10n.get("preview_start_frame"))
77 | self.lblStart.pack(UiPack.lbl)
78 | self.sliStart = tk.Scale(
79 | self.frmStart,
80 | from_=0,
81 | to=600,
82 | resolution=10,
83 | orient=tk.HORIZONTAL,
84 | variable=self.varStart,
85 | takefocus=True,
86 | )
87 | self.sliStart.pack(UiPack.sli)
88 | self.frmStart.pack(UiPack.frm)
89 |
90 | self.frmLength = ttk.Frame(parent)
91 | self.varLength = tk.IntVar(value=0)
92 | self.lblLength = ttk.Label(self.frmLength, text=L10n.get("preview_length"))
93 | self.lblLength.pack(UiPack.lbl)
94 | self.sliLength = tk.Scale(
95 | self.frmLength,
96 | from_=0,
97 | to=600,
98 | resolution=10,
99 | orient=tk.HORIZONTAL,
100 | variable=self.varLength,
101 | takefocus=True,
102 | )
103 | self.sliLength.pack(UiPack.sli)
104 | self.frmLength.pack(UiPack.frm)
105 |
106 | def initTask(self, parent):
107 | self.frmTask = ttk.Frame(parent)
108 |
109 | self.varForever = tk.BooleanVar(value=False)
110 | self.chkForever = tk.Checkbutton(
111 | self.frmTask, text=L10n.get("task_forever"), variable=self.varForever
112 | )
113 | self.chkForever.pack(UiPack.chk)
114 |
115 | self.varPauseByError = tk.BooleanVar(value=True)
116 | self.chkPauseByError = tk.Checkbutton(
117 | self.frmTask,
118 | text=L10n.get("task_pause_by_error"),
119 | variable=self.varPauseByError,
120 | )
121 | self.chkPauseByError.pack(UiPack.chk)
122 |
123 | self.frmTask.pack(UiPack.frm)
124 |
125 | self.varTask = tk.StringVar()
126 | Task.queueInfoChangedEvent.append(lambda queue: self.varTask.set(queue))
127 | self.lstTask = tk.Listbox(self.frmTool, listvariable=self.varTask)
128 | self.lstTask.pack(fill=tk.BOTH, expand=True, padx=Ui.padX, pady=Ui.padY)
129 |
130 | def initCtxMenu(self):
131 | self.mnuInTxt = tk.Menu(self.txtInput, tearoff=False)
132 | self.mnuInTxt.add_command(
133 | label=L10n.get("clear"),
134 | command=lambda: self.txtInput.delete("1.0", tk.END),
135 | )
136 | self.txtInput.bind(
137 | "", lambda e: self.mnuInTxt.post(e.x_root, e.y_root)
138 | )
139 |
140 | @classmethod
141 | def loadConfig(cls):
142 | InputForm.initH = Config.get("ui_size", "input_h", fallback=InputForm.initH)
143 |
144 | def storeConfig(self):
145 | input_h = self.frm.winfo_height()
146 | if input_h != 1:
147 | Config.set("ui_size", "input_h", input_h)
148 |
149 | def setDarkTheme(self, colors):
150 | self.txtInput.configure(colors["txt"])
151 | self.chkUpscale.configure(colors["chk"])
152 | # self.chkInterpolation.configure(colors["chk"])
153 | self.sliStart.configure(colors["sli"])
154 | self.sliLength.configure(colors["sli"])
155 | self.lstTask.configure(colors["lst"])
156 | self.chkForever.configure(colors["chk"])
157 | self.chkPauseByError.configure(colors["chk"])
158 |
--------------------------------------------------------------------------------
/editor/src/ui_ip_adapter.py:
--------------------------------------------------------------------------------
1 | import os, subprocess
2 | import tkinter as tk
3 | import tkinter.ttk as ttk
4 | from const import Path
5 | from l10n import L10n
6 | from config import Config
7 | from ui_const import UiPack
8 |
9 |
10 | class IpAdapterForm:
11 | def __init__(self, parent):
12 | self.frm = ttk.Frame(parent)
13 |
14 | self.initFlags()
15 | self.initScale()
16 | self.initImageDir()
17 | self.initInfo()
18 |
19 | parent.add(self.frm, text=L10n.get("ip_adapter_tab"))
20 |
21 | def initFlags(self):
22 | self.frmFlags = ttk.Frame(self.frm)
23 |
24 | self.varUse = tk.BooleanVar()
25 | self.chkUse = tk.Checkbutton(
26 | self.frmFlags,
27 | text=L10n.get("ip_adapter_use"),
28 | variable=self.varUse,
29 | )
30 | self.chkUse.pack(UiPack.chk)
31 |
32 | self.varUsePlus = tk.BooleanVar()
33 | self.chkUsePlus = tk.Checkbutton(
34 | self.frmFlags,
35 | text=L10n.get("ip_adapter_use_plus"),
36 | variable=self.varUsePlus,
37 | )
38 | self.chkUsePlus.pack(UiPack.chk)
39 |
40 | self.varUsePlusFace = tk.BooleanVar()
41 | self.chkUsePlusFace = tk.Checkbutton(
42 | self.frmFlags,
43 | text=L10n.get("ip_adapter_use_plus_face"),
44 | variable=self.varUsePlusFace,
45 | )
46 | self.chkUsePlusFace.pack(UiPack.chk)
47 |
48 | self.frmFlags.pack(UiPack.frm)
49 |
50 | def initScale(self):
51 | self.frmScale = ttk.Frame(self.frm)
52 |
53 | self.lblScale = ttk.Label(self.frmScale, text=L10n.get("ip_adapter_scale"))
54 | self.lblScale.pack(UiPack.lbl)
55 |
56 | self.varScale = tk.DoubleVar(value=0.5)
57 | self.sliScale = tk.Scale(
58 | self.frmScale,
59 | from_=0.0,
60 | to=1.0,
61 | resolution=0.05,
62 | orient=tk.HORIZONTAL,
63 | variable=self.varScale,
64 | takefocus=True,
65 | )
66 | self.sliScale.pack(UiPack.sli)
67 |
68 | self.frmScale.pack(UiPack.frm)
69 |
70 | def initImageDir(self):
71 | self.frmImageDir = ttk.Frame(self.frm)
72 |
73 | self.lblImageDir = ttk.Label(
74 | self.frmImageDir, text=L10n.get("ip_adapter_image_dir")
75 | )
76 | self.lblImageDir.pack(UiPack.lbl)
77 |
78 | self.varImageDir = tk.StringVar()
79 | self.cmbImageDir = ttk.Combobox(
80 | self.frmImageDir,
81 | textvariable=self.varImageDir,
82 | state="readonly",
83 | postcommand=self.listImageDirValues,
84 | width=20,
85 | height=20,
86 | )
87 | self.cmbImageDir.pack(UiPack.cmb, fill=tk.X, expand=True)
88 |
89 | self.btnImageDirExplorer = ttk.Button(
90 | self.frmImageDir,
91 | text=L10n.get("ip_adapter_image_dir_explorer"),
92 | command=lambda: subprocess.run(["explorer", Path.ipAdapter]),
93 | )
94 | self.btnImageDirExplorer.pack(UiPack.btn)
95 |
96 | self.frmImageDir.pack(UiPack.frm)
97 |
98 | def listImageDirValues(self):
99 | subDirs = [f.name for f in os.scandir(Path.ipAdapter) if f.is_dir()]
100 | self.cmbImageDir.configure(values=subDirs)
101 |
102 | def initInfo(self):
103 | self.frmInfo = ttk.Frame(self.frm)
104 |
105 | self.lblIpAdapterInfo = ttk.Label(
106 | self.frmInfo, text=L10n.get("ip_adapter_info")
107 | )
108 | self.lblIpAdapterInfo.pack(UiPack.lbl)
109 |
110 | self.frmInfo.pack(UiPack.frm)
111 |
112 | def setDarkTheme(self, colors):
113 | self.chkUse.configure(colors["chk"])
114 | self.chkUsePlus.configure(colors["chk"])
115 | self.chkUsePlusFace.configure(colors["chk"])
116 | self.sliScale.configure(colors["sli"])
117 |
--------------------------------------------------------------------------------
/editor/src/ui_menu.py:
--------------------------------------------------------------------------------
1 | import json
2 | import tkinter as tk
3 | import tkinter.ttk as ttk
4 | from const import Path
5 | from l10n import L10n
6 | from ui_const import Ui, UiPack
7 |
8 |
9 | class Menu:
10 | def __init__(self, parent):
11 | self.menuBar = tk.Menu(parent)
12 | parent.config(menu=self.menuBar)
13 |
14 | self.initFileMenu()
15 | self.initAnimeMenu()
16 | self.initFolderMenu()
17 | self.initToolMenu()
18 | self.initDownloadMenu()
19 | self.initSettingMenu()
20 | self.initHelpMenu()
21 |
22 | def initFileMenu(self):
23 | self.fileMenu = tk.Menu(self.menuBar, tearoff=False)
24 | self.menuBar.add_cascade(label=L10n.get("m_file"), menu=self.fileMenu)
25 | self.fileMenu.add_command(label=L10n.get("m_file_new"))
26 | self.fileMenu.add_command(label=L10n.get("m_file_open"))
27 | self.fileMenu.add_command(label=L10n.get("m_file_save"))
28 | self.fileMenu.add_command(label=L10n.get("m_file_save_as"))
29 | self.fileMenu.add_separator()
30 | self.fileMenu.add_command(label=L10n.get("m_file_exit"))
31 |
32 | def initAnimeMenu(self):
33 | self.animeMenu = tk.Menu(self.menuBar, tearoff=False)
34 | self.menuBar.add_cascade(label=L10n.get("m_anime"), menu=self.animeMenu)
35 |
36 | self.animeMenu.add_command(label=L10n.get("m_anime_preview"))
37 | self.animeMenu.add_command(label=L10n.get("m_anime_seed_gacha"))
38 | self.animeMenu.add_command(label=L10n.get("m_anime_generate"))
39 | self.animeMenu.add_separator()
40 | self.animeMenu.add_command(label=L10n.get("m_anime_upscale"))
41 | self.animeMenu.add_command(label=L10n.get("m_anime_upscale_config"))
42 |
43 | def initFolderMenu(self):
44 | self.folderMenu = tk.Menu(self.menuBar, tearoff=False)
45 | self.menuBar.add_cascade(label=L10n.get("m_folder"), menu=self.folderMenu)
46 |
47 | self.folderMenu.add_command(label=L10n.get("m_output_folder"))
48 |
49 | self.promptTravelFolderMenu = tk.Menu(self.folderMenu, tearoff=False)
50 | self.folderMenu.add_cascade(
51 | label=L10n.get("m_prompt_travel_folder"), menu=self.promptTravelFolderMenu
52 | )
53 |
54 | self.promptTravelFolderMenu.add_command(label=L10n.get("m_sd_model_folder"))
55 | self.promptTravelFolderMenu.add_command(
56 | label=L10n.get("m_motion_module_folder")
57 | )
58 | self.promptTravelFolderMenu.add_command(label=L10n.get("m_lora_folder"))
59 | self.promptTravelFolderMenu.add_command(label=L10n.get("m_embeddings_folder"))
60 | self.promptTravelFolderMenu.add_command(label=L10n.get("m_wildcard_folder"))
61 |
62 | self.folderMenu.add_separator()
63 | self.promptTravelOutputFolderMenu = tk.Menu(self.folderMenu, tearoff=False)
64 | self.folderMenu.add_cascade(
65 | label=L10n.get("m_prompt_travel_output_folder"),
66 | menu=self.promptTravelOutputFolderMenu,
67 | )
68 |
69 | self.promptTravelOutputFolderMenu.add_command(
70 | label=L10n.get("m_prompt_travel_generate_folder")
71 | )
72 | self.promptTravelOutputFolderMenu.add_command(
73 | label=L10n.get("m_prompt_travel_tile_folder")
74 | )
75 | self.promptTravelOutputFolderMenu.add_command(
76 | label=L10n.get("m_prompt_travel_refine_folder")
77 | )
78 |
79 | self.folderMenu.add_command(label=L10n.get("m_temp_folder"))
80 | self.folderMenu.add_command(label=L10n.get("m_log_folder"))
81 |
82 | def initToolMenu(self):
83 | self.toolMenu = tk.Menu(self.menuBar, tearoff=False)
84 | self.menuBar.add_cascade(label=L10n.get("m_tool"), menu=self.toolMenu)
85 |
86 | self.toolMenu.add_command(label=L10n.get("m_tool_convert_lora"))
87 |
88 | self.toolMenu.add_command(label=L10n.get("m_tool_easy_leco"))
89 |
90 | def initDownloadMenu(self):
91 | self.downloadMenuData = ""
92 | with open(Path.downloadMenu, "r", encoding="utf-8-sig") as f:
93 | self.downloadMenuData = json.load(f)
94 |
95 | self.downloadMenu = tk.Menu(self.menuBar, tearoff=False)
96 | self.menuBar.add_cascade(label=L10n.get("m_download"), menu=self.downloadMenu)
97 |
98 | def initDlMenu(category):
99 | dlMenu = tk.Menu(self.downloadMenu, tearoff=False)
100 | self.downloadMenu.add_cascade(
101 | label=L10n.get(f"m_dl_{category}"), menu=dlMenu
102 | )
103 |
104 | for name, data in self.downloadMenuData[category].items():
105 | label = f'({data["tag"]}) {name}' if "tag" in data else name
106 | data["menu"] = dlMenu
107 | data["checked"] = tk.BooleanVar(value=True)
108 | dlMenu.add_checkbutton(label=label, variable=data["checked"])
109 | return dlMenu
110 |
111 | self.dlModel = initDlMenu("model")
112 | self.dlLora = initDlMenu("lora")
113 | self.dlMotionModule = initDlMenu("motion_module")
114 | self.dlMotionModule = initDlMenu("motion_lora")
115 | self.dlVae = initDlMenu("vae")
116 | self.dlEmbedding = initDlMenu("embedding")
117 |
118 | def initSettingMenu(self):
119 | self.settingMenu = tk.Menu(self.menuBar, tearoff=False)
120 | self.menuBar.add_cascade(label=L10n.get("m_setting"), menu=self.settingMenu)
121 |
122 | self.settingMenu.add_command(label=L10n.get("m_set_default_prompt"))
123 | self.settingMenu.add_command(label=L10n.get("m_set_default_setting"))
124 | self.settingMenu.add_command(label=L10n.get("m_set_change_light_dark"))
125 | self.settingMenu.add_separator()
126 | self.settingMenu.add_command(label=L10n.get("m_set_open_ini_file"))
127 | self.settingMenu.add_command(label=L10n.get("m_set_open_default_prompt_file"))
128 | self.settingMenu.add_command(
129 | label=L10n.get("m_set_open_prompt_travel_template_file")
130 | )
131 |
132 | def initHelpMenu(self):
133 | self.helpMenu = tk.Menu(self.menuBar, tearoff=False)
134 | self.menuBar.add_cascade(label=L10n.get("m_help"), menu=self.helpMenu)
135 |
136 | self.helpMenu.add_command(label=L10n.get("m_prompt_help"))
137 |
138 | self.helpMenu.add_command(label=L10n.get("m_github"))
139 |
140 | self.helpMenu.add_separator()
141 | self.referenceMenu = tk.Menu(self.helpMenu, tearoff=False)
142 | self.helpMenu.add_cascade(
143 | label=L10n.get("m_reference"), menu=self.referenceMenu
144 | )
145 |
146 | self.referenceMenu.add_command(label="AnimateDiff prompt travel")
147 | self.referenceMenu.add_command(label="sd-scripts")
148 | self.referenceMenu.add_command(label="Codex FFmpeg")
149 | self.referenceMenu.add_command(label="Practical-RIFE")
150 | self.referenceMenu.add_command(label="NudeNet")
151 |
--------------------------------------------------------------------------------
/editor/src/ui_output.py:
--------------------------------------------------------------------------------
1 | from config import Config
2 | from l10n import L10n
3 | import tkinter as tk
4 | import tkinter.ttk as ttk
5 | from tkinter import scrolledtext
6 | from ui_const import Ui, UiPack
7 |
8 |
9 | class OutputForm:
10 | initH = 100
11 | minH = 82
12 |
13 | def __init__(self, parent):
14 | self.ntb = ttk.Notebook(parent)
15 | parent.add(
16 | self.ntb, height=OutputForm.initH, minsize=OutputForm.minH, stretch="never"
17 | )
18 |
19 | self.txtUser = scrolledtext.ScrolledText(self.ntb, state=tk.DISABLED)
20 | self.txtUser.configure(Ui.cfgTxtArea)
21 | self.txtUser.pack(UiPack.txt)
22 | self.ntb.add(self.txtUser, text=L10n.get("user_log_tab"))
23 |
24 | self.txtSys = scrolledtext.ScrolledText(self.ntb, state=tk.DISABLED)
25 | self.txtSys.configure(Ui.cfgTxtArea)
26 | self.txtSys.pack(UiPack.txt)
27 | self.ntb.add(self.txtSys, text=L10n.get("system_log_tab"))
28 |
29 | # self.ntb.select(1) # select system log tab
30 |
31 | self.initCtxMenu()
32 |
33 | def initCtxMenu(self):
34 | self.mnuUser = tk.Menu(self.txtUser, tearoff=False)
35 | self.mnuUser.add_command(
36 | label=L10n.get("clear"), command=lambda: self.txtUser.delete("1.0", tk.END)
37 | )
38 |
39 | self.txtUser.bind("", lambda e: self.mnuUser.post(e.x_root, e.y_root))
40 |
41 | self.mnuSys = tk.Menu(self.txtSys, tearoff=False)
42 | self.mnuSys.add_command(
43 | label=L10n.get("clear"), command=lambda: self.txtSys.delete("1.0", tk.END)
44 | )
45 |
46 | self.txtSys.bind("", lambda e: self.mnuSys.post(e.x_root, e.y_root))
47 |
48 | @classmethod
49 | def loadConfig(self):
50 | OutputForm.initH = Config.get("ui_size", "output_h", OutputForm.initH)
51 |
52 | def storeConfig(self):
53 | output_h = self.ntb.winfo_height()
54 | if output_h != 1:
55 | Config.set("ui_size", "output_h", output_h)
56 |
57 | def setDarkTheme(self, colors):
58 | self.txtUser.configure(colors["selIns"])
59 | self.txtSys.configure(colors["selIns"])
60 |
--------------------------------------------------------------------------------
/editor/src/ui_preview.py:
--------------------------------------------------------------------------------
1 | from config import Config
2 | from l10n import L10n
3 | import tkinter as tk
4 | import tkinter.ttk as ttk
5 | from tkinter import scrolledtext
6 | from ui_const import Ui, UiPack
7 |
8 |
9 | class PreviewForm:
10 | def __init__(self, parent):
11 | self.frm = ttk.Frame(parent)
12 |
13 | self.initOptions()
14 |
15 | self.txtPrev = scrolledtext.ScrolledText(self.frm, state=tk.DISABLED)
16 | self.txtPrev.configure(Ui.cfgTxtArea)
17 | self.txtPrev.pack(UiPack.txt)
18 |
19 | parent.add(self.frm, text=L10n.get("preview_tab"))
20 |
21 | def initOptions(self):
22 | self.frmOptions = ttk.Frame(self.frm)
23 |
24 | self.varShowKeyframe = tk.BooleanVar(value=True)
25 | self.chkShowKeyframe = tk.Checkbutton(
26 | self.frmOptions,
27 | text=L10n.get("preview_show_keyframe"),
28 | variable=self.varShowKeyframe,
29 | )
30 | self.chkShowKeyframe.pack(UiPack.chk)
31 |
32 | self.varShowHeaderFooter = tk.BooleanVar(value=True)
33 | self.chkShowHeaderFooter = tk.Checkbutton(
34 | self.frmOptions,
35 | text=L10n.get("preview_show_header_footer"),
36 | variable=self.varShowHeaderFooter,
37 | )
38 | self.chkShowHeaderFooter.pack(UiPack.chk)
39 |
40 | self.varShowKeyframe.trace_add("write", self.updateShowKeyframe)
41 |
42 | self.varShowAnime = tk.BooleanVar(value=True)
43 | self.chkShowAnime = tk.Checkbutton(
44 | self.frmOptions,
45 | text=L10n.get("preview_show_anime"),
46 | variable=self.varShowAnime,
47 | )
48 | self.chkShowAnime.pack(UiPack.chk)
49 |
50 | self.frmOptions.pack(UiPack.frm, side=tk.BOTTOM)
51 |
52 | def updateShowKeyframe(self, *args):
53 | if self.varShowKeyframe.get():
54 | self.chkShowHeaderFooter.configure(state=tk.NORMAL)
55 | else:
56 | self.chkShowHeaderFooter.configure(state=tk.DISABLED)
57 |
58 | def setDarkTheme(self, colors):
59 | self.chkShowKeyframe.configure(colors["chk"])
60 | self.chkShowHeaderFooter.configure(colors["chk"])
61 | self.chkShowAnime.configure(colors["chk"])
62 | self.txtPrev.configure(colors["txt"])
63 |
--------------------------------------------------------------------------------
/sample/Gacha-L120-C16-W448-H512.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "path": "models/sd/nadenadesitai_v10.safetensors",
4 | "motion_module": "models/motion-module/mm_sd_v15_v2.ckpt",
5 | "compile": false,
6 | "seed": [
7 | -1
8 | ],
9 | "scheduler": "k_dpmpp_sde",
10 | "steps": 20,
11 | "guidance_scale": 8,
12 | "clip_skip": 2,
13 | "head_prompt": "(crowds, street of Akihabara Tokyo: 1.2), black short hair, black cat ears, black eyes",
14 | "prompt_map": {
15 | "0": "closed eyes, portrait, maid headdress, edwardian maid outfit",
16 | "10": "portrait, upper body, maid headdress, edwardian maid outfit",
17 | "20": "upper body, dancing, maid headdress, edwardian maid outfit, happy",
18 | "30": "upper body, cowboy shot, dancing, maid headdress, edwardian maid outfit, happy",
19 | "40": "cowboy shot, dancing, maid headdress, maid outfit, happy",
20 | "50": "dancing, cowboy shot, full body, maid headdress, miniskirt maid outfit, happy",
21 | "60": "dancing, full body, maid headdress, miniskirt maid outfit, frilled, happy",
22 | "70": "night, dancing, full body, cowboy shot, idol, frilled, ribbon, tiara, happy",
23 | "80": "night, dancing, cowboy shot, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
24 | "90": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
25 | "100": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara",
26 | "110": "night, upper body, maid headdress, edwardian maid outfit"
27 | },
28 | "tail_prompt": "1girl, solo, black choker, hair between eyes",
29 | "n_prompt": [
30 | "(worst quality, low quality:1.4), cat, cow"
31 | ],
32 | "lora_map": {},
33 | "upscale_config": {
34 | "scheduler": "k_dpmpp_sde",
35 | "steps": 15,
36 | "strength": 0.5,
37 | "guidance_scale": 8,
38 | "controlnet_tile": {
39 | "enable": true,
40 | "use_preprocessor": false,
41 | "controlnet_conditioning_scale": 1.0,
42 | "guess_mode": false,
43 | "control_guidance_start": 0.0,
44 | "control_guidance_end": 1.0
45 | }
46 | },
47 | "output": {
48 | "format": "mp4",
49 | "fps": 10,
50 | "encode_param": {
51 | "crf": 20
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/sample/Gacha.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0..\Generate.bat" "%~dp0Gacha-L120-C16-W448-H512.json"
3 |
--------------------------------------------------------------------------------
/sample/Oiyami-L60-C16-W512-H384-T768.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "path": "models/sd/onigiriMix_v10Clearvae.safetensors",
4 | "motion_module": "models/motion-module/mm_sd_v15_v2.ckpt",
5 | "compile": false,
6 | "seed": [
7 | -1
8 | ],
9 | "scheduler": "k_dpmpp_sde",
10 | "steps": 20,
11 | "guidance_scale": 8,
12 | "clip_skip": 2,
13 | "prompt_fixed_ratio": 0.0,
14 | "head_prompt": "",
15 | "prompt_map": {
16 | "0": "美味しいヤミー❗️✨🤟😁👍✨⚡️感謝❗️🙌✨🙌✨🙌感謝❗️🙌✨🙌✨🙌またいっぱい食べたいな❗️🥓🥩🍗🍖😋🍖🍴✨デリシャッ‼️🙏✨シャ‼️🙏✨ シャ‼️🙏✨ シャ‼️🙏✨ シャ‼️🙏✨ シャ‼️🙏✨ シャッッ‼😁🙏✨😁🙏✨😁🙏✨ハッピー🌟スマイル❗️❗️❗💥✨👉😁👈✨💥"
17 | },
18 | "tail_prompt": "",
19 | "n_prompt": [
20 | "(worst quality, low quality:1.4)"
21 | ],
22 | "lora_map": {},
23 | "upscale_config": {
24 | "scheduler": "k_dpmpp_sde",
25 | "steps": 15,
26 | "strength": 0.5,
27 | "guidance_scale": 8,
28 | "controlnet_tile": {
29 | "enable": true,
30 | "use_preprocessor": false,
31 | "controlnet_conditioning_scale": 1.0,
32 | "guess_mode": false,
33 | "control_guidance_start": 0.0,
34 | "control_guidance_end": 1.0
35 | }
36 | },
37 | "output": {
38 | "format": "mp4",
39 | "fps": 10,
40 | "encode_param": {
41 | "crf": 20
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/sample/Oiyami.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0..\Generate.bat" "%~dp0Oiyami-L60-C16-W512-H384-T768.json"
3 | echo Oiyami: https://www.google.com/search?q=%%E7%%BE%%8E%%E5%%91%%B3%%E3%%81%%97%%E3%%81%%84%%E3%%83%%A4%%E3%%83%%9F%%E3%%83%%BC%%E6%%84%%9F%%E8%%AC%%9D%%E6%%84%%9F%%E8%%AC%%9D
4 | pause
5 |
--------------------------------------------------------------------------------
/sample/UpscaledGacha-L120-C16-W448-H512-T768.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "path": "models/sd/nadenadesitai_v10.safetensors",
4 | "motion_module": "models/motion-module/mm_sd_v15_v2.ckpt",
5 | "compile": false,
6 | "seed": [
7 | -1
8 | ],
9 | "scheduler": "k_dpmpp_sde",
10 | "steps": 20,
11 | "guidance_scale": 8,
12 | "clip_skip": 2,
13 | "head_prompt": "(crowds, street of Akihabara Tokyo: 1.2), black short hair, black cat ears, black eyes",
14 | "prompt_map": {
15 | "0": "closed eyes, portrait, maid headdress, edwardian maid outfit",
16 | "10": "portrait, upper body, maid headdress, edwardian maid outfit",
17 | "20": "upper body, dancing, maid headdress, edwardian maid outfit, happy",
18 | "30": "upper body, cowboy shot, dancing, maid headdress, edwardian maid outfit, happy",
19 | "40": "cowboy shot, dancing, maid headdress, maid outfit, happy",
20 | "50": "dancing, cowboy shot, full body, maid headdress, miniskirt maid outfit, happy",
21 | "60": "dancing, full body, maid headdress, miniskirt maid outfit, frilled, happy",
22 | "70": "night, dancing, full body, cowboy shot, idol, frilled, ribbon, tiara, happy",
23 | "80": "night, dancing, cowboy shot, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
24 | "90": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
25 | "100": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara",
26 | "110": "night, upper body, maid headdress, edwardian maid outfit"
27 | },
28 | "tail_prompt": "1girl, solo, black choker, hair between eyes",
29 | "n_prompt": [
30 | "(worst quality, low quality:1.4), cat, cow"
31 | ],
32 | "lora_map": {},
33 | "upscale_config": {
34 | "scheduler": "k_dpmpp_sde",
35 | "steps": 15,
36 | "strength": 0.5,
37 | "guidance_scale": 8,
38 | "controlnet_tile": {
39 | "enable": true,
40 | "use_preprocessor": false,
41 | "controlnet_conditioning_scale": 1.0,
42 | "guess_mode": false,
43 | "control_guidance_start": 0.0,
44 | "control_guidance_end": 1.0
45 | }
46 | },
47 | "output": {
48 | "format": "mp4",
49 | "fps": 10,
50 | "encode_param": {
51 | "crf": 20
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/sample/UpscaledGacha.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0..\Generate.bat" "%~dp0UpscaledGacha-L120-C16-W448-H512-T768.json"
3 |
--------------------------------------------------------------------------------
/sample/Vram12G-L120-C16-W608-H384-T768-T1152.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "path": "models/sd/xxmix9realistic_v40.safetensors",
4 | "motion_module": "models/motion-module/mm_sd_v15_v2.ckpt",
5 | "compile": false,
6 | "seed": [
7 | -1
8 | ],
9 | "scheduler": "k_dpmpp_sde",
10 | "steps": 20,
11 | "guidance_scale": 8,
12 | "clip_skip": 2,
13 | "head_prompt": "(crowds, street of Akihabara Tokyo: 1.2), black short hair, black cat ears, black eyes",
14 | "prompt_map": {
15 | "0": "closed eyes, portrait, maid headdress, edwardian maid outfit",
16 | "10": "portrait, upper body, maid headdress, edwardian maid outfit",
17 | "20": "upper body, dancing, maid headdress, edwardian maid outfit, happy",
18 | "30": "upper body, cowboy shot, dancing, maid headdress, edwardian maid outfit, happy",
19 | "40": "cowboy shot, dancing, maid headdress, maid outfit, happy",
20 | "50": "dancing, cowboy shot, full body, maid headdress, miniskirt maid outfit, happy",
21 | "60": "dancing, full body, maid headdress, miniskirt maid outfit, frilled, happy",
22 | "70": "night, dancing, full body, cowboy shot, idol, frilled, ribbon, tiara, happy",
23 | "80": "night, dancing, cowboy shot, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
24 | "90": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
25 | "100": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara",
26 | "110": "night, upper body, maid headdress, edwardian maid outfit"
27 | },
28 | "tail_prompt": "1girl, solo, black choker, hair between eyes",
29 | "n_prompt": [
30 | "(worst quality, low quality:1.4), cat, cow"
31 | ],
32 | "lora_map": {},
33 | "upscale_config": {
34 | "scheduler": "k_dpmpp_sde",
35 | "steps": 15,
36 | "strength": 0.5,
37 | "guidance_scale": 8,
38 | "controlnet_tile": {
39 | "enable": true,
40 | "use_preprocessor": false,
41 | "controlnet_conditioning_scale": 1.0,
42 | "guess_mode": false,
43 | "control_guidance_start": 0.0,
44 | "control_guidance_end": 1.0
45 | }
46 | },
47 | "output": {
48 | "format": "mp4",
49 | "fps": 10,
50 | "encode_param": {
51 | "crf": 20
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/sample/Vram12G.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0..\Generate.bat" "%~dp0Vram12G-L120-C16-W608-H384-T768-T1152.json"
3 |
--------------------------------------------------------------------------------
/sample/Vram8G-L120-C16-W448-H512-T768-T1024.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "path": "models/sd/nadenadesitai_v10.safetensors",
4 | "motion_module": "models/motion-module/mm_sd_v15_v2.ckpt",
5 | "compile": false,
6 | "seed": [
7 | -1
8 | ],
9 | "scheduler": "k_dpmpp_sde",
10 | "steps": 20,
11 | "guidance_scale": 8,
12 | "clip_skip": 2,
13 | "head_prompt": "(crowds, street of Akihabara Tokyo: 1.2), black short hair, black cat ears, black eyes",
14 | "prompt_map": {
15 | "0": "closed eyes, portrait, maid headdress, edwardian maid outfit",
16 | "10": "portrait, upper body, maid headdress, edwardian maid outfit",
17 | "20": "upper body, dancing, maid headdress, edwardian maid outfit, happy",
18 | "30": "upper body, cowboy shot, dancing, maid headdress, edwardian maid outfit, happy",
19 | "40": "cowboy shot, dancing, maid headdress, maid outfit, happy",
20 | "50": "dancing, cowboy shot, full body, maid headdress, miniskirt maid outfit, happy",
21 | "60": "dancing, full body, maid headdress, miniskirt maid outfit, frilled, happy",
22 | "70": "night, dancing, full body, cowboy shot, idol, frilled, ribbon, tiara, happy",
23 | "80": "night, dancing, cowboy shot, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
24 | "90": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara, happy",
25 | "100": "night, stylish pose, explosion, idol, magical girl, angel wings, frilled, ribbon, tiara",
26 | "110": "night, upper body, maid headdress, edwardian maid outfit"
27 | },
28 | "tail_prompt": "1girl, solo, black choker, hair between eyes",
29 | "n_prompt": [
30 | "(worst quality, low quality:1.4), cat, cow"
31 | ],
32 | "lora_map": {},
33 | "upscale_config": {
34 | "scheduler": "k_dpmpp_sde",
35 | "steps": 15,
36 | "strength": 0.5,
37 | "guidance_scale": 8,
38 | "controlnet_tile": {
39 | "enable": true,
40 | "use_preprocessor": false,
41 | "controlnet_conditioning_scale": 1.0,
42 | "guess_mode": false,
43 | "control_guidance_start": 0.0,
44 | "control_guidance_end": 1.0
45 | }
46 | },
47 | "output": {
48 | "format": "mp4",
49 | "fps": 10,
50 | "encode_param": {
51 | "crf": 20
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/sample/Vram8G.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call "%~dp0..\Generate.bat" "%~dp0Vram8G-L120-C16-W448-H512-T768-T1024.json"
3 |
--------------------------------------------------------------------------------
/src/Setup-ECCV2022-RIFE.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0..
4 | if not exist ECCV2022-RIFE (
5 | echo git clone https://github.com/megvii-research/ECCV2022-RIFE
6 | git clone https://github.com/megvii-research/ECCV2022-RIFE
7 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
8 | )
9 | popd rem %~dp0..
10 |
11 | pushd %~dp0..\ECCV2022-RIFE
12 | if not exist venv (
13 | python -m venv venv
14 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
15 | )
16 | call venv\Scripts\activate.bat
17 |
18 | echo pip install ECCV2022-RIFE venv
19 | python -m pip install -q --upgrade pip
20 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
21 |
22 | pip install -q torch==2.0.1+cu118 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
23 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
24 |
25 | pip install -q -r requirements.txt
26 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
27 |
28 | pip install -q numpy==1.23.5
29 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
30 |
31 | call venv\Scripts\deactivate.bat
32 |
33 |
34 | if not exist train_log (
35 | echo curl https://github.com/hzwer/Practical-RIFE/blob/main/README.md#model-list
36 | curl -Lo RifeModel.zip https://drive.google.com/uc?id=1APIzVeI-4ZZCEuIRE1m6WYfSCaOsi_7_
37 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
38 |
39 | echo Unzip RifeModel.zip
40 | PowerShell -Version 5.1 -ExecutionPolicy Bypass Expand-Archive -Path RifeModel.zip -DestinationPath .
41 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
42 |
43 | echo del RifeModel.zip
44 | del RifeModel.zip
45 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
46 | )
47 | popd rem ECCV2022-RIFE
48 |
--------------------------------------------------------------------------------
/src/Setup-EasyPromptAnime.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | pushd %~dp0
3 |
4 | where findstr > nul 2>&1
5 | if %ERRORLEVEL% neq 0 (
6 | echo [ERROR] findstr not found. Add C:\Windows\System32 to PATH.
7 | pause & popd & exit /b 1
8 | )
9 |
10 | echo "%~dp0" | findstr /r /c:"[^a-zA-Z0-9:/\\]+" >nul
11 | if %ERRORLEVEL% equ 0 (
12 | echo [ERROR] "%~dp0" contains non-alphanumeric characters.
13 | pause & popd & exit /b 1
14 | )
15 |
16 | python --version | findstr "3.10." || (
17 | echo [ERROR] Invalid python version. require 3.10.6.
18 | python --version
19 | echo Ctrl + Click: https://www.python.org/ftp/python/3.10.6/python-3.10.6-amd64.exe
20 | pause & popd & exit /b 1
21 | )
22 |
23 | where git
24 | if %ERRORLEVEL% neq 0 (
25 | echo [ERROR] git not found. require Git for Windows.
26 | echo Ctrl + Click: https://gitforwindows.org/
27 | pause & popd & exit /b 1
28 | )
29 |
30 | git clone https://github.com/Zuntan03/EasyPromptAnime
31 | if %ERRORLEVEL% neq 0 (
32 | echo [ERROR] git clone https://github.com/Zuntan03/EasyPromptAnime
33 | echo Disable virus checking software.
34 | pause & popd & exit /b 1
35 | )
36 |
37 | robocopy .\EasyPromptAnime\ . /s /move
38 |
39 | call src\Setup.bat
40 | if %errorlevel% neq 0 ( popd & exit /b %errorlevel% )
41 |
42 | start cmd /c EasyPromptAnimeEditor.bat
43 |
44 | popd rem %~dp0
45 | echo The following error messages are not a problem.
46 | del "%~f0"
47 |
--------------------------------------------------------------------------------
/src/Setup-Practical-RIFE.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0..
4 | if not exist Practical-RIFE (
5 | echo git clone https://github.com/hzwer/Practical-RIFE
6 | git clone https://github.com/hzwer/Practical-RIFE
7 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
8 | )
9 | popd rem %~dp0..
10 |
11 | pushd %~dp0..\Practical-RIFE
12 | if not exist venv (
13 | python -m venv venv
14 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
15 | )
16 | call venv\Scripts\activate.bat
17 |
18 | echo pip install Practical-RIFE venv
19 | python -m pip install -q --upgrade pip
20 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
21 |
22 | pip install -q torch==2.0.1+cu118 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
23 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
24 |
25 | @REM pip install numpy>=1.16 tqdm>=4.35.0 sk-video>=1.1.10 opencv-python>=4.1.2 moviepy>=1.0.3
26 | pip install -q tqdm sk-video opencv-python moviepy
27 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
28 |
29 | @REM pip install -q -r requirements.txt
30 | @REM pip install -r requirements.txt
31 | @REM if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
32 |
33 |
34 | @REM numpy>=1.16
35 | @REM tqdm>=4.35.0
36 | @REM sk-video>=1.1.10
37 | @REM torch>=1.3.0
38 | @REM opencv-python>=4.1.2
39 | @REM moviepy>=1.0.3
40 | @REM torchvision==0.7.0
41 |
42 | pip install -q numpy==1.23.5
43 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
44 |
45 | call venv\Scripts\deactivate.bat
46 |
47 |
48 | if not exist train_log (
49 | echo curl https://github.com/hzwer/Practical-RIFE/blob/main/README.md#model-list
50 | curl -Lo RifeModel.zip https://drive.google.com/uc?id=1V6yJsfZgxfx3l03k1sex3YpLdqsJbj61
51 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
52 |
53 | echo Unzip RifeModel.zip
54 | PowerShell -Version 5.1 -ExecutionPolicy Bypass Expand-Archive -Path RifeModel.zip -DestinationPath .
55 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
56 |
57 | echo del RifeModel.zip
58 | del RifeModel.zip
59 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
60 | )
61 | popd rem Practical-RIFE
62 |
--------------------------------------------------------------------------------
/src/Setup-animatediff-cli-prompt-travel.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0..
4 | if not exist animatediff-cli-prompt-travel (
5 | echo git clone https://github.com/s9roll7/animatediff-cli-prompt-travel
6 | git clone https://github.com/s9roll7/animatediff-cli-prompt-travel
7 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
8 | )
9 | popd rem %~dp0..
10 |
11 | pushd %~dp0..\animatediff-cli-prompt-travel
12 | if not exist venv (
13 | python -m venv venv
14 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
15 | )
16 |
17 | call venv\Scripts\activate.bat
18 |
19 | echo pip install animatediff-cli-prompt-travel venv
20 | python -m pip install -q --upgrade pip
21 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
22 |
23 | pip install -q torch==2.1.0+cu121 torchvision==0.16.0+cu121 torchaudio==2.1.0+cu121 --index-url https://download.pytorch.org/whl/cu121
24 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
25 |
26 | pip install -q xformers==0.0.22.post7 --index-url https://download.pytorch.org/whl/cu121
27 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
28 |
29 | pip install -q mediapipe pytorch_lightning
30 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
31 |
32 | pip install -q -e .
33 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
34 |
35 | @REM chcp 65001 rem stylize_mask
36 | pip install -q -e .[stylize]
37 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
38 |
39 | call venv\Scripts\deactivate.bat
40 |
41 | if not exist data\lora ( mkdir data\lora )
42 | if not exist data\vae ( mkdir data\vae )
43 |
44 | if not exist data\models\sd\real_model_N.safetensors (
45 | echo curl https://huggingface.co/fcski/real_model_L
46 | curl -Lo data\models\sd\real_model_N.safetensors^
47 | https://huggingface.co/fcski/real_model_L/resolve/main/real_model_N.safetensors
48 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
49 | timeout /t 1 /nobreak >nul
50 | )
51 |
52 | if not exist data\models\motion-module\mm_sd_v15_v2.ckpt (
53 | echo curl https://huggingface.co/guoyww/animatediff
54 | curl -Lo data\models\motion-module\mm_sd_v15_v2.ckpt^
55 | https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v15_v2.ckpt
56 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
57 | timeout /t 1 /nobreak >nul
58 | )
59 |
60 | if not exist data\vae\kl-f8-anime2.ckpt (
61 | echo curl https://huggingface.co/hakurei/waifu-diffusion-v1-4
62 | curl -Lo data\vae\kl-f8-anime2.ckpt^
63 | https://huggingface.co/hakurei/waifu-diffusion-v1-4/resolve/main/vae/kl-f8-anime2.ckpt
64 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
65 | timeout /t 1 /nobreak >nul
66 | )
67 |
68 | if not exist data\vae\vae-ft-mse-840000-ema-pruned.safetensors (
69 | echo curl https://huggingface.co/stabilityai/sd-vae-ft-mse-original
70 | curl -Lo data\vae\vae-ft-mse-840000-ema-pruned.safetensors^
71 | https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors
72 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
73 | timeout /t 1 /nobreak >nul
74 | )
75 | popd rem %~dp0..\animatediff-cli-prompt-travel
76 |
--------------------------------------------------------------------------------
/src/Setup-editor.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | pushd %~dp0..
3 |
4 | if not exist editor\venv (
5 | python -m venv editor\venv
6 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
7 | )
8 |
9 | call editor\venv\Scripts\activate.bat
10 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
11 |
12 | python -m pip install -q --upgrade pip
13 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
14 |
15 | pip install -q darkdetect tkinterdnd2 nudenet
16 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
17 |
18 | call editor\venv\Scripts\deactivate.bat
19 |
20 | popd rem %~dp0..
21 |
--------------------------------------------------------------------------------
/src/Setup-ffmpeg-master-latest-win64-gpl.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0..
4 | set FFMPEG_DIR=ffmpeg-master-latest-win64-gpl
5 | if not exist %FFMPEG_DIR% (
6 | echo curl -Lo ffmpeg.zip https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip
7 | curl -Lo ffmpeg.zip https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip
8 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
9 |
10 | echo Unzip ffmpeg.zip
11 | PowerShell -Version 5.1 -ExecutionPolicy Bypass Expand-Archive -Path ffmpeg.zip -DestinationPath .
12 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
13 |
14 | echo del ffmpeg.zip
15 | del ffmpeg.zip
16 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
17 | )
18 |
19 | if not exist animatediff-cli-prompt-travel\ffmpeg.exe (
20 | copy /Y %FFMPEG_DIR%\bin\ffmpeg.exe animatediff-cli-prompt-travel
21 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
22 | )
23 |
24 | @REM if not exist ECCV2022-RIFE\ffmpeg.exe (
25 | @REM copy /Y %FFMPEG_DIR%\bin\ffmpeg.exe ECCV2022-RIFE
26 | @REM if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
27 | @REM )
28 | @REM if not exist ECCV2022-RIFE\ffprobe.exe (
29 | @REM copy /Y %FFMPEG_DIR%\bin\ffprobe.exe ECCV2022-RIFE
30 | @REM if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
31 | @REM )
32 |
33 | if not exist Practical-RIFE\ffmpeg.exe (
34 | copy /Y %FFMPEG_DIR%\bin\ffmpeg.exe Practical-RIFE
35 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
36 | )
37 | if not exist Practical-RIFE\ffprobe.exe (
38 | copy /Y %FFMPEG_DIR%\bin\ffprobe.exe Practical-RIFE
39 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
40 | )
41 |
42 | popd rem %~dp0..
43 |
--------------------------------------------------------------------------------
/src/Setup-sd-scripts-accelerate_config.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if not exist %~dp0..\sd-scripts (
3 | echo Not exist sd-scripts. & pause & exit /b 1
4 | )
5 |
6 | pushd %~dp0..\sd-scripts
7 | call venv\Scripts\activate.bat
8 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
9 |
10 | echo ------------------------------------------------------------------------------------------------------------------------
11 | echo This machine [Enter], No distributed training [Enter], NO [Enter], NO [Enter], NO [Enter],
12 | echo all [Enter], bf16(RTX 30X0~) [2][Enter][Enter] fp16(~RTX 20X0) [1][Enter][Enter]
13 | accelerate config
14 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
15 |
16 | call venv\Scripts\deactivate.bat
17 | popd rem %~dp0..\sd-scripts
18 |
--------------------------------------------------------------------------------
/src/Setup-sd-scripts.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | pushd %~dp0..
4 | if not exist sd-scripts (
5 | echo git clone https://github.com/kohya-ss/sd-scripts
6 | git clone https://github.com/kohya-ss/sd-scripts
7 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
8 | )
9 | popd rem %~dp0..
10 |
11 | pushd %~dp0..\sd-scripts
12 | if not exist venv (
13 | python -m venv venv
14 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
15 | )
16 |
17 | call venv\Scripts\activate.bat
18 |
19 | echo pip install sd-scripts venv
20 | python -m pip install -q --upgrade pip
21 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
22 |
23 | pip install -q torch==2.0.1+cu118 torchvision==0.15.2+cu118 --index-url https://download.pytorch.org/whl/cu118
24 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
25 |
26 | pip install -q --upgrade -r requirements.txt
27 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
28 |
29 | pip install -q xformers==0.0.20
30 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
31 |
32 | call venv\Scripts\deactivate.bat
33 |
34 | popd rem %~dp0..\sd-scripts
35 |
--------------------------------------------------------------------------------
/src/Setup.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | call %~dp0Setup-animatediff-cli-prompt-travel.bat
3 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
4 |
5 | @REM call %~dp0Setup-ECCV2022-RIFE.bat
6 | @REM if %errorlevel% neq 0 ( exit /b %errorlevel% )
7 |
8 | call %~dp0Setup-Practical-RIFE.bat
9 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
10 |
11 | call %~dp0Setup-sd-scripts.bat
12 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
13 |
14 | call %~dp0Setup-ffmpeg-master-latest-win64-gpl.bat
15 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
16 |
17 | call %~dp0Setup-editor.bat
18 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
19 |
--------------------------------------------------------------------------------
/src/Update-ECCV2022-RIFE.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | if exist %~dp0..\ECCV2022-RIFE (
4 | pushd %~dp0..\ECCV2022-RIFE
5 | git pull
6 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
7 | popd rem %~dp0..\ECCV2022-RIFE
8 | )
9 |
--------------------------------------------------------------------------------
/src/Update-Practical-RIFE.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | if exist %~dp0..\Practical-RIFE (
4 | pushd %~dp0..\Practical-RIFE
5 | git pull
6 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
7 | popd rem %~dp0..\Practical-RIFE
8 | )
9 |
--------------------------------------------------------------------------------
/src/Update-animatediff-cli-prompt-travel.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | if exist %~dp0..\animatediff-cli-prompt-travel (
4 | pushd %~dp0..\animatediff-cli-prompt-travel
5 | git pull
6 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
7 | popd rem %~dp0..\animatediff-cli-prompt-travel
8 | )
9 |
--------------------------------------------------------------------------------
/src/Update-sd-scripts.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | if exist %~dp0..\sd-scripts (
4 | pushd %~dp0..\sd-scripts
5 | git pull
6 | if %errorlevel% neq 0 ( pause & popd & exit /b %errorlevel% )
7 | popd rem %~dp0..\sd-scripts
8 | )
9 |
--------------------------------------------------------------------------------
/src/Update.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | call "%~dp0Update-animatediff-cli-prompt-travel.bat"
4 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
5 |
6 | @REM call "%~dp0Update-ECCV2022-RIFE.bat"
7 | @REM if %errorlevel% neq 0 ( exit /b %errorlevel% )
8 |
9 | call "%~dp0Update-Practical-RIFE.bat"
10 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
11 |
12 | call "%~dp0Update-sd-scripts.bat"
13 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
14 |
15 | call "%~dp0Setup.bat"
16 | if %errorlevel% neq 0 ( exit /b %errorlevel% )
17 |
--------------------------------------------------------------------------------