├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── Language_FramePack-eichi.bat ├── README.md ├── README ├── README_changelog.md ├── README_column.md ├── README_column_en.md ├── README_column_ru.md ├── README_column_zh.md ├── README_en.md ├── README_ru.md ├── README_setup.md ├── README_setup_en.md ├── README_setup_ru.md ├── README_setup_zh.md ├── README_userguide.md ├── README_userguide_en.md ├── README_userguide_ru.md ├── README_userguide_zh.md └── README_zh.md ├── docker-compose.yml ├── images ├── framepack_eichi_error_screenshot1.png ├── framepack_eichi_screenshot1.png ├── framepack_eichi_screenshot2.png ├── framepack_eichi_screenshot3.png └── framepack_oichi_screenshot.png ├── linux ├── README_linux.md ├── README_linux_en.md ├── README_linux_ru.md ├── README_linux_zh-tw.md ├── install_linux.sh ├── run_endframe_ichi.sh ├── run_endframe_ichi_en.sh ├── run_endframe_ichi_en_f1.sh ├── run_endframe_ichi_f1.sh ├── run_endframe_ichi_f1_ru.sh ├── run_endframe_ichi_ru.sh ├── run_endframe_ichi_zh-tw.sh ├── run_endframe_ichi_zh-tw_f1.sh ├── run_oneframe_ichi.sh ├── run_oneframe_ichi_en.sh ├── run_oneframe_ichi_ru.sh ├── run_oneframe_ichi_zh-tw.sh ├── setup_submodule.sh └── update.sh ├── run_endframe_ichi.bat ├── run_endframe_ichi_en.bat ├── run_endframe_ichi_en_f1.bat ├── run_endframe_ichi_f1.bat ├── run_endframe_ichi_ru.bat ├── run_endframe_ichi_ru_f1.bat ├── run_endframe_ichi_zh-tw.bat ├── run_endframe_ichi_zh-tw_f1.bat ├── run_oneframe_ichi.bat ├── run_oneframe_ichi_en.bat ├── run_oneframe_ichi_ru.bat ├── run_oneframe_ichi_zh-tw.bat ├── version └── v1.9.3 │ ├── Dockerfile │ ├── docker-compose.yml │ ├── run_endframe_ichi.bat │ ├── run_endframe_ichi_en.bat │ ├── run_endframe_ichi_en_f1.bat │ ├── run_endframe_ichi_f1.bat │ ├── run_endframe_ichi_zh-tw.bat │ ├── run_endframe_ichi_zh-tw_f1.bat │ ├── run_oneframe_ichi.bat │ ├── run_oneframe_ichi_en.bat │ ├── run_oneframe_ichi_zh-tw.bat │ └── webui │ ├── diffusers_helper │ ├── bucket_tools.py │ └── memory.py │ ├── eichi_utils │ ├── __init__.py │ ├── frame_calculator.py │ ├── keyframe_handler.py │ ├── keyframe_handler_extended.py │ ├── lora_preset_manager.py │ ├── model_downloader.py │ ├── png_metadata.py │ ├── preset_manager.py │ ├── section_manager.py │ ├── settings_manager.py │ ├── tensor_combiner.py │ ├── text_encoder_manager.py │ ├── transformer_manager.py │ ├── ui_styles.py │ ├── vae_cache.py │ ├── vae_settings.py │ └── video_mode_settings.py │ ├── endframe_ichi.py │ ├── endframe_ichi_f1.py │ ├── locales │ ├── en.json │ ├── i18n.py │ ├── i18n_extended.py │ ├── ja.json │ ├── ru.json │ └── zh-tw.json │ ├── lora_utils │ ├── __init__.py │ ├── dynamic_swap_lora.py │ ├── fp8_optimization_utils.py │ ├── lora_check_helper.py │ ├── lora_loader.py │ ├── lora_utils.py │ └── safetensors_utils.py │ └── oneframe_ichi.py └── webui ├── diffusers_helper ├── bucket_tools.py └── memory.py ├── eichi_utils ├── __init__.py ├── combine_mode.py ├── config_queue_manager.py ├── frame_calculator.py ├── keyframe_handler.py ├── keyframe_handler_extended.py ├── log_manager.py ├── lora_preset_manager.py ├── model_downloader.py ├── png_metadata.py ├── preset_manager.py ├── section_manager.py ├── settings_manager.py ├── tensor_combiner.py ├── tensor_processing.py ├── tensor_tool.py ├── text_encoder_manager.py ├── transformer_manager.py ├── ui_styles.py ├── vae_cache.py ├── vae_settings.py └── video_mode_settings.py ├── endframe_ichi.py ├── endframe_ichi_f1.py ├── locales ├── en.json ├── i18n.py ├── i18n_extended.py ├── ja.json ├── ru.json └── zh-tw.json ├── lora_utils ├── __init__.py ├── dynamic_swap_lora.py ├── fp8_optimization_utils.py ├── lora_check_helper.py ├── lora_loader.py ├── lora_utils.py └── safetensors_utils.py └── oneframe_ichi.py /.gitignore: -------------------------------------------------------------------------------- 1 | webui/submodules 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "webui/submodules/FramePack"] 2 | path = webui/submodules/FramePack 3 | url = https://github.com/lllyasviel/FramePack 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for FramePack-eichi 2 | FROM nvidia/cuda:12.6.0-devel-ubuntu22.04 3 | 4 | # Set environment variables 5 | ENV DEBIAN_FRONTEND=noninteractive \ 6 | PYTHONUNBUFFERED=1 7 | 8 | # Install system dependencies 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | git \ 11 | python3.10 \ 12 | python3.10-dev \ 13 | python3-pip \ 14 | python3-venv \ 15 | ffmpeg \ 16 | libsm6 \ 17 | libxext6 \ 18 | libgl1 \ 19 | build-essential \ 20 | wget \ 21 | curl \ 22 | && apt-get clean \ 23 | && rm -rf /var/lib/apt/lists/* 24 | 25 | # Create symbolic links for Python 26 | RUN ln -sf /usr/bin/python3.10 /usr/bin/python && \ 27 | ln -sf /usr/bin/pip3 /usr/bin/pip 28 | 29 | # Upgrade pip and install build dependencies 30 | RUN pip install --upgrade pip 31 | RUN pip install packaging wheel setuptools ninja 32 | 33 | # Install PyTorch with CUDA 12.6 support 34 | RUN pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu126 35 | 36 | # Install acceleration libraries one by one to better diagnose any issues 37 | RUN pip install xformers==0.0.29.post3 --no-deps --index-url https://download.pytorch.org/whl/cu126 --no-cache-dir 38 | RUN pip install triton==2.2.0 --no-cache-dir 39 | RUN pip install -U "huggingface_hub[cli]" 40 | 41 | # Install flash-attention with the closest available version 42 | RUN pip install flash-attn==2.7.4.post1 43 | 44 | # Try installing sageattention 45 | # Option 1: Install from git repository directly 46 | RUN pip install sageattention 47 | 48 | # Install remaining packages 49 | RUN pip install pynvml "jinja2>=3.1.2" peft 50 | 51 | # Create working directory 52 | WORKDIR /app 53 | 54 | # Clone original FramePack repository (needed for the base functionality) 55 | RUN git clone https://github.com/lllyasviel/FramePack.git /app/framepack 56 | 57 | # Install dependencies 58 | WORKDIR /app/framepack 59 | RUN pip install -r requirements.txt 60 | 61 | # Clone FramePack-eichi repository 62 | RUN git clone https://github.com/git-ai-code/FramePack-eichi /tmp/framepack-eichi 63 | 64 | # Copy FramePack-eichi files 65 | RUN cp -rf /tmp/framepack-eichi/webui/* /app/framepack/ 66 | 67 | # Create a simple startup script with better error handling 68 | RUN echo '#!/bin/bash' > /app/start.sh && \ 69 | echo 'cd /app/framepack' >> /app/start.sh && \ 70 | echo 'echo "Starting demo_gradio.py..."' >> /app/start.sh && \ 71 | echo 'python demo_gradio.py --server 0.0.0.0 --port 7860 &' >> /app/start.sh && \ 72 | echo 'SERVER_PID=$!' >> /app/start.sh && \ 73 | echo 'sleep 5' >> /app/start.sh && \ 74 | echo 'if [ -f "endframe_ichi.py" ]; then' >> /app/start.sh && \ 75 | echo ' echo "Starting endframe_ichi.py..."' >> /app/start.sh && \ 76 | echo ' python endframe_ichi.py --server 0.0.0.0 --port 7861 "$@"' >> /app/start.sh && \ 77 | echo 'fi' >> /app/start.sh && \ 78 | chmod +x /app/start.sh 79 | 80 | # Set working directory for when container starts 81 | WORKDIR /app/framepack 82 | 83 | # Command to run when container starts 84 | ENTRYPOINT ["/app/start.sh"] 85 | 86 | # Default arguments (can be overridden) 87 | CMD ["--lang", "en", "--listen"] -------------------------------------------------------------------------------- /Language_FramePack-eichi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :SEL 3 | cls 4 | echo. 1 Japan (ja)------------------------FramePack-Eichi_endframe_ichi 5 | echo. 2 English (en)----------------------FramePack-Eichi_endframe_ichi 6 | echo. 3 Traditional Chinese (Zh-TW)-------FramePack-Eichi_endframe_ichi 7 | echo. 4 Russian (ru)----------------------FramePack-Eichi_endframe_ichi 8 | echo. 9 | echo. 10 Japan (ja)-----------------------FramePack-Eichi_endframe_ichi_f1 10 | echo. 11 English (en)---------------------FramePack-Eichi_endframe_ichi_f1 11 | echo. 12 Traditional Chinese (Zh-TW)------FramePack-Eichi_endframe_ichi_f1 12 | echo. 13 Russian (ru)---------------------FramePack-Eichi_endframe_ichi_f1 13 | echo. 14 | echo. 20 Japan (ja)-----------------------FramePack-Eichi_oneframe_ichi 15 | echo. 21 English (en)---------------------FramePack-Eichi_oneframe_ichi 16 | echo. 22 Traditional Chinese (Zh-TW)------FramePack-Eichi_oneframe_ichi 17 | echo. 23 Russian (ru)---------------------FramePack-Eichi_oneframe_ichi 18 | echo. 19 | echo. 30 FramePack------------------------Original FramePack 20 | echo. 31 FramePack-F1---------------------Original FramePack 21 | echo. 22 | echo. 99 Go to Official FramePack 23 | echo. 00 Go to Official FramePack-Eichi 24 | echo. 25 | set /p Type=Please select language (number): 26 | if "%Type%"=="1" goto JP-1 27 | if "%Type%"=="2" goto EN-2 28 | if "%Type%"=="3" goto TW-3 29 | if "%Type%"=="4" goto RU-4 30 | if "%Type%"=="10" goto JP-10 31 | if "%Type%"=="11" goto EN-11 32 | if "%Type%"=="12" goto TW-12 33 | if "%Type%"=="13" goto RU-13 34 | if "%Type%"=="20" goto JP-20 35 | if "%Type%"=="21" goto EN-21 36 | if "%Type%"=="22" goto TW-22 37 | if "%Type%"=="23" goto RU-23 38 | if "%Type%"=="30" goto FP 39 | if "%Type%"=="31" goto FPF1 40 | if "%Type%"=="99" goto FPO 41 | if "%Type%"=="00" goto FPEO 42 | if "%Type%"=="" goto PP 43 | 44 | :JP-1 45 | cls 46 | @echo EndFrame_Ichi_Japan Language... 47 | call environment.bat 48 | cd %~dp0webui 49 | python.exe endframe_ichi.py --server 127.0.0.1 --inbrowser 50 | goto PP 51 | 52 | :EN-2 53 | cls 54 | @echo EndFrame_Ichi_EnglishJapan Language... 55 | call environment.bat 56 | cd %~dp0webui 57 | python.exe endframe_ichi.py --server 127.0.0.1 --inbrowser --lang en 58 | goto PP 59 | 60 | :TW-3 61 | cls 62 | @echo EndFrame_Ichi_Traditional Chinese Language... 63 | call environment.bat 64 | cd %~dp0webui 65 | python.exe endframe_ichi.py --server 127.0.0.1 --inbrowser --lang zh-tw 66 | goto PP 67 | 68 | :RU-4 69 | cls 70 | @echo EndFrame_Ichi_Russian Language... 71 | call environment.bat 72 | cd %~dp0webui 73 | python.exe endframe_ichi.py --server 127.0.0.1 --inbrowser --lang ru 74 | goto PP 75 | 76 | :JP-10 77 | cls 78 | @echo EndFrame_Ichi_Japan Language... 79 | call environment.bat 80 | cd %~dp0webui 81 | python.exe endframe_ichi_f1.py --server 127.0.0.1 --inbrowser 82 | goto PP 83 | 84 | :EN-11 85 | cls 86 | @echo EndFrame_Ichi_EnglishJapan Language... 87 | call environment.bat 88 | cd %~dp0webui 89 | python.exe endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang en 90 | goto PP 91 | 92 | :TW-12 93 | cls 94 | @echo EndFrame_Ichi_Traditional Chinese Language... 95 | call environment.bat 96 | cd %~dp0webui 97 | python.exe endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang zh-tw 98 | goto PP 99 | 100 | :RU-13 101 | cls 102 | @echo EndFrame_Ichi_Russian Language... 103 | call environment.bat 104 | cd %~dp0webui 105 | python.exe endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang ru 106 | goto PP 107 | 108 | :JP-20 109 | cls 110 | @echo EndFrame_Ichi_Japan Language... 111 | call environment.bat 112 | cd %~dp0webui 113 | python.exe oneframe_ichi.py --server 127.0.0.1 --inbrowser 114 | goto PP 115 | 116 | :EN-21 117 | cls 118 | @echo EndFrame_Ichi_EnglishJapan Language... 119 | call environment.bat 120 | cd %~dp0webui 121 | python.exe oneframe_ichi.py --server 127.0.0.1 --inbrowser --lang en 122 | goto PP 123 | 124 | :TW-22 125 | cls 126 | @echo EndFrame_Ichi_Traditional Chinese Language... 127 | call environment.bat 128 | cd %~dp0webui 129 | python.exe oneframe_ichi.py --server 127.0.0.1 --inbrowser --lang zh-tw 130 | goto PP 131 | 132 | :RU-23 133 | cls 134 | @echo EndFrame_Ichi_Russian Language... 135 | call environment.bat 136 | cd %~dp0webui 137 | python.exe oneframe_ichi.py --server 127.0.0.1 --inbrowser --lang ru 138 | goto PP 139 | 140 | :FP 141 | cls 142 | @echo FramePack... 143 | call environment.bat 144 | cd %~dp0webui 145 | python.exe demo_gradio.py --server 127.0.0.1 --inbrowser 146 | goto PP 147 | 148 | :FPF1 149 | cls 150 | @echo FramePack F1... 151 | call environment.bat 152 | cd %~dp0webui 153 | python.exe demo_gradio_f1.py --server 127.0.0.1 --inbrowser 154 | goto PP 155 | 156 | :FPO 157 | cls 158 | @echo Go to FramePack Official... 159 | Start https://github.com/lllyasviel/FramePack 160 | goto PP 161 | 162 | :FPEO 163 | cls 164 | @echo Go to FramePack Official... 165 | Start https://github.com/git-ai-code/FramePack-eichi 166 | goto PP 167 | 168 | :PP 169 | pause -------------------------------------------------------------------------------- /README/README_userguide_zh.md: -------------------------------------------------------------------------------- 1 | # FramePack-eichi 使用指南 | [日本語](README_userguide.md) | [English](README_userguide_en.md) | [Русский](README_userguide_ru.md) 2 | 3 | 本指南詳細介紹了FramePack-eichi標準版和F1版的使用方法。這兩個版本各有不同的特點和用途,請根據您的目的選擇最適合的模型。有關基本信息和安裝方法,請參閱[README](README_zh.md);有關詳細設置,請參閱[擴充文檔](README_column_zh.md);更新歷史請參閱[更新日誌](README_changelog.md)。 4 | 5 | ## 目錄 6 | 7 | - [什麼是FramePack-eichi](#什麼是framepack-eichi) 8 | - [標準版與F1版的區別](#標準版與f1版的區別) 9 | - [標準版的使用方法](#標準版的使用方法) 10 | - [F1版的使用方法](#f1版的使用方法) 11 | - [oichi版(單幀版)的使用方法](#oichi版單幀版的使用方法) 12 | - [各設置項目的詳細說明](#各設置項目的詳細說明) 13 | - [故障排除](#故障排除) 14 | 15 | ## 什麼是FramePack-eichi 16 | 17 | FramePack-eichi是一款從圖像生成視頻的AI工具。它實現了傳統圖像生成AI無法表達的「動作」。您可以從靜態圖像生成短時間視頻,或基於多個關鍵幀創建動畫。 18 | 19 | 此工具有兩個版本: 20 | 21 | 1. **標準版 (Standard)**: 專注於關鍵幀控制的模型 22 | 2. **F1版 (F1)**: 專注於正向生成的模型 23 | 24 | ## 標準版與F1版的區別 25 | 26 | |功能|標準版 (Standard)|F1版 (F1)| 27 | |---|---|---| 28 | |**基本概念**|通過關鍵幀控制精確指定動作|通過正向生成產生自然活潑的動作| 29 | |**關鍵幀**|可以指定多個區段圖像|僅使用第一張圖像| 30 | |**最終幀**|可以明確指定|僅自動生成| 31 | |**Image影響度**|1.0~100.0%|100.0~102.0%| 32 | |**適用場景**|當您想控制精確動作時|當您想生成自然不可預測的動作時| 33 | |**自定義性**|高(對每個區段進行詳細控制)|低(指定整體氛圍)| 34 | |**學習曲線**|較高(需要理解多個參數)|低(操作簡單)| 35 | 36 | ## 標準版的使用方法 37 | 38 | ### 1. 啟動方式 39 | 40 | 要使用標準版,請執行以下批處理文件之一: 41 | 42 | - 日文版: `run_endframe_ichi.bat` 43 | - 英文版: `run_endframe_ichi_en.bat` 44 | - 中文版: `run_endframe_ichi_zh-tw.bat` 45 | - 俄文版: `run_endframe_ichi_ru.bat` 46 | 47 | ### 2. 基本使用方法 48 | 49 | 1. **選擇視頻模式**: 50 | - 從右上角的「視頻模式」下拉菜單中選擇所需的秒數模式 51 | - 標準版可用模式:1-20秒、30秒、40秒 ※v1.9.3擴展 52 | - 模式決定了區段數量(例如:「4秒」為4個區段) 53 | 54 | 2. **設置關鍵幀圖像**: 55 | - 選擇「0」到「區段數-1」的標籤來為每個區段設置圖像 56 | - 點擊「Image」按鈕上傳圖像(或使用「Clipboard」從剪貼板粘貼) 57 | - 查看紅色/藍色框架以了解圖像作為關鍵幀的角色(指示複製到偶數/奇數區段) 58 | 59 | 3. **最終幀(可選)**: 60 | - 選擇「Final」標籤 61 | - 點擊「Image」按鈕上傳最終幀圖像(或從剪貼板粘貼) 62 | 63 | 4. **設置提示詞**: 64 | - 您可以為每個區段設置提示詞(區段標籤下的文本框) 65 | - 在「Base」標籤中設置所有區段共用的基礎提示詞 66 | - 使用「負面提示詞」排除不需要的元素 67 | 68 | 5. **詳細設置**: 69 | - 「EndFrame影響度」: 調整最終幀的影響力(0.01至1.00) 70 | - 「關鍵幀複製」: 打開/關閉自動複製功能 71 | - 「Seed」: 設置隨機種子值(-1為隨機) 72 | 73 | 6. **LoRA設置(可選)**: 74 | - 應用LoRA模型調整表現 75 | - 在「path1:」下輸入.safetensors文件的路徑 76 | - 在「weight1:」中設置0.0至1.0之間的權重 77 | - **預設管理器 ※v1.9.3新功能**: 78 | - 保存和管理最多5個預設 79 | - 從UI輕鬆切換預設 80 | - 批量保存LoRA文件、強度和其他設置 81 | 82 | 7. **視頻生成**: 83 | - 完成所有設置後點擊「Start」按鈕 84 | - 生成過程完成後,視頻將保存到輸出文件夾並自動播放 85 | 86 | ### 3. 高級使用方法 87 | 88 | #### 循環功能使用和限制 89 | 90 | FramePack-eichi的標準版包含「循環模式」,但存在以下限制: 91 | 92 | ##### 循環功能限制 93 | - **技術限制**: 由於AI影片生成模型的特性,創建完全無縫的循環在技術上具有挑戰性 94 | - **幀間微細變動**: AI生成幀的微細差異可能導致循環接合處出現不自然現象 95 | - **時間一致性限制**: 基礎的Hunyuan Video模型在時間一致性方面存在限制 96 | 97 | ##### 推薦替代解決方案 98 | 對於需要高品質和精確循環的用戶,我們推薦使用專為循環生成設計的 **[FramePackLooP](https://github.com/red-polo/FramePackLoop)**。 99 | 100 | ##### 未來改進計劃 101 | 我們正在通過張量拼接工具研究改進,並調查這些技術是否可以在未來版本中應用於循環功能。 102 | 103 | #### 批量區段信息添加功能 104 | 105 | - 您可以使用zip文件一次性設置多個區段信息 106 | - zip文件結構: 107 | ``` 108 | zipname.zip 109 | ├── anydir/ (僅允許一個文件夾層級或無文件夾) 110 | │ ├── sections.yaml (必需) 111 | │ ├── start.png (起始幀,可選。文件名必須以"start*"開頭) 112 | │ ├── end.png (最終幀,可選。文件名必須以"end*"開頭) 113 | │ ├── 000.png (區段0圖像。文件名必須以3位數字開頭) 114 | │ ├── 001.png (區段1圖像。文件名必須以3位數字開頭) 115 | ``` 116 | - sections.yaml格式: 117 | ```yaml 118 | section_info: 119 | - section: 0 120 | prompt: "角色伸出手" 121 | - section: 1 122 | prompt: "角色轉向我們" 123 | ``` 124 | - 上傳後,區段圖像和提示詞將自動設置 125 | 126 | #### 利用關鍵幀複製功能 127 | 128 | - 當「關鍵幀複製」開啟時,區段0(紅框)的圖像會複製到所有偶數區段(0, 2, 4...),區段1(藍框)的圖像會複製到所有奇數區段(1, 3, 5...) 129 | - 這允許您通過最少的圖像設置高效生成視頻 130 | - 對於長視頻模式(10秒以上)特別有效 131 | 132 | #### 區段專屬提示詞設置 133 | 134 | - 每個區段的提示詞決定了該區段的特性 135 | - 由於它與基礎提示詞結合使用,僅描述區段特有的特徵即可 136 | - 包含表示動作變化的詞語(例如「走路」→「跳躍」→「坐下」)效果顯著 137 | 138 | #### 調整EndFrame影響度 139 | 140 | - 高值(接近1.00): 對最終幀的收斂強烈,趨向於產生線性動作 141 | - 低值(接近0.01): 對最終幀的收斂較弱,趨向於產生更自由的動作 142 | - 對於自然動作,建議使用0.1至0.3左右的值 143 | 144 | ## F1版的使用方法 145 | 146 | ### 1. 啟動方式 147 | 148 | 要使用F1版,請執行以下批處理文件之一: 149 | 150 | - 日文版: `run_endframe_ichi_f1.bat` 151 | - 英文版: `run_endframe_ichi_en_f1.bat` 152 | - 中文版: `run_endframe_ichi_zh-tw_f1.bat` 153 | - 俄文版: `run_endframe_ichi_f1_ru.bat` 154 | 155 | ### 2. 基本使用方法 156 | 157 | 1. **選擇視頻模式**: 158 | - 從右上角的「視頻模式」下拉菜單中選擇所需的秒數模式 159 | - F1版可用模式:1-20秒、30秒、40秒、60秒、120秒 ※v1.9.3擴展 160 | - F1版僅使用第一張圖像作為區段圖像 161 | 162 | 2. **設置初始圖像**: 163 | - 點擊「Image」按鈕上傳圖像(或使用「Clipboard」從剪貼板粘貼) 164 | - 此圖像將成為視頻的第一幀,後續幀將自動生成 165 | 166 | 3. **設置提示詞**: 167 | - 在「Base」標籤中設置提示詞(F1版僅使用此提示詞) 168 | - 使用「負面提示詞」排除不需要的元素 169 | 170 | 4. **詳細設置**: 171 | - 「Image影響度」: 調整初始圖像的影響力(100.0至102.0%) 172 | - 「Seed」: 設置隨機種子值(-1為隨機) 173 | 174 | 5. **LoRA設置(可選)**: 175 | - 應用LoRA模型調整表現 176 | - 在「path1:」下輸入.safetensors文件的路徑 177 | - 在「weight1:」中設置0.0至1.0之間的權重 178 | - **預設管理器 ※v1.9.3新功能**: 179 | - 保存和管理最多5個預設 180 | - 從UI輕鬆切換預設 181 | - 批量保存LoRA文件、強度和其他設置 182 | 183 | 6. **視頻生成**: 184 | - 完成所有設置後點擊「Start」按鈕 185 | - 生成過程完成後,視頻將保存到輸出文件夾並自動播放 186 | 187 | ### 3. F1版技巧 188 | 189 | #### 適當調整Image影響度 190 | 191 | - **100.0%**: 生成非常忠實於初始圖像的視頻。角色和構圖變化不大,提供最大穩定性。 192 | - **100.5%**: 平衡的設置,兼具一定變化和穩定性。 193 | - **101.0至102.0%**: 產生更大膽的動作和變化,但可能偏離初始圖像。 194 | 195 | #### 通過提示詞引導動作 196 | 197 | - 使用動詞和動作詞來引導特定動作 198 | - 例如: "walking", "running", "jumping", "dancing"等 199 | - 不僅可以指定角色動作,還可以指定鏡頭工作 200 | - 例如: "zoom in", "pan left", "closeup"等 201 | 202 | #### 了解F1版特性 203 | 204 | - 新的F1版是"FramePack_F1_I2V_HY_20250503",專注於正向生成的模型 205 | - 與標準版不同,您無法指定精細動作,但可以產生更自然且不可預測的動作 206 | - 有效的使用方式是通過提示詞指定大致方向,將細節交給AI處理 207 | 208 | ## oichi版(單幀版)的使用方法 209 | 210 | ### 1. 概述 211 | 212 | oichi版是從單一圖像生成下一幀的特殊版本。它專注於預測一幀,而不是生成整個視頻,實現更輕量更快速的處理。在v1.9.3中,新增了kisekaeichi功能。 213 | 214 | ### 2. 啟動方式 215 | 216 | 要使用oichi版,請執行以下批處理文件之一: 217 | 218 | - 日文版: `run_oneframe_ichi.bat` 219 | - 英文版: `run_oneframe_ichi_en.bat` 220 | - 中文版: `run_oneframe_ichi_zh-tw.bat` 221 | - 俄文版: `run_oneframe_ichi_ru.bat` 222 | 223 | ### 3. 基本使用方法 224 | 225 | 1. **圖像設置**: 226 | - 上傳單一圖像 227 | - 從此圖像生成下一幀 228 | 229 | 2. **提示詞設置**: 230 | - 描述您想要生成的下一幀內容 231 | - 詳細指定動作或變化 232 | 233 | 3. **kisekaeichi功能 ※v1.9.3新功能**: 234 | - furusu構思、Kohya實現的使用參考圖像的技術 235 | - 使用目標索引和歷史索引進行精確控制 236 | - 僅更改特定區域的遮罩功能 237 | - 用於應用不同服裝或風格時使用 238 | 239 | 4. **執行生成**: 240 | - 點擊「Generate」按鈕 241 | - 生成下一幀 242 | 243 | ### 4. kisekaeichi功能詳情 244 | 245 | - **參考圖像設置**: 設置您想要更改的風格或服裝的參考圖像 246 | - **遮罩功能**: 指定要更改的區域(可進行整體或部分更改) 247 | - **強度調整**: 調整參考圖像的影響度 248 | - **注意事項**: 作為實驗性功能,結果高度依賴於輸入圖像和參考圖像 249 | 250 | ## 各設置項目的詳細說明 251 | 252 | ### 共通設置 253 | 254 | #### 提示詞相關 255 | - **提示詞**: 指定生成視頻內容的文本。詳細描述您想要的元素和特徵。 256 | - **負面提示詞**: 指定要避免的元素的文本。包含"low quality", "blurry"等術語可提高質量。 257 | 258 | #### 技術設置 259 | - **Seed**: 隨機種子值。使用相同的值會產生相似的結果。使用"-1"表示隨機。 260 | - **FP8優化**: 在應用LoRA時減少VRAM使用量的選項。在RTX 40系列GPU上特別有效。 261 | - **縮放處理**: 指定如何調整視頻分辨率。 262 | - **輸出文件夾**: 生成文件的保存目的地。 263 | - **MP4質量**: 設置視頻壓縮率(0-100,數值越低質量越高,數值越高體積越小)。 264 | 265 | #### LoRA設置 266 | - **LoRA路徑**: 要使用的LoRA模型的文件路徑。 267 | - **LoRA權重**: 每個LoRA模型的影響度(0.0至1.0)。 268 | 269 | ### 標準版特有設置 270 | 271 | - **關鍵幀複製**: 開啟時,按特定模式自動複製圖像。 272 | - **EndFrame影響度**: 調整最終幀的影響力(0.01至1.00)。 273 | - **區段標籤 (0, 1, 2...)**: 每個關鍵幀的設置界面。 274 | - **Final標籤**: 最終幀的設置界面。 275 | 276 | ### F1版特有設置 277 | 278 | - **Image影響度**: 初始圖像的影響力(100.0至102.0%) 279 | - 100.0%: 非常忠實於初始圖像 280 | - 100.5%: 平衡的變化 281 | - 101.0至102.0%: 更大膽的變化和動作 282 | 283 | ## 故障排除 284 | 285 | ### 常見問題 286 | 287 | #### 生成黑屏 288 | - **原因**: GPU VRAM不足,批處理大小或進程衝突 289 | - **解決方案**: 290 | - 終止其他GPU進程 291 | - 開啟FP8優化 292 | - 將MP4質量值設為約16 293 | - 使用較小的視頻模式(1秒、2秒等) 294 | 295 | #### 錯誤: CUDA out of memory 296 | - **原因**: GPU VRAM不足 297 | - **解決方案**: 298 | - 開啟FP8優化 299 | - 使用較小的視頻模式 300 | - 終止其他GPU進程 301 | - 使用較低分辨率的圖像 302 | 303 | #### 提示詞未正確反映 304 | - **原因**: 提示詞優先級或表達不明確 305 | - **解決方案**: 306 | - 將重要元素放在開頭 307 | - 使用括號或強調語法(例如,(important element)) 308 | - 使用更具體的詞語 309 | - 對於標準版,檢查區段專屬提示詞和基礎提示詞的組合 310 | 311 | ### 標準版特有問題 312 | 313 | #### 區段間變化不自然 314 | - **原因**: 關鍵幀之間的差距過大 315 | - **解決方案**: 316 | - 使用具有更相似特徵的關鍵幀圖像 317 | - 將變化逐漸分散到多個區段 318 | - 調整EndFrame影響度 319 | 320 | #### 關鍵幀被忽略 321 | - **原因**: Image影響度過低 322 | - **解決方案**: 323 | - 增加Image影響度(建議50%以上) 324 | - 在提示詞中強調關鍵幀特徵 325 | 326 | ### F1版特有問題 327 | 328 | #### 與初始圖像偏差過大 329 | - **原因**: Image影響度過高 330 | - **解決方案**: 331 | - 降低Image影響度(建議100.0至100.5%) 332 | - 在提示詞中更具體地描述 333 | 334 | #### 動作少/單調 335 | - **原因**: 提示詞中缺乏表示動作的表達 336 | - **解決方案**: 337 | - 添加動詞和動作詞 338 | - 增加Image影響度(101.0至102.0%) 339 | - 選擇較長的視頻模式 -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | framepack-eichi: 3 | build: 4 | context: . 5 | dockerfile: Dockerfile 6 | args: 7 | - TZ=Europe/Brussels 8 | ports: 9 | - "7861:7861" 10 | volumes: 11 | - ./data:/app/framepack/data 12 | - ./models:/app/framepack/hf_download 13 | - ./outputs:/app/framepack/outputs 14 | environment: 15 | - NVIDIA_VISIBLE_DEVICES=all 16 | - NVIDIA_DRIVER_CAPABILITIES=all 17 | deploy: 18 | resources: 19 | reservations: 20 | devices: 21 | - driver: nvidia 22 | count: 1 23 | capabilities: [gpu] 24 | restart: unless-stopped 25 | command: ["--lang", "en"] # Options: "en", "ja", "zh-tw" 26 | -------------------------------------------------------------------------------- /images/framepack_eichi_error_screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-ai-code/FramePack-eichi/3ab81148f3c44bf65b4bc0a1c2e0621f06a72d20/images/framepack_eichi_error_screenshot1.png -------------------------------------------------------------------------------- /images/framepack_eichi_screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-ai-code/FramePack-eichi/3ab81148f3c44bf65b4bc0a1c2e0621f06a72d20/images/framepack_eichi_screenshot1.png -------------------------------------------------------------------------------- /images/framepack_eichi_screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-ai-code/FramePack-eichi/3ab81148f3c44bf65b4bc0a1c2e0621f06a72d20/images/framepack_eichi_screenshot2.png -------------------------------------------------------------------------------- /images/framepack_eichi_screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-ai-code/FramePack-eichi/3ab81148f3c44bf65b4bc0a1c2e0621f06a72d20/images/framepack_eichi_screenshot3.png -------------------------------------------------------------------------------- /images/framepack_oichi_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-ai-code/FramePack-eichi/3ab81148f3c44bf65b4bc0a1c2e0621f06a72d20/images/framepack_oichi_screenshot.png -------------------------------------------------------------------------------- /linux/README_linux.md: -------------------------------------------------------------------------------- 1 | # FramePack-eichi Linuxサポート (非公式) 2 | 3 | このディレクトリには、FramePack-eichiをLinux環境で使用するための非公式サポートスクリプトが含まれています。これらのスクリプトは便宜上提供されるものであり、**公式サポート対象外**です。ご利用は自己責任でお願いします。 4 | 5 | ## システム要件 6 | 7 | - **OS**: Ubuntu 22.04 LTS以降が推奨(他のPython 3.10対応ディストリビューションも動作可能) 8 | - **CPU**: 8コア以上の最新のマルチコアCPU推奨 9 | - **RAM**: 最小16GB、推奨32GB以上(複雑な処理や高解像度には64GB推奨) 10 | - **GPU**: NVIDIA RTX 30XX/40XX/50XX シリーズ(8GB VRAM以上) 11 | - **VRAM**: 最小8GB(推奨12GB以上) 12 | - **ストレージ**: 150GB以上の空き容量(SSD推奨) 13 | - **必須ソフトウェア**: 14 | - CUDA Toolkit 12.6 15 | - Python 3.10.x 16 | - CUDAサポート付きPyTorch 2.6 17 | 18 | ## 含まれるスクリプト 19 | 20 | - `update.sh` - 本家リポジトリの更新とFramePack-eichiファイルの上書き適用を行うスクリプト 21 | - `setup_submodule.sh` - 初回セットアップ用スクリプト 22 | - `install_linux.sh` - Linux向け簡易インストーラー 23 | - `run_endframe_ichi.sh` - 無印版/日本語実行スクリプト 24 | - `run_endframe_ichi_f1.sh` - F1版/日本語実行スクリプト 25 | - `run_oneframe_ichi.sh` - 1フレーム推論版/日本語実行スクリプト 26 | - その他言語版実行スクリプト 27 | 28 | ## Linuxセットアップ手順(サブモジュール) 29 | 30 | ### 1. 前提条件のインストール 31 | 32 | ```bash 33 | # システムパッケージの更新 34 | sudo apt update && sudo apt upgrade -y 35 | 36 | # 基本的な開発ツールとライブラリのインストール 37 | sudo apt install -y git wget ffmpeg libavformat-dev libavdevice-dev libavfilter-dev libswscale-dev libopenblas-dev 38 | 39 | # CUDA Toolkit 12.6のインストール 40 | # 注: NVIDIAの公式手順に従ってCUDA Toolkitをインストールしてください 41 | # https://developer.nvidia.com/cuda-12-6-0-download-archive 42 | 43 | # Python 3.10のインストール 44 | sudo apt install -y python3.10 python3.10-venv python3-pip 45 | ``` 46 | 47 | ### 2. FramePack-eichiのクローンとセットアップ 48 | 49 | ```bash 50 | # FramePack-eichiリポジトリのクローン 51 | git clone https://github.com/git-ai-code/FramePack-eichi.git 52 | cd FramePack-eichi 53 | 54 | # 仮想環境の作成と有効化 55 | python3.10 -m venv venv 56 | source venv/bin/activate 57 | 58 | # サブモジュールのセットアップ (本家FramePackを自動的にダウンロード) 59 | ./linux/setup_submodule.sh 60 | 61 | # CUDAサポート付きPyTorchと依存関係のインストール 62 | cd webui/submodules/FramePack 63 | pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126 64 | pip install -r requirements.txt 65 | ``` 66 | 67 | ### 3. FramePack-eichiの起動 68 | 69 | ```bash 70 | # FramePack-eichiのルートディレクトリに戻る 71 | cd ~/FramePack-eichi # インストール先に合わせてパスを調整してください 72 | 73 | # 実行スクリプトを使用して起動 74 | ./linux/run_endframe_ichi.sh # 標準版/日本語UI 75 | ./linux/run_endframe_ichi_f1.sh # F1モデル版/日本語UI 76 | ./linux/run_oneframe_ichi.sh # 1フレーム推論版/日本語UI 77 | 78 | # 他の言語バージョン 79 | ./linux/run_endframe_ichi_en.sh # 英語UI 80 | ./linux/run_endframe_ichi_zh-tw.sh # 繁体字中国語UI 81 | ``` 82 | 83 | ## 使い方 84 | 85 | ### 既存リポジトリのセットアップ 86 | 87 | ```bash 88 | cd /path/to/FramePack-eichi 89 | ./linux/setup_submodule.sh 90 | ``` 91 | 92 | ### 本家の更新反映 93 | 94 | ```bash 95 | cd /path/to/FramePack-eichi 96 | ./linux/update.sh 97 | ``` 98 | 99 | ### アプリケーション実行 100 | 101 | ```bash 102 | cd /path/to/FramePack-eichi 103 | ./linux/run_endframe_ichi.sh # 無印版/日本語 104 | ./linux/run_endframe_ichi_f1.sh # F1版/日本語 105 | ./linux/run_oneframe_ichi.sh # 1フレーム推論版/日本語 106 | ``` 107 | 108 | ## 高速化ライブラリのインストール 109 | 110 | FramePackの実行時に以下のメッセージが表示される場合、高速化ライブラリがインストールされていません: 111 | 112 | ``` 113 | Xformers is not installed! 114 | Flash Attn is not installed! 115 | Sage Attn is not installed! 116 | ``` 117 | 118 | これらのライブラリをインストールすると処理速度が向上します(約30%程度の高速化が期待できます)。 119 | 120 | ### インストール方法 121 | 122 | お使いのPython環境に応じて以下のコマンドを実行してください: 123 | 124 | ```bash 125 | # 1. FramePackのディレクトリに移動 126 | cd /path/to/FramePack-eichi/webui/submodules/FramePack 127 | 128 | # 2. 必要なライブラリをインストール 129 | pip install xformers triton 130 | pip install packaging ninja 131 | pip install flash-attn --no-build-isolation 132 | pip install sage-attn==1.0.6 133 | 134 | # 3. 再起動してインストールを確認 135 | ``` 136 | 137 | ### スタンドアロン環境での高速化ライブラリインストール 138 | 139 | スタンドアロンセットアップの場合は、以下のようにインストールしてください: 140 | 141 | ```bash 142 | # 仮想環境が有効化されていることを確認 143 | source venv/bin/activate 144 | 145 | # FramePackディレクトリに移動 146 | cd FramePack 147 | 148 | # 高速化ライブラリをインストール 149 | pip install xformers triton 150 | pip install packaging ninja 151 | pip install flash-attn --no-build-isolation 152 | pip install sageattention==1.0.6 153 | ``` 154 | 155 | ### インストール時の注意点 156 | 157 | - CUDA 12.xを使用している場合のみ対応(CUDA 11.xでは一部ライブラリをビルドする必要があります) 158 | - 環境によっては`flash-attn`のインストールが難しい場合があります。その場合、Xformersだけでも性能向上が見込めます 159 | - PyTorchのバージョンが2.0.0以上であることを確認してください 160 | - sage-attnパッケージはsageattentionという名前に変更されている場合があります(バージョン1.0.6を指定) 161 | 162 | ## トラブルシューティング 163 | 164 | ### エラー「CUDA out of memory」が発生する場合 165 | 166 | メモリ不足の場合は、以下の対策を試してください: 167 | 168 | 1. 他のGPUを使用するアプリケーションを終了する 169 | 2. 画像サイズを小さくする(512x512付近を推奨) 170 | 3. バッチサイズを減らす 171 | 4. `gpu_memory_preservation`値を増やす(設定値を高くすると使用メモリは減りますが処理速度も低下) 172 | 173 | ### CUDAインストールと互換性の問題 174 | 175 | 「CUDAは利用できません」エラーや「CPU実行に切り替えます」という警告が表示される場合: 176 | 177 | 1. CUDAを正しくインストールしているか確認: 178 | ```bash 179 | nvidia-smi 180 | ``` 181 | 182 | 2. PyTorchがCUDAを認識しているか確認: 183 | ```python 184 | python -c "import torch; print(torch.cuda.is_available()); print(torch.version.cuda)" 185 | ``` 186 | 187 | 3. サポートされているGPU(RTX 30XX、40XX、または50XXシリーズ推奨)を使用していることを確認 188 | 189 | 4. CUDAドライバーとPyTorchの互換性を確認: 190 | - CUDA 12.6対応のドライバー 191 | - CUDAサポート付きPyTorch 2.6 192 | 193 | ### モデルロードの失敗 194 | 195 | モデルシャードのロード時にエラーが発生する場合: 196 | 197 | 1. モデルが適切にダウンロードされていることを確認 198 | 2. 初回起動時は、必要なモデル(約30GB)が自動的にダウンロードされるのを待つ 199 | 3. ディスク容量が十分にあることを確認(最低150GB推奨) 200 | 201 | ## 注意事項 202 | 203 | - これらのスクリプトは公式サポート対象外です 204 | - 実行パスの関係でエラーが発生する場合は、スクリプトを適宜修正してください 205 | - 複雑な処理や高解像度設定ではメモリ使用量が増加します(十分なRAMと高VRAMのGPUを推奨) 206 | - 長時間使用後にメモリリークが発生した場合は、アプリケーションを再起動してください 207 | - ご質問やバグ報告はIssueとして登録していただけますが、対応を約束するものではありません 208 | 209 | ## 参考情報 210 | 211 | - 公式FramePack: https://github.com/lllyasviel/FramePack 212 | - FramePack-eichi: https://github.com/git-ai-code/FramePack-eichi 213 | - 高速化ライブラリのインストール: https://github.com/lllyasviel/FramePack/issues/138 214 | - CUDAツールキット: https://developer.nvidia.com/cuda-12-6-0-download-archive -------------------------------------------------------------------------------- /linux/README_linux_en.md: -------------------------------------------------------------------------------- 1 | # FramePack-eichi Linux Support (Unofficial) 2 | 3 | This directory contains unofficial support scripts for using FramePack-eichi in a Linux environment. These scripts are provided for convenience and are **not officially supported**. Use at your own risk. 4 | 5 | ## System Requirements 6 | 7 | - **OS**: Ubuntu 22.04 LTS recommended (other distributions supporting Python 3.10 should also work) 8 | - **CPU**: 8+ cores of a modern multi-core CPU recommended 9 | - **RAM**: Minimum 16GB, 32GB+ recommended (64GB recommended for complex processing and high resolutions) 10 | - **GPU**: NVIDIA RTX 30XX/40XX/50XX series (8GB+ VRAM) 11 | - **VRAM**: Minimum 8GB (12GB+ recommended) 12 | - **Storage**: 150GB+ of free space (SSD recommended) 13 | - **Required Software**: 14 | - CUDA Toolkit 12.6 15 | - Python 3.10.x 16 | - PyTorch 2.6 with CUDA support 17 | 18 | ## Included Scripts 19 | 20 | - `update.sh` - Script to update the main repository and apply FramePack-eichi files 21 | - `setup_submodule.sh` - Script for initial setup 22 | - `install_linux.sh` - Simple installer for Linux 23 | - `run_endframe_ichi.sh` - Standard version/Japanese execution script 24 | - `run_endframe_ichi_f1.sh` - F1 version/Japanese execution script 25 | - `run_oneframe_ichi.sh` - One-frame inference version/Japanese execution script 26 | - Other language version execution scripts 27 | 28 | ## Linux Setup Guide (Submodule Method) 29 | 30 | ### 1. Install Prerequisites 31 | 32 | ```bash 33 | # Update system packages 34 | sudo apt update && sudo apt upgrade -y 35 | 36 | # Install basic development tools and libraries 37 | sudo apt install -y git wget ffmpeg libavformat-dev libavdevice-dev libavfilter-dev libswscale-dev libopenblas-dev 38 | 39 | # Install CUDA Toolkit 12.6 40 | # Note: Follow NVIDIA's official instructions to install CUDA Toolkit 41 | # https://developer.nvidia.com/cuda-12-6-0-download-archive 42 | 43 | # Install Python 3.10 44 | sudo apt install -y python3.10 python3.10-venv python3-pip 45 | ``` 46 | 47 | ### 2. Clone and Set Up FramePack-eichi 48 | 49 | ```bash 50 | # Clone FramePack-eichi repository 51 | git clone https://github.com/git-ai-code/FramePack-eichi.git 52 | cd FramePack-eichi 53 | 54 | # Create and activate virtual environment 55 | python3.10 -m venv venv 56 | source venv/bin/activate 57 | 58 | # Set up submodules (automatically downloads original FramePack) 59 | ./linux/setup_submodule.sh 60 | 61 | # Install PyTorch with CUDA support and dependencies 62 | cd webui/submodules/FramePack 63 | pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126 64 | pip install -r requirements.txt 65 | ``` 66 | 67 | ### 3. Launch FramePack-eichi 68 | 69 | ```bash 70 | # Return to FramePack-eichi root directory 71 | cd ~/FramePack-eichi # Adjust path according to your installation location 72 | 73 | # Launch using execution scripts 74 | ./linux/run_endframe_ichi_en.sh # Standard version/English UI 75 | ./linux/run_endframe_ichi_en_f1.sh # F1 model version/English UI 76 | ./linux/run_oneframe_ichi_en.sh # One-frame inference version/English UI 77 | 78 | # Other language versions 79 | ./linux/run_endframe_ichi.sh # Japanese UI 80 | ./linux/run_endframe_ichi_zh-tw.sh # Traditional Chinese UI 81 | ``` 82 | 83 | ## Usage 84 | 85 | ### Setup for Existing Repository 86 | 87 | ```bash 88 | cd /path/to/FramePack-eichi 89 | ./linux/setup_submodule.sh 90 | ``` 91 | 92 | ### Update from Original Repository 93 | 94 | ```bash 95 | cd /path/to/FramePack-eichi 96 | ./linux/update.sh 97 | ``` 98 | 99 | ### Running the Application 100 | 101 | ```bash 102 | cd /path/to/FramePack-eichi 103 | ./linux/run_endframe_ichi.sh # Standard version/Japanese 104 | ./linux/run_endframe_ichi_f1.sh # F1 version/Japanese 105 | ./linux/run_oneframe_ichi.sh # One-frame inference version/Japanese 106 | ``` 107 | 108 | ## Installing Acceleration Libraries 109 | 110 | If you see the following messages when running FramePack, the acceleration libraries are not installed: 111 | 112 | ``` 113 | Xformers is not installed! 114 | Flash Attn is not installed! 115 | Sage Attn is not installed! 116 | ``` 117 | 118 | Installing these libraries can improve processing speed (approximately 30% speedup can be expected). 119 | 120 | ### Installation Method 121 | 122 | Depending on your Python environment, run the following commands: 123 | 124 | ```bash 125 | # 1. Navigate to the FramePack directory 126 | cd /path/to/FramePack-eichi/webui/submodules/FramePack 127 | 128 | # 2. Install required libraries 129 | pip install xformers triton 130 | pip install packaging ninja 131 | pip install flash-attn --no-build-isolation 132 | pip install sage-attn==1.0.6 133 | 134 | # 3. Restart to verify installation 135 | ``` 136 | 137 | ### Installing Acceleration Libraries for Standalone Setup 138 | 139 | For standalone setup, install as follows: 140 | 141 | ```bash 142 | # Ensure your virtual environment is activated 143 | source venv/bin/activate 144 | 145 | # Navigate to FramePack directory 146 | cd FramePack 147 | 148 | # Install acceleration libraries 149 | pip install xformers triton 150 | pip install packaging ninja 151 | pip install flash-attn --no-build-isolation 152 | pip install sageattention==1.0.6 153 | ``` 154 | 155 | ### Installation Notes 156 | 157 | - Only supported with CUDA 12.x (for CUDA 11.x, some libraries need to be built) 158 | - Installing `flash-attn` may be difficult in some environments. In that case, using only Xformers can still improve performance 159 | - Make sure your PyTorch version is 2.0.0 or higher 160 | - The sage-attn package may be renamed to sageattention (specify version 1.0.6) 161 | 162 | ## Troubleshooting 163 | 164 | ### "CUDA out of memory" Error 165 | 166 | If you encounter memory issues, try the following: 167 | 168 | 1. Close other applications using the GPU 169 | 2. Reduce image size (512x512 range recommended) 170 | 3. Reduce batch size 171 | 4. Increase the `gpu_memory_preservation` value (higher settings reduce memory usage but also reduce processing speed) 172 | 173 | ### CUDA Installation and Compatibility Issues 174 | 175 | If you see "CUDA is not available" errors or warnings about "switching to CPU execution": 176 | 177 | 1. Check if CUDA is correctly installed: 178 | ```bash 179 | nvidia-smi 180 | ``` 181 | 182 | 2. Check if PyTorch recognizes CUDA: 183 | ```python 184 | python -c "import torch; print(torch.cuda.is_available()); print(torch.version.cuda)" 185 | ``` 186 | 187 | 3. Verify that you're using a supported GPU (RTX 30XX, 40XX, or 50XX series recommended) 188 | 189 | 4. Check CUDA driver and PyTorch compatibility: 190 | - Driver compatible with CUDA 12.6 191 | - PyTorch 2.6 with CUDA support 192 | 193 | ### Model Loading Failures 194 | 195 | If you encounter errors when loading model shards: 196 | 197 | 1. Verify that models are properly downloaded 198 | 2. For first launch, wait for the necessary models (about 30GB) to download automatically 199 | 3. Ensure you have sufficient disk space (minimum 150GB recommended) 200 | 201 | ## Notes 202 | 203 | - These scripts are not officially supported 204 | - If you encounter errors related to execution paths, please modify the scripts accordingly 205 | - Complex processing and high-resolution settings increase memory usage (sufficient RAM and high VRAM GPUs recommended) 206 | - If memory leaks occur after extended use, restart the application 207 | - While you can register questions or bug reports as Issues, we cannot guarantee they will be addressed 208 | 209 | ## References 210 | 211 | - Official FramePack: https://github.com/lllyasviel/FramePack 212 | - FramePack-eichi: https://github.com/git-ai-code/FramePack-eichi 213 | - Acceleration Library Installation: https://github.com/lllyasviel/FramePack/issues/138 214 | - CUDA Toolkit: https://developer.nvidia.com/cuda-12-6-0-download-archive -------------------------------------------------------------------------------- /linux/README_linux_ru.md: -------------------------------------------------------------------------------- 1 | # Поддержка Linux для FramePack-eichi (неофициальная) 2 | 3 | Этот каталог содержит неофициальные скрипты поддержки для использования FramePack-eichi в среде Linux. Эти скрипты предоставляются для удобства и **не являются официально поддерживаемыми**. Используйте их на свой страх и риск. 4 | 5 | ## Системные требования 6 | 7 | - **ОС**: Рекомендуется Ubuntu 22.04 LTS или новее (также могут работать другие дистрибутивы с поддержкой Python 3.10) 8 | - **CPU**: Рекомендуется современный многоядерный CPU с 8 или более ядрами 9 | - **RAM**: Минимум 16ГБ, рекомендуется 32ГБ или больше (64ГБ рекомендуется для сложной обработки или высокого разрешения) 10 | - **GPU**: NVIDIA RTX 30XX/40XX/50XX серии (8ГБ VRAM или больше) 11 | - **VRAM**: Минимум 8ГБ (рекомендуется 12ГБ или больше) 12 | - **Хранилище**: 150ГБ или больше свободного места (рекомендуется SSD) 13 | - **Необходимое программное обеспечение**: 14 | - CUDA Toolkit 12.6 15 | - Python 3.10.x 16 | - PyTorch 2.6 с поддержкой CUDA 17 | 18 | ## Включенные скрипты 19 | 20 | - `update.sh` - Скрипт для обновления из основного репозитория и применения изменений FramePack-eichi 21 | - `setup_submodule.sh` - Скрипт для первоначальной настройки 22 | - `install_linux.sh` - Простой установщик для Linux 23 | - `run_endframe_ichi.sh` - Скрипт запуска обычной версии на японском 24 | - `run_endframe_ichi_ru.sh` - Скрипт запуска обычной версии на русском 25 | - `run_endframe_ichi_f1.sh` - Скрипт запуска версии F1 на японском 26 | - `run_endframe_ichi_f1_ru.sh` - Скрипт запуска версии F1 на русском 27 | - `run_oneframe_ichi.sh` - Скрипт запуска версии вывода одного кадра на японском 28 | - `run_oneframe_ichi_ru.sh` - Скрипт запуска версии вывода одного кадра на русском 29 | - Скрипты запуска для других языков 30 | 31 | ## Процедура настройки Linux (подмодуль) 32 | 33 | ### 1. Установка предварительных требований 34 | 35 | ```bash 36 | # Обновление системных пакетов 37 | sudo apt update && sudo apt upgrade -y 38 | 39 | # Установка базовых инструментов разработки и библиотек 40 | sudo apt install -y git wget ffmpeg libavformat-dev libavdevice-dev libavfilter-dev libswscale-dev libopenblas-dev 41 | 42 | # Установка CUDA Toolkit 12.6 43 | # Примечание: Следуйте официальным инструкциям NVIDIA для установки CUDA Toolkit 44 | # https://developer.nvidia.com/cuda-12-6-0-download-archive 45 | 46 | # Установка Python 3.10 47 | sudo apt install -y python3.10 python3.10-venv python3-pip 48 | ``` 49 | 50 | ### 2. Клонирование и настройка FramePack-eichi 51 | 52 | ```bash 53 | # Клонирование репозитория FramePack-eichi 54 | git clone https://github.com/git-ai-code/FramePack-eichi.git 55 | cd FramePack-eichi 56 | 57 | # Создание и активация виртуальной среды 58 | python3.10 -m venv venv 59 | source venv/bin/activate 60 | 61 | # Настройка подмодуля (автоматически загружает оригинальный FramePack) 62 | ./linux/setup_submodule.sh 63 | 64 | # Установка PyTorch с поддержкой CUDA и зависимостей 65 | cd webui/submodules/FramePack 66 | pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126 67 | pip install -r requirements.txt 68 | ``` 69 | 70 | ### 3. Запуск FramePack-eichi 71 | 72 | ```bash 73 | # Возврат в корневой каталог FramePack-eichi 74 | cd ~/FramePack-eichi # Настройте путь в соответствии с вашим местом установки 75 | 76 | # Использование скриптов запуска 77 | ./linux/run_endframe_ichi.sh # Стандартная версия/японский UI 78 | ./linux/run_endframe_ichi_f1.sh # Версия с моделью F1/японский UI 79 | ./linux/run_oneframe_ichi.sh # Версия вывода одного кадра/японский UI 80 | ./linux/run_endframe_ichi_ru.sh # Стандартная версия/русский UI 81 | ./linux/run_endframe_ichi_f1_ru.sh # Версия с моделью F1/русский UI 82 | ./linux/run_oneframe_ichi_ru.sh # Версия вывода одного кадра/русский UI 83 | 84 | # Другие языковые версии 85 | ./linux/run_endframe_ichi_en.sh # Английский UI 86 | ./linux/run_endframe_ichi_zh-tw.sh # UI на традиционном китайском 87 | ``` 88 | 89 | ## Использование 90 | 91 | ### Настройка существующего репозитория 92 | 93 | ```bash 94 | cd /path/to/FramePack-eichi 95 | ./linux/setup_submodule.sh 96 | ``` 97 | 98 | ### Отражение обновлений из основного проекта 99 | 100 | ```bash 101 | cd /path/to/FramePack-eichi 102 | ./linux/update.sh 103 | ``` 104 | 105 | ### Запуск приложения 106 | 107 | ```bash 108 | cd /path/to/FramePack-eichi 109 | ./linux/run_endframe_ichi.sh # Обычная версия/японский 110 | ./linux/run_endframe_ichi_f1.sh # Версия F1/японский 111 | ./linux/run_oneframe_ichi.sh # Версия вывода одного кадра/японский 112 | ./linux/run_endframe_ichi_ru.sh # Обычная версия/русский 113 | ./linux/run_endframe_ichi_f1_ru.sh # Версия F1/русский 114 | ./linux/run_oneframe_ichi_ru.sh # Версия вывода одного кадра/русский 115 | ``` 116 | 117 | ## Установка библиотек ускорения 118 | 119 | Если при запуске FramePack отображаются следующие сообщения, библиотеки ускорения не установлены: 120 | 121 | ``` 122 | Xformers is not installed! 123 | Flash Attn is not installed! 124 | Sage Attn is not installed! 125 | ``` 126 | 127 | Установка этих библиотек повысит скорость обработки (ожидается ускорение примерно на 30%). 128 | 129 | ### Метод установки 130 | 131 | Выполните следующие команды в соответствии с вашей средой Python: 132 | 133 | ```bash 134 | # 1. Перейдите в директорию FramePack 135 | cd /path/to/FramePack-eichi/webui/submodules/FramePack 136 | 137 | # 2. Установите необходимые библиотеки 138 | pip install xformers triton 139 | pip install packaging ninja 140 | pip install flash-attn --no-build-isolation 141 | pip install sage-attn==1.0.6 142 | 143 | # 3. Перезапустите для проверки установки 144 | ``` 145 | 146 | ### Установка библиотек ускорения в автономной среде 147 | 148 | Для автономной установки используйте следующий метод: 149 | 150 | ```bash 151 | # Убедитесь, что виртуальная среда активирована 152 | source venv/bin/activate 153 | 154 | # Перейдите в директорию FramePack 155 | cd FramePack 156 | 157 | # Установите библиотеки ускорения 158 | pip install xformers triton 159 | pip install packaging ninja 160 | pip install flash-attn --no-build-isolation 161 | pip install sageattention==1.0.6 162 | ``` 163 | 164 | ### Примечания по установке 165 | 166 | - Поддерживается только при использовании CUDA 12.x (для CUDA 11.x некоторые библиотеки необходимо собирать) 167 | - Установка `flash-attn` может быть трудной в некоторых средах. В таком случае, улучшение производительности ожидается даже с одним Xformers 168 | - Убедитесь, что версия PyTorch 2.0.0 или выше 169 | - Пакет sage-attn может быть переименован в sageattention (указывайте версию 1.0.6) 170 | 171 | ## Устранение неполадок 172 | 173 | ### Ошибка «CUDA out of memory» 174 | 175 | Если возникает нехватка памяти, попробуйте следующие меры: 176 | 177 | 1. Закройте другие приложения, использующие GPU 178 | 2. Уменьшите размер изображения (рекомендуется около 512x512) 179 | 3. Уменьшите размер батча 180 | 4. Увеличьте значение `gpu_memory_preservation` (более высокое значение уменьшает использование памяти, но снижает скорость обработки) 181 | 182 | ### Проблемы с установкой CUDA и совместимостью 183 | 184 | Если отображается ошибка «CUDA недоступна» или предупреждение «Переключение на выполнение CPU»: 185 | 186 | 1. Проверьте, правильно ли установлен CUDA: 187 | ```bash 188 | nvidia-smi 189 | ``` 190 | 191 | 2. Проверьте, распознаёт ли PyTorch CUDA: 192 | ```python 193 | python -c "import torch; print(torch.cuda.is_available()); print(torch.version.cuda)" 194 | ``` 195 | 196 | 3. Убедитесь, что вы используете поддерживаемый GPU (рекомендуется RTX 30XX, 40XX или 50XX серии) 197 | 198 | 4. Проверьте совместимость драйвера CUDA и PyTorch: 199 | - Драйвер, совместимый с CUDA 12.6 200 | - PyTorch 2.6 с поддержкой CUDA 201 | 202 | ### Сбой загрузки модели 203 | 204 | Если возникают ошибки при загрузке сегментов модели: 205 | 206 | 1. Убедитесь, что модели правильно загружены 207 | 2. При первом запуске дождитесь автоматической загрузки необходимых моделей (около 30ГБ) 208 | 3. Убедитесь, что у вас достаточно места на диске (рекомендуется минимум 150ГБ) 209 | 210 | ## Предостережения 211 | 212 | - Эти скрипты не имеют официальной поддержки 213 | - Если возникают ошибки, связанные с путями выполнения, пожалуйста, измените скрипты соответствующим образом 214 | - Использование памяти увеличивается при сложной обработке или высоком разрешении (рекомендуется достаточный RAM и GPU с высоким VRAM) 215 | - Если после длительного использования возникает утечка памяти, перезапустите приложение 216 | - Вы можете регистрировать вопросы или отчеты об ошибках как Issues, но решение не гарантируется 217 | 218 | ## Справочная информация 219 | 220 | - Официальный FramePack: https://github.com/lllyasviel/FramePack 221 | - FramePack-eichi: https://github.com/git-ai-code/FramePack-eichi 222 | - Установка библиотек ускорения: https://github.com/lllyasviel/FramePack/issues/138 223 | - CUDA Toolkit: https://developer.nvidia.com/cuda-12-6-0-download-archive -------------------------------------------------------------------------------- /linux/README_linux_zh-tw.md: -------------------------------------------------------------------------------- 1 | # FramePack-eichi Linux 支援 (非官方) 2 | 3 | 此目錄包含在 Linux 環境中使用 FramePack-eichi 的非官方支援腳本。這些腳本僅為便利提供,**不提供官方支援**。使用時風險自負。 4 | 5 | ## 系統需求 6 | 7 | - **作業系統**: 推薦 Ubuntu 22.04 LTS(其他支援 Python 3.10 的發行版也應該可以運作) 8 | - **CPU**: 推薦 8 核心以上的現代多核心 CPU 9 | - **RAM**: 最低 16GB,推薦 32GB 以上(複雜處理和高解析度推薦 64GB) 10 | - **GPU**: NVIDIA RTX 30XX/40XX/50XX 系列(8GB 以上 VRAM) 11 | - **VRAM**: 最低 8GB(推薦 12GB 以上) 12 | - **儲存空間**: 150GB 以上的可用空間(推薦 SSD) 13 | - **必要軟體**: 14 | - CUDA Toolkit 12.6 15 | - Python 3.10.x 16 | - 支援 CUDA 的 PyTorch 2.6 17 | 18 | ## 包含的腳本 19 | 20 | - `update.sh` - 更新主要儲存庫並應用 FramePack-eichi 檔案的腳本 21 | - `setup_submodule.sh` - 初始設置用腳本 22 | - `install_linux.sh` - Linux 簡易安裝程式 23 | - `run_endframe_ichi.sh` - 標準版/日文執行腳本 24 | - `run_endframe_ichi_f1.sh` - F1版/日文執行腳本 25 | - `run_oneframe_ichi.sh` - 單幀推論版/日文執行腳本 26 | - 其他語言版本執行腳本 27 | 28 | ## Linux 設置指南(子模組方法) 29 | 30 | ### 1. 安裝必要條件 31 | 32 | ```bash 33 | # 更新系統套件 34 | sudo apt update && sudo apt upgrade -y 35 | 36 | # 安裝基本開發工具和庫 37 | sudo apt install -y git wget ffmpeg libavformat-dev libavdevice-dev libavfilter-dev libswscale-dev libopenblas-dev 38 | 39 | # 安裝 CUDA Toolkit 12.6 40 | # 注意:請按照 NVIDIA 的官方說明安裝 CUDA Toolkit 41 | # https://developer.nvidia.com/cuda-12-6-0-download-archive 42 | 43 | # 安裝 Python 3.10 44 | sudo apt install -y python3.10 python3.10-venv python3-pip 45 | ``` 46 | 47 | ### 2. 複製並設置 FramePack-eichi 48 | 49 | ```bash 50 | # 複製 FramePack-eichi 儲存庫 51 | git clone https://github.com/git-ai-code/FramePack-eichi.git 52 | cd FramePack-eichi 53 | 54 | # 創建並啟用虛擬環境 55 | python3.10 -m venv venv 56 | source venv/bin/activate 57 | 58 | # 設置子模組(自動下載原始 FramePack) 59 | ./linux/setup_submodule.sh 60 | 61 | # 安裝帶 CUDA 支援的 PyTorch 和依賴項 62 | cd webui/submodules/FramePack 63 | pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126 64 | pip install -r requirements.txt 65 | ``` 66 | 67 | ### 3. 啟動 FramePack-eichi 68 | 69 | ```bash 70 | # 返回 FramePack-eichi 根目錄 71 | cd ~/FramePack-eichi # 根據您的安裝位置調整路徑 72 | 73 | # 使用執行腳本啟動 74 | ./linux/run_endframe_ichi_zh-tw.sh # 標準版/繁體中文介面 75 | ./linux/run_endframe_ichi_zh-tw_f1.sh # F1 模型版本/繁體中文介面 76 | ./linux/run_oneframe_ichi_zh-tw.sh # 單幀推論版本/繁體中文介面 77 | 78 | # 其他語言版本 79 | ./linux/run_endframe_ichi.sh # 日文介面 80 | ./linux/run_endframe_ichi_en.sh # 英文介面 81 | ``` 82 | 83 | ## 使用方式 84 | 85 | ### 現有存儲庫的設置 86 | 87 | ```bash 88 | cd /path/to/FramePack-eichi 89 | ./linux/setup_submodule.sh 90 | ``` 91 | 92 | ### 更新原始儲存庫 93 | 94 | ```bash 95 | cd /path/to/FramePack-eichi 96 | ./linux/update.sh 97 | ``` 98 | 99 | ### 執行應用程式 100 | 101 | ```bash 102 | cd /path/to/FramePack-eichi 103 | ./linux/run_endframe_ichi.sh # 標準版/日文 104 | ./linux/run_endframe_ichi_f1.sh # F1版/日文 105 | ./linux/run_oneframe_ichi.sh # 單幀推論版/日文 106 | ``` 107 | 108 | ## 安裝加速庫 109 | 110 | 如果執行 FramePack 時看到以下訊息,表示尚未安裝加速庫: 111 | 112 | ``` 113 | Xformers is not installed! 114 | Flash Attn is not installed! 115 | Sage Attn is not installed! 116 | ``` 117 | 118 | 安裝這些庫可以提高處理速度(預期可達約 30% 的加速)。 119 | 120 | ### 安裝方法 121 | 122 | 根據您的 Python 環境,執行以下命令: 123 | 124 | ```bash 125 | # 1. 導航到 FramePack 目錄 126 | cd /path/to/FramePack-eichi/webui/submodules/FramePack 127 | 128 | # 2. 安裝必要庫 129 | pip install xformers triton 130 | pip install packaging ninja 131 | pip install flash-attn --no-build-isolation 132 | pip install sage-attn==1.0.6 133 | 134 | # 3. 重新啟動以驗證安裝 135 | ``` 136 | 137 | ### 為獨立設置安裝加速庫 138 | 139 | 對於獨立設置,請按照以下方式安裝: 140 | 141 | ```bash 142 | # 確保您的虛擬環境已啟用 143 | source venv/bin/activate 144 | 145 | # 導航到 FramePack 目錄 146 | cd FramePack 147 | 148 | # 安裝加速庫 149 | pip install xformers triton 150 | pip install packaging ninja 151 | pip install flash-attn --no-build-isolation 152 | pip install sageattention==1.0.6 153 | ``` 154 | 155 | ### 安裝注意事項 156 | 157 | - 僅支援 CUDA 12.x(對於 CUDA 11.x,需要編譯某些庫) 158 | - 在某些環境中安裝 `flash-attn` 可能有困難。在這種情況下,僅使用 Xformers 仍然可以提高性能 159 | - 確保您的 PyTorch 版本為 2.0.0 或更高 160 | - sage-attn 包可能被重命名為 sageattention(指定版本 1.0.6) 161 | 162 | ## 故障排除 163 | 164 | ### "CUDA out of memory" 錯誤 165 | 166 | 如果遇到記憶體問題,請嘗試以下方法: 167 | 168 | 1. 關閉使用 GPU 的其他應用程式 169 | 2. 減小圖像大小(建議 512x512 左右) 170 | 3. 減少批次大小 171 | 4. 增加 `gpu_memory_preservation` 值(較高的設定會減少記憶體使用量,但也會降低處理速度) 172 | 173 | ### CUDA 安裝和相容性問題 174 | 175 | 如果看到 "CUDA 不可用" 錯誤或關於 "切換到 CPU 執行" 的警告: 176 | 177 | 1. 檢查 CUDA 是否正確安裝: 178 | ```bash 179 | nvidia-smi 180 | ``` 181 | 182 | 2. 檢查 PyTorch 是否識別 CUDA: 183 | ```python 184 | python -c "import torch; print(torch.cuda.is_available()); print(torch.version.cuda)" 185 | ``` 186 | 187 | 3. 確認您使用的是支援的 GPU(建議 RTX 30XX、40XX 或 50XX 系列) 188 | 189 | 4. 檢查 CUDA 驅動程式和 PyTorch 相容性: 190 | - 與 CUDA 12.6 相容的驅動程式 191 | - 支援 CUDA 的 PyTorch 2.6 192 | 193 | ### 模型載入失敗 194 | 195 | 如果遇到載入模型分片時的錯誤: 196 | 197 | 1. 確認模型已正確下載 198 | 2. 對於首次啟動,等待必要的模型(約 30GB)自動下載 199 | 3. 確保有足夠的磁碟空間(建議最少 150GB) 200 | 201 | ## 注意事項 202 | 203 | - 這些腳本不受官方支援 204 | - 如果遇到與執行路徑相關的錯誤,請相應修改腳本 205 | - 複雜處理和高解析度設定會增加記憶體使用量(建議足夠的 RAM 和高 VRAM GPU) 206 | - 如果長時間使用後出現記憶體洩漏,請重新啟動應用程式 207 | - 雖然您可以將問題或錯誤報告註冊為 Issues,但我們不保證會解決這些問題 208 | 209 | ## 參考資訊 210 | 211 | - 官方 FramePack:https://github.com/lllyasviel/FramePack 212 | - FramePack-eichi:https://github.com/git-ai-code/FramePack-eichi 213 | - 加速庫安裝:https://github.com/lllyasviel/FramePack/issues/138 214 | - CUDA Toolkit:https://developer.nvidia.com/cuda-12-6-0-download-archive -------------------------------------------------------------------------------- /linux/install_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # FramePack-eichi Linux Installation Script 3 | # Note: Unofficial support - no warranty 4 | 5 | set -e 6 | 7 | INSTALL_DIR="${HOME}/FramePack-eichi" 8 | REPO_URL="https://github.com/git-ai-code/FramePack-eichi.git" 9 | PYTHON_VERSION="3.10" 10 | 11 | # Parse arguments 12 | while [[ $# -gt 0 ]]; do 13 | case $1 in 14 | --dir=*) 15 | INSTALL_DIR="${1#*=}" 16 | shift 17 | ;; 18 | --help) 19 | echo "Usage: $0 [options]" 20 | echo "Options:" 21 | echo " --dir=PATH Specify installation directory (default: $INSTALL_DIR)" 22 | echo " --help Display this help message" 23 | exit 0 24 | ;; 25 | *) 26 | echo "Unknown option: $1" 27 | exit 1 28 | ;; 29 | esac 30 | done 31 | 32 | echo "=== FramePack-eichi Installer ===" 33 | echo "Installation destination: $INSTALL_DIR" 34 | 35 | # Check and install required packages 36 | echo "1. Checking dependencies..." 37 | 38 | # Check Python installation 39 | if ! command -v python$PYTHON_VERSION &> /dev/null && ! command -v python3 &> /dev/null; then 40 | echo "Installing Python..." 41 | if command -v apt-get &> /dev/null; then 42 | sudo apt-get update 43 | sudo apt-get install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-venv python${PYTHON_VERSION}-dev 44 | elif command -v yum &> /dev/null; then 45 | sudo yum install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-devel 46 | elif command -v dnf &> /dev/null; then 47 | sudo dnf install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-devel 48 | elif command -v pacman &> /dev/null; then 49 | sudo pacman -S python 50 | elif command -v brew &> /dev/null; then 51 | brew install python@${PYTHON_VERSION} 52 | else 53 | echo "Warning: System not supported for automatic installation. Please install Python manually." 54 | fi 55 | fi 56 | 57 | # Check Git installation 58 | if ! command -v git &> /dev/null; then 59 | echo "Installing Git..." 60 | if command -v apt-get &> /dev/null; then 61 | sudo apt-get update 62 | sudo apt-get install -y git 63 | elif command -v yum &> /dev/null; then 64 | sudo yum install -y git 65 | elif command -v dnf &> /dev/null; then 66 | sudo dnf install -y git 67 | elif command -v pacman &> /dev/null; then 68 | sudo pacman -S git 69 | elif command -v brew &> /dev/null; then 70 | brew install git 71 | else 72 | echo "Warning: System not supported for automatic installation. Please install Git manually." 73 | fi 74 | fi 75 | 76 | # Create and navigate to directory 77 | echo "2. Preparing repository..." 78 | mkdir -p "$INSTALL_DIR" 79 | cd "$INSTALL_DIR" 80 | 81 | # Clone or update repository 82 | if [ -d ".git" ]; then 83 | echo "Updating existing repository..." 84 | git pull 85 | else 86 | echo "Cloning repository..." 87 | git clone "$REPO_URL" . 88 | fi 89 | 90 | # Initialize and update submodules 91 | echo "3. Initializing submodules..." 92 | git submodule init 93 | git submodule update 94 | 95 | # Create execution scripts 96 | echo "4. Creating execution scripts..." 97 | 98 | # Copy Linux scripts 99 | echo "Copying Linux scripts..." 100 | mkdir -p "$INSTALL_DIR/linux" 101 | cp -r "$INSTALL_DIR/linux"/*.sh "$INSTALL_DIR/linux/" 102 | chmod +x "$INSTALL_DIR/linux"/*.sh 103 | 104 | # Apply eichi-specific files 105 | echo "5. Applying FramePack-eichi files..." 106 | chmod +x "$INSTALL_DIR/linux/update.sh" 107 | "$INSTALL_DIR/linux/update.sh" 108 | 109 | echo "=== Installation Complete! ===" 110 | echo "You can run FramePack-eichi with the following commands:" 111 | echo "cd $INSTALL_DIR && ./linux/run_endframe_ichi.sh" 112 | echo "" 113 | echo "For F1 model version:" 114 | echo "cd $INSTALL_DIR && ./linux/run_endframe_ichi_f1.sh" 115 | echo "" 116 | echo "For English UI:" 117 | echo "cd $INSTALL_DIR && ./linux/run_endframe_ichi_en.sh" 118 | echo "" 119 | echo "Note: These scripts are unofficial and you use them at your own risk." -------------------------------------------------------------------------------- /linux/run_endframe_ichi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Endframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/endframe_ichi.py "$@" -------------------------------------------------------------------------------- /linux/run_endframe_ichi_en.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # English Endframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/endframe_ichi.py --lang=en "$@" -------------------------------------------------------------------------------- /linux/run_endframe_ichi_en_f1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # English F1 Model Endframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/endframe_ichi_f1.py --lang=en "$@" -------------------------------------------------------------------------------- /linux/run_endframe_ichi_f1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # F1 Model Endframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/endframe_ichi_f1.py "$@" -------------------------------------------------------------------------------- /linux/run_endframe_ichi_f1_ru.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # スクリプトのあるディレクトリを取得 4 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 5 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 6 | 7 | cd "$ROOT_DIR/webui" 8 | 9 | # Python実行パスの検出(優先度順) 10 | if [ -f "$ROOT_DIR/venv/bin/python" ]; then 11 | # 仮想環境が存在する場合 12 | PYTHON_CMD="$ROOT_DIR/venv/bin/python" 13 | elif command -v python3.10 &> /dev/null; then 14 | # python3.10が利用可能な場合 15 | PYTHON_CMD="python3.10" 16 | elif command -v python3.9 &> /dev/null; then 17 | # python3.9が利用可能な場合 18 | PYTHON_CMD="python3.9" 19 | elif command -v python3 &> /dev/null; then 20 | # python3が利用可能な場合 21 | PYTHON_CMD="python3" 22 | else 23 | echo "エラー: Python 3.9以上が見つかりません" 24 | exit 1 25 | fi 26 | 27 | # Python環境の表示 28 | echo "使用するPython環境: $PYTHON_CMD" 29 | $PYTHON_CMD --version 30 | 31 | # スクリプト実行 32 | $PYTHON_CMD endframe_ichi_f1.py --lang ru -------------------------------------------------------------------------------- /linux/run_endframe_ichi_ru.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # スクリプトのあるディレクトリを取得 4 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 5 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 6 | 7 | cd "$ROOT_DIR/webui" 8 | 9 | # Python実行パスの検出(優先度順) 10 | if [ -f "$ROOT_DIR/venv/bin/python" ]; then 11 | # 仮想環境が存在する場合 12 | PYTHON_CMD="$ROOT_DIR/venv/bin/python" 13 | elif command -v python3.10 &> /dev/null; then 14 | # python3.10が利用可能な場合 15 | PYTHON_CMD="python3.10" 16 | elif command -v python3.9 &> /dev/null; then 17 | # python3.9が利用可能な場合 18 | PYTHON_CMD="python3.9" 19 | elif command -v python3 &> /dev/null; then 20 | # python3が利用可能な場合 21 | PYTHON_CMD="python3" 22 | else 23 | echo "エラー: Python 3.9以上が見つかりません" 24 | exit 1 25 | fi 26 | 27 | # Python環境の表示 28 | echo "使用するPython環境: $PYTHON_CMD" 29 | $PYTHON_CMD --version 30 | 31 | # スクリプト実行 32 | $PYTHON_CMD endframe_ichi.py --lang ru -------------------------------------------------------------------------------- /linux/run_endframe_ichi_zh-tw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Traditional Chinese Endframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/endframe_ichi.py --lang=zh-tw "$@" -------------------------------------------------------------------------------- /linux/run_endframe_ichi_zh-tw_f1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Traditional Chinese F1 Model Endframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/endframe_ichi_f1.py --lang=zh-tw "$@" -------------------------------------------------------------------------------- /linux/run_oneframe_ichi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Oneframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/oneframe_ichi.py "$@" -------------------------------------------------------------------------------- /linux/run_oneframe_ichi_en.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # English Oneframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/oneframe_ichi.py --lang=en "$@" -------------------------------------------------------------------------------- /linux/run_oneframe_ichi_ru.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # スクリプトのあるディレクトリを取得 4 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 5 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 6 | 7 | cd "$ROOT_DIR/webui" 8 | 9 | # Python実行パスの検出(優先度順) 10 | if [ -f "$ROOT_DIR/venv/bin/python" ]; then 11 | # 仮想環境が存在する場合 12 | PYTHON_CMD="$ROOT_DIR/venv/bin/python" 13 | elif command -v python3.10 &> /dev/null; then 14 | # python3.10が利用可能な場合 15 | PYTHON_CMD="python3.10" 16 | elif command -v python3.9 &> /dev/null; then 17 | # python3.9が利用可能な場合 18 | PYTHON_CMD="python3.9" 19 | elif command -v python3 &> /dev/null; then 20 | # python3が利用可能な場合 21 | PYTHON_CMD="python3" 22 | else 23 | echo "エラー: Python 3.9以上が見つかりません" 24 | exit 1 25 | fi 26 | 27 | # Python環境の表示 28 | echo "使用するPython環境: $PYTHON_CMD" 29 | $PYTHON_CMD --version 30 | 31 | # スクリプト実行 32 | $PYTHON_CMD oneframe_ichi.py --lang ru -------------------------------------------------------------------------------- /linux/run_oneframe_ichi_zh-tw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Traditional Chinese Oneframe Execution Script 3 | # Note: Unofficial support - no warranty 4 | 5 | # Get the script directory 6 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | 8 | # FramePack-eichi root directory (parent of script directory) 9 | ROOT_DIR="$(dirname "$SCRIPT_DIR")" 10 | 11 | # Navigate to main FramePack directory 12 | cd "$ROOT_DIR/webui/submodules/FramePack" 13 | python3 webui/oneframe_ichi.py --lang=zh-tw "$@" -------------------------------------------------------------------------------- /linux/setup_submodule.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Submodule Setup Script 3 | # Sets up the original FramePack repository as a submodule 4 | # Note: Unofficial support - no warranty 5 | 6 | set -e 7 | 8 | # Get the script directory 9 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 | 11 | # Navigate to FramePack-eichi root directory (parent of script directory) 12 | cd "$(dirname "$SCRIPT_DIR")" 13 | 14 | # Prepare submodule directory 15 | mkdir -p webui/submodules 16 | 17 | # Remove existing symbolic link or directory if exists 18 | if [ -e webui/submodules/FramePack ]; then 19 | echo "Removing existing FramePack directory..." 20 | rm -rf webui/submodules/FramePack 21 | fi 22 | 23 | echo "Initializing submodules..." 24 | 25 | # Add original repository as a submodule 26 | # (Not needed if submodule is already configured) 27 | if ! grep -q "\[submodule \"webui/submodules/FramePack\"\]" .gitmodules 2>/dev/null; then 28 | echo "Adding original FramePack repository as a submodule..." 29 | git submodule add https://github.com/lllyasviel/FramePack.git webui/submodules/FramePack 30 | else 31 | echo "Updating submodules..." 32 | git submodule update --init --recursive 33 | fi 34 | 35 | # Run update script to complete initial setup 36 | echo "Applying FramePack-eichi files..." 37 | "$SCRIPT_DIR/update.sh" 38 | 39 | echo "Submodule setup completed!" -------------------------------------------------------------------------------- /linux/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # FramePack-eichi Update Script 3 | # Updates the original repository and applies FramePack-eichi features 4 | # Note: Unofficial support - no warranty 5 | 6 | set -e 7 | 8 | # Get the script directory 9 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 | 11 | # Navigate to FramePack-eichi root directory (parent of script directory) 12 | cd "$(dirname "$SCRIPT_DIR")" 13 | 14 | echo "=== FramePack-eichi Update ===" 15 | echo "1. Updating submodules..." 16 | 17 | # Update submodules 18 | if [ -d "webui/submodules/FramePack/.git" ]; then 19 | cd webui/submodules/FramePack 20 | git pull origin main 21 | # Return to root directory 22 | cd "$(dirname "$SCRIPT_DIR")" 23 | else 24 | # Initialize submodules if they don't exist 25 | echo "Initializing submodules..." 26 | git submodule update --init --recursive 27 | fi 28 | 29 | echo "2. Overwriting FramePack-eichi files..." 30 | 31 | # Copy eichi-specific files 32 | mkdir -p webui/submodules/FramePack/webui/eichi_utils 33 | cp -r webui/eichi_utils/* webui/submodules/FramePack/webui/eichi_utils/ 34 | 35 | mkdir -p webui/submodules/FramePack/webui/lora_utils 36 | cp -r webui/lora_utils/* webui/submodules/FramePack/webui/lora_utils/ 37 | 38 | mkdir -p webui/submodules/FramePack/webui/diffusers_helper 39 | cp -r webui/diffusers_helper/* webui/submodules/FramePack/webui/diffusers_helper/ 40 | 41 | mkdir -p webui/submodules/FramePack/webui/locales 42 | cp -r webui/locales/* webui/submodules/FramePack/webui/locales/ 43 | 44 | # Copy main script files 45 | cp webui/endframe_ichi.py webui/submodules/FramePack/webui/ 46 | cp webui/endframe_ichi_f1.py webui/submodules/FramePack/webui/ 47 | cp webui/oneframe_ichi.py webui/submodules/FramePack/webui/ 48 | 49 | # Create settings directory and copy settings files 50 | mkdir -p webui/submodules/FramePack/webui/settings 51 | if [ -d "webui/settings" ]; then 52 | cp -n webui/settings/* webui/submodules/FramePack/webui/settings/ 2>/dev/null || true 53 | fi 54 | 55 | echo "3. Setting permissions for execution scripts..." 56 | chmod +x "$SCRIPT_DIR"/*.sh 2>/dev/null || true 57 | 58 | echo "=== Update Complete! ===" 59 | echo "FramePack-eichi files have been successfully applied." -------------------------------------------------------------------------------- /run_endframe_ichi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_en.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser --lang en 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_en_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang en 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_ru.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser --lang ru 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_ru_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang ru 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_zh-tw.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser --lang zh-tw 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_endframe_ichi_zh-tw_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang zh-tw 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_oneframe_ichi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_oneframe_ichi_en.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --lang en --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_oneframe_ichi_ru.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --inbrowser --lang ru 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /run_oneframe_ichi_zh-tw.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --lang zh-tw --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for FramePack-eichi 2 | FROM nvidia/cuda:12.6.0-devel-ubuntu22.04 3 | 4 | # Set environment variables 5 | ENV DEBIAN_FRONTEND=noninteractive \ 6 | PYTHONUNBUFFERED=1 7 | 8 | # Install system dependencies 9 | RUN apt-get update && apt-get install -y --no-install-recommends \ 10 | git \ 11 | python3.10 \ 12 | python3.10-dev \ 13 | python3-pip \ 14 | python3-venv \ 15 | ffmpeg \ 16 | libsm6 \ 17 | libxext6 \ 18 | libgl1 \ 19 | build-essential \ 20 | wget \ 21 | curl \ 22 | && apt-get clean \ 23 | && rm -rf /var/lib/apt/lists/* 24 | 25 | # Create symbolic links for Python 26 | RUN ln -sf /usr/bin/python3.10 /usr/bin/python && \ 27 | ln -sf /usr/bin/pip3 /usr/bin/pip 28 | 29 | # Upgrade pip and install build dependencies 30 | RUN pip install --upgrade pip 31 | RUN pip install packaging wheel setuptools ninja 32 | 33 | # Install PyTorch with CUDA 12.6 support 34 | RUN pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu126 35 | 36 | # Install acceleration libraries one by one to better diagnose any issues 37 | RUN pip install xformers==0.0.29.post3 --no-deps --index-url https://download.pytorch.org/whl/cu126 --no-cache-dir 38 | RUN pip install triton==2.2.0 --no-cache-dir 39 | RUN pip install -U "huggingface_hub[cli]" 40 | 41 | # Install flash-attention with the closest available version 42 | RUN pip install flash-attn==2.7.4.post1 43 | 44 | # Try installing sageattention 45 | # Option 1: Install from git repository directly 46 | RUN pip install sageattention 47 | 48 | # Install remaining packages 49 | RUN pip install pynvml "jinja2>=3.1.2" peft 50 | 51 | # Create working directory 52 | WORKDIR /app 53 | 54 | # Clone original FramePack repository (needed for the base functionality) 55 | RUN git clone https://github.com/lllyasviel/FramePack.git /app/framepack 56 | 57 | # Install dependencies 58 | WORKDIR /app/framepack 59 | RUN pip install -r requirements.txt 60 | 61 | # Clone FramePack-eichi repository 62 | RUN git clone https://github.com/git-ai-code/FramePack-eichi /tmp/framepack-eichi 63 | 64 | # Copy FramePack-eichi files 65 | RUN cp -rf /tmp/framepack-eichi/webui/* /app/framepack/ 66 | 67 | # Create a simple startup script with better error handling 68 | RUN echo '#!/bin/bash' > /app/start.sh && \ 69 | echo 'cd /app/framepack' >> /app/start.sh && \ 70 | echo 'echo "Starting demo_gradio.py..."' >> /app/start.sh && \ 71 | echo 'python demo_gradio.py --server 0.0.0.0 --port 7860 &' >> /app/start.sh && \ 72 | echo 'SERVER_PID=$!' >> /app/start.sh && \ 73 | echo 'sleep 5' >> /app/start.sh && \ 74 | echo 'if [ -f "endframe_ichi.py" ]; then' >> /app/start.sh && \ 75 | echo ' echo "Starting endframe_ichi.py..."' >> /app/start.sh && \ 76 | echo ' python endframe_ichi.py --server 0.0.0.0 --port 7861 "$@"' >> /app/start.sh && \ 77 | echo 'fi' >> /app/start.sh && \ 78 | chmod +x /app/start.sh 79 | 80 | # Set working directory for when container starts 81 | WORKDIR /app/framepack 82 | 83 | # Command to run when container starts 84 | ENTRYPOINT ["/app/start.sh"] 85 | 86 | # Default arguments (can be overridden) 87 | CMD ["--lang", "en", "--listen"] -------------------------------------------------------------------------------- /version/v1.9.3/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | framepack-eichi: 3 | build: 4 | context: . 5 | dockerfile: Dockerfile 6 | args: 7 | - TZ=Europe/Brussels 8 | ports: 9 | - "7861:7861" 10 | volumes: 11 | - ./data:/app/framepack/data 12 | - ./models:/app/framepack/hf_download 13 | - ./outputs:/app/framepack/outputs 14 | environment: 15 | - NVIDIA_VISIBLE_DEVICES=all 16 | - NVIDIA_DRIVER_CAPABILITIES=all 17 | deploy: 18 | resources: 19 | reservations: 20 | devices: 21 | - driver: nvidia 22 | count: 1 23 | capabilities: [gpu] 24 | restart: unless-stopped 25 | command: ["--lang", "en"] # Options: "en", "ja", "zh-tw" 26 | -------------------------------------------------------------------------------- /version/v1.9.3/run_endframe_ichi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_endframe_ichi_en.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser --lang en 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_endframe_ichi_en_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang en 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_endframe_ichi_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_endframe_ichi_zh-tw.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi.py --server 127.0.0.1 --inbrowser --lang zh-tw 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_endframe_ichi_zh-tw_f1.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" endframe_ichi_f1.py --server 127.0.0.1 --inbrowser --lang zh-tw 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_oneframe_ichi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_oneframe_ichi_en.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --lang en --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/run_oneframe_ichi_zh-tw.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call environment.bat 4 | 5 | cd %~dp0webui 6 | 7 | "%DIR%\python\python.exe" oneframe_ichi.py --server 127.0.0.1 --lang zh-tw --inbrowser 8 | 9 | :done 10 | pause -------------------------------------------------------------------------------- /version/v1.9.3/webui/diffusers_helper/bucket_tools.py: -------------------------------------------------------------------------------- 1 | # 安全な解像度値のリスト 2 | SAFE_RESOLUTIONS = [512, 640, 768, 960, 1080] 3 | 4 | # 標準解像度のバケット設定 5 | bucket_options = { 6 | 512: [ 7 | (352, 704), 8 | (384, 640), 9 | (416, 608), 10 | (448, 576), 11 | (480, 544), 12 | (512, 512), 13 | (544, 480), 14 | (576, 448), 15 | (608, 416), 16 | (640, 384), 17 | (704, 352), 18 | ], 19 | 640: [ 20 | (416, 960), 21 | (448, 864), 22 | (480, 832), 23 | (512, 768), 24 | (544, 704), 25 | (576, 672), 26 | (608, 640), 27 | (640, 608), 28 | (672, 576), 29 | (704, 544), 30 | (768, 512), 31 | (832, 480), 32 | (864, 448), 33 | (960, 416), 34 | ], 35 | 768: [ 36 | (512, 1024), 37 | (576, 960), 38 | (640, 896), 39 | (704, 832), 40 | (768, 768), 41 | (832, 704), 42 | (896, 640), 43 | (960, 576), 44 | (1024, 512), 45 | ], 46 | 960: [ 47 | (640, 1280), 48 | (704, 1152), 49 | (768, 1024), 50 | (832, 960), 51 | (896, 896), 52 | (960, 832), 53 | (1024, 768), 54 | (1152, 704), 55 | (1280, 640), 56 | ], 57 | 1080: [ 58 | (720, 1440), 59 | (768, 1344), 60 | (832, 1248), 61 | (896, 1152), 62 | (960, 1080), 63 | (1024, 1024), 64 | (1080, 960), 65 | (1152, 896), 66 | (1248, 832), 67 | (1344, 768), 68 | (1440, 720), 69 | ], 70 | } 71 | 72 | 73 | def find_nearest_bucket(h, w, resolution=640): 74 | """最も適切なアスペクト比のバケットを見つける関数""" 75 | # 安全な解像度に丸める 76 | if resolution not in SAFE_RESOLUTIONS: 77 | # 最も近い安全な解像度を選択 78 | closest_resolution = min(SAFE_RESOLUTIONS, key=lambda x: abs(x - resolution)) 79 | print(f"Warning: Resolution {resolution} is not in safe list. Using {closest_resolution} instead.") 80 | resolution = closest_resolution 81 | 82 | min_metric = float('inf') 83 | best_bucket = None 84 | for (bucket_h, bucket_w) in bucket_options[resolution]: 85 | # アスペクト比の差を計算 86 | metric = abs(h * bucket_w - w * bucket_h) 87 | if metric <= min_metric: 88 | min_metric = metric 89 | best_bucket = (bucket_h, bucket_w) 90 | 91 | return best_bucket 92 | 93 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/diffusers_helper/memory.py: -------------------------------------------------------------------------------- 1 | # By lllyasviel 2 | 3 | 4 | import torch 5 | 6 | 7 | cpu = torch.device('cpu') 8 | gpu = torch.device(f'cuda:{torch.cuda.current_device()}') 9 | gpu_complete_modules = [] 10 | 11 | 12 | class DynamicSwapInstaller: 13 | @staticmethod 14 | def _install_module(module: torch.nn.Module, **kwargs): 15 | original_class = module.__class__ 16 | module.__dict__['forge_backup_original_class'] = original_class 17 | 18 | def hacked_get_attr(self, name: str): 19 | if '_parameters' in self.__dict__: 20 | _parameters = self.__dict__['_parameters'] 21 | if name in _parameters: 22 | p = _parameters[name] 23 | if p is None: 24 | return None 25 | if p.__class__ == torch.nn.Parameter: 26 | return torch.nn.Parameter(p.to(**kwargs), requires_grad=p.requires_grad) 27 | else: 28 | return p.to(**kwargs) 29 | if '_buffers' in self.__dict__: 30 | _buffers = self.__dict__['_buffers'] 31 | if name in _buffers: 32 | return _buffers[name].to(**kwargs) 33 | return super(original_class, self).__getattr__(name) 34 | 35 | module.__class__ = type('DynamicSwap_' + original_class.__name__, (original_class,), { 36 | '__getattr__': hacked_get_attr, 37 | }) 38 | 39 | return 40 | 41 | @staticmethod 42 | def _uninstall_module(module: torch.nn.Module): 43 | if 'forge_backup_original_class' in module.__dict__: 44 | module.__class__ = module.__dict__.pop('forge_backup_original_class') 45 | return 46 | 47 | @staticmethod 48 | def install_model(model: torch.nn.Module, **kwargs): 49 | for m in model.modules(): 50 | DynamicSwapInstaller._install_module(m, **kwargs) 51 | return 52 | 53 | @staticmethod 54 | def uninstall_model(model: torch.nn.Module): 55 | for m in model.modules(): 56 | DynamicSwapInstaller._uninstall_module(m) 57 | return 58 | 59 | 60 | def fake_diffusers_current_device(model: torch.nn.Module, target_device: torch.device): 61 | if hasattr(model, 'scale_shift_table'): 62 | model.scale_shift_table.data = model.scale_shift_table.data.to(target_device) 63 | return 64 | 65 | for k, p in model.named_modules(): 66 | if hasattr(p, 'weight'): 67 | p.to(target_device) 68 | return 69 | 70 | 71 | def get_cuda_free_memory_gb(device=None): 72 | if device is None: 73 | device = gpu 74 | 75 | memory_stats = torch.cuda.memory_stats(device) 76 | bytes_active = memory_stats['active_bytes.all.current'] 77 | bytes_reserved = memory_stats['reserved_bytes.all.current'] 78 | bytes_free_cuda, _ = torch.cuda.mem_get_info(device) 79 | bytes_inactive_reserved = bytes_reserved - bytes_active 80 | bytes_total_available = bytes_free_cuda + bytes_inactive_reserved 81 | return bytes_total_available / (1024 ** 3) 82 | 83 | 84 | def move_model_to_device_with_memory_preservation(model, target_device, preserved_memory_gb=0): 85 | print(f'Moving {model.__class__.__name__} to {target_device} with preserved memory: {preserved_memory_gb} GB') 86 | 87 | for m in model.modules(): 88 | if get_cuda_free_memory_gb(target_device) <= preserved_memory_gb: 89 | torch.cuda.empty_cache() 90 | return 91 | 92 | if hasattr(m, 'weight'): 93 | m.to(device=target_device) 94 | 95 | model.to(device=target_device) 96 | torch.cuda.empty_cache() 97 | return 98 | 99 | 100 | def offload_model_from_device_for_memory_preservation(model, target_device, preserved_memory_gb=0): 101 | print(f'Offloading {model.__class__.__name__} from {target_device} to preserve memory: {preserved_memory_gb} GB') 102 | 103 | for m in model.modules(): 104 | if get_cuda_free_memory_gb(target_device) >= preserved_memory_gb: 105 | torch.cuda.empty_cache() 106 | return 107 | 108 | if hasattr(m, 'weight'): 109 | m.to(device=cpu) 110 | 111 | model.to(device=cpu) 112 | torch.cuda.empty_cache() 113 | return 114 | 115 | 116 | def unload_complete_models(*args): 117 | for m in gpu_complete_modules + list(args): 118 | if m is None: 119 | continue 120 | m.to(device=cpu) 121 | print(f'Unloaded {m.__class__.__name__} as complete.') 122 | 123 | gpu_complete_modules.clear() 124 | torch.cuda.empty_cache() 125 | return 126 | 127 | 128 | def load_model_as_complete(model, target_device, unload=True): 129 | if unload: 130 | unload_complete_models() 131 | 132 | model.to(device=target_device) 133 | print(f'Loaded {model.__class__.__name__} to {target_device} as complete.') 134 | 135 | gpu_complete_modules.append(model) 136 | return 137 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | eichi_utils - FramePack-eichiのユーティリティモジュール 3 | プリセット管理、設定管理、キーフレーム処理、動画モード設定などを含む 4 | """ 5 | 6 | __version__ = "1.0.0" 7 | 8 | # 外部モジュールからアクセスできるようにエクスポート 9 | from .vae_settings import ( 10 | load_vae_settings, 11 | save_vae_settings, 12 | apply_vae_settings, 13 | create_vae_settings_ui, 14 | get_current_vae_settings_display 15 | ) 16 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/frame_calculator.py: -------------------------------------------------------------------------------- 1 | """ 2 | フレームサイズに基づくセクション数計算モジュール 3 | 0.5秒/1秒モードに対応したセクション数の計算機能を提供します 4 | """ 5 | 6 | import math 7 | from eichi_utils.video_mode_settings import VIDEO_MODE_SETTINGS 8 | 9 | from locales import i18n_extended 10 | 11 | def calculate_frames_per_section(latent_window_size=9): 12 | """1セクションあたりのフレーム数を計算""" 13 | return latent_window_size * 4 - 3 14 | 15 | 16 | def calculate_sections_from_frames(total_frames, latent_window_size=9): 17 | """フレーム数から必要なセクション数を計算""" 18 | frames_per_section = calculate_frames_per_section(latent_window_size) 19 | return math.ceil(total_frames / frames_per_section) 20 | 21 | 22 | def calculate_total_frame_count(sections, latent_window_size=9): 23 | """セクション数から総フレーム数を計算""" 24 | frames_per_section = calculate_frames_per_section(latent_window_size) 25 | return sections * frames_per_section 26 | 27 | 28 | def calculate_total_second_length(frames, fps=30): 29 | """フレーム数から秒数を計算""" 30 | return frames / fps 31 | 32 | 33 | def calculate_sections_for_mode_and_size(mode_key, frame_size_setting=None): 34 | """動画モードとフレームサイズ設定から必要なセクション数を計算""" 35 | # 動画モードからフレーム数を取得 36 | if mode_key not in VIDEO_MODE_SETTINGS: 37 | return 15 # デフォルト値 38 | 39 | total_frames = VIDEO_MODE_SETTINGS[mode_key]["frames"] 40 | 41 | # フレームサイズ設定からlatent_window_sizeを判定 42 | frame_size_internal_key = i18n_extended.get_internal_key(frame_size_setting) 43 | if frame_size_internal_key == "_KEY_FRAME_SIZE_05SEC": 44 | latent_window_size = 4.5 # 0.5秒モード 45 | else: 46 | latent_window_size = 9 # 1秒モードがデフォルト 47 | 48 | # 必要なセクション数を計算 49 | required_sections = calculate_sections_from_frames(total_frames, latent_window_size) 50 | 51 | # デバッグ情報 52 | frames_per_section = calculate_frames_per_section(latent_window_size) 53 | print(i18n_extended.translate("計算詳細: モード={mode_key}, フレームサイズ={frame_size_setting}, 総フレーム数={total_frames}, セクションあたり={frames_per_section}フレーム, 必要セクション数={required_sections}").format(mode_key=mode_key, frame_size_setting=frame_size_setting, total_frames=total_frames, frames_per_section=frames_per_section, required_sections=required_sections)) 54 | 55 | # 結果を返す 56 | return required_sections 57 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/keyframe_handler_extended.py: -------------------------------------------------------------------------------- 1 | """ 2 | 拡張キーフレーム処理モジュール 3 | キーフレーム処理に関する拡張機能を提供します 4 | """ 5 | 6 | import gradio as gr 7 | 8 | from locales.i18n_extended import translate 9 | 10 | from eichi_utils.video_mode_settings import ( 11 | get_total_sections, 12 | get_important_keyframes, 13 | get_video_seconds, 14 | MODE_TYPE_LOOP 15 | ) 16 | from eichi_utils.keyframe_handler import code_to_ui_index, get_max_keyframes_count 17 | from eichi_utils.frame_calculator import calculate_sections_for_mode_and_size 18 | 19 | def extended_mode_length_change_handler(mode, length, section_number_inputs, section_row_groups=None, frame_size_setting="_KEY_FRAME_SIZE_1SEC", enable_keyframe_copy=None): 20 | """モードと動画長の変更を統一的に処理する関数(セクション行の表示/非表示制御を追加) 21 | 22 | Args: 23 | mode: モード ("通常" or "ループ") 24 | length: 動画長 ("6秒", "8秒", "10秒", "12秒", "16秒", "20秒") 25 | section_number_inputs: セクション番号入力欄のリスト 26 | section_row_groups: セクション行のUIグループリスト(オプション) 27 | frame_size_setting: フレームサイズ設定 ("1秒 (33フレーム)" or "0.5秒 (17フレーム)") 28 | enable_keyframe_copy: キーフレーム自動コピー機能の有効/無効状態(オプション) 29 | 30 | Returns: 31 | 更新リスト: 各UI要素の更新情報のリスト 32 | """ 33 | # 通常モードでは全ての赤枠青枠を強制的に非表示にする処理を追加 34 | is_loop_mode = (mode == MODE_TYPE_LOOP) 35 | 36 | # キーフレーム自動コピー機能の状態を確認 37 | # モードとコピー有効フラグの両方が指定されている場合のみコピーを有効にする 38 | if is_loop_mode and enable_keyframe_copy is not None: 39 | is_copy_enabled = enable_keyframe_copy 40 | print(translate("[keyframe_handler_extended] ループモードでキーフレーム自動コピー機能の状態: {state}").format(state=is_copy_enabled)) 41 | else: 42 | # デフォルト状態を使用 - ループモードならTrue、それ以外はFalse 43 | is_copy_enabled = is_loop_mode 44 | print(translate("[keyframe_handler_extended] モード変更によるキーフレーム自動コピー機能のデフォルト状態: {state}").format(state=is_copy_enabled)) 45 | 46 | if not is_loop_mode: 47 | print(translate("[keyframe_handler_extended] 通常モードで強制的に赤枠/青枠を非表示に設定")) 48 | else: 49 | print(translate("[keyframe_handler_extended] ループモードで赤枠/青枠を表示可能に設定")) 50 | 51 | # 基本要素のクリア(入力画像と終了フレーム) 52 | updates = [gr.update(value=None) for _ in range(2)] 53 | 54 | # すべてのキーフレーム画像をクリア 55 | section_image_count = get_max_keyframes_count() 56 | for _ in range(section_image_count): 57 | updates.append(gr.update(value=None, elem_classes="")) 58 | 59 | # セクション番号ラベルをリセット 60 | for i in range(len(section_number_inputs)): 61 | section_number_inputs[i].elem_classes = "" 62 | 63 | # 重要なキーフレームを強調表示 64 | important_kfs = get_important_keyframes(length) 65 | for idx in important_kfs: 66 | ui_idx = code_to_ui_index(idx) 67 | update_idx = ui_idx + 1 # 入力画像と終了フレームの2つを考慮 68 | if update_idx < len(updates): 69 | # 通常モードの場合はすべての枠を非表示にする 70 | if not is_loop_mode: 71 | updates[update_idx] = gr.update(value=None, elem_classes="") 72 | if idx < len(section_number_inputs): 73 | section_number_inputs[idx].elem_classes = "" 74 | continue 75 | 76 | # ループモードでもキーフレーム自動コピー機能が無効なら枠を表示しない 77 | if not is_copy_enabled: 78 | updates[update_idx] = gr.update(value=None, elem_classes="") 79 | if idx < len(section_number_inputs): 80 | section_number_inputs[idx].elem_classes = "" 81 | continue 82 | 83 | # ループモードでキーフレーム自動コピー機能が有効の場合のみセクションによって枠の色を変える 84 | if idx == 0: 85 | # セクション0は赤枠 86 | updates[update_idx] = gr.update(value=None, elem_classes="highlighted-keyframe-red") 87 | if idx < len(section_number_inputs): 88 | section_number_inputs[idx].elem_classes = "highlighted-label-red" 89 | elif idx == 1: 90 | # セクション1は青枠 91 | updates[update_idx] = gr.update(value=None, elem_classes="highlighted-keyframe-blue") 92 | if idx < len(section_number_inputs): 93 | section_number_inputs[idx].elem_classes = "highlighted-label-blue" 94 | else: 95 | # その他のセクションは通常の枠 96 | updates[update_idx] = gr.update(value=None, elem_classes="highlighted-keyframe") 97 | if idx < len(section_number_inputs): 98 | section_number_inputs[idx].elem_classes = "highlighted-label" 99 | 100 | # ループモードの場合はキーフレーム0も強調(まだ強調されていない場合) 101 | # セクション0は赤枠にする - ループモードでコピー機能が有効の場合のみ 102 | if is_loop_mode and is_copy_enabled and 0 not in important_kfs: 103 | print(translate("[keyframe_handler_extended] ループモードでセクション0に赤枠を適用")) 104 | updates[2] = gr.update(value=None, elem_classes="highlighted-keyframe-red") 105 | if 0 < len(section_number_inputs): 106 | section_number_inputs[0].elem_classes = "highlighted-label-red" 107 | elif not (is_loop_mode and is_copy_enabled): 108 | print(translate("[keyframe_handler_extended] モードまたはコピー機能状態により、セクション0の赤枠を適用せず")) 109 | # 通常モードまたはコピー機能が無効の場合は強制的にクリア 110 | updates[2] = gr.update(value=None, elem_classes="") 111 | 112 | # 動画長の設定 113 | video_length = get_video_seconds(length) 114 | 115 | # 最終的な動画長設定を追加 116 | updates.append(gr.update(value=video_length)) 117 | 118 | # セクション行の表示/非表示を制御 119 | if section_row_groups is not None: 120 | # 動画モードとフレームサイズから必要なセクション数を計算 121 | required_sections = calculate_sections_for_mode_and_size(length, frame_size_setting) 122 | print(translate("動画モード '{length}' とフレームサイズ '{frame_size_setting}' で必要なセクション数: {required_sections}").format(length=length, frame_size_setting=frame_size_setting, required_sections=required_sections)) 123 | 124 | # 各セクション行の表示/非表示を設定 125 | row_updates = [] 126 | for i, _ in enumerate(section_row_groups): 127 | if i < required_sections: 128 | row_updates.append(gr.update(visible=True)) 129 | else: 130 | row_updates.append(gr.update(visible=False)) 131 | 132 | # 更新リストに行の表示/非表示設定を追加 133 | updates.extend(row_updates) 134 | 135 | # キーフレーム自動コピー機能のチェックボックス更新は直接出力に追加しない 136 | # 呼び出し元で必要なら別途追加する方が安全 137 | # (既存の出力要素数が変更されると表示崩れの原因になる) 138 | 139 | return updates 140 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/lora_preset_manager.py: -------------------------------------------------------------------------------- 1 | """ 2 | LoRAプリセット管理モジュール 3 | LoRA選択とスケール値のプリセット保存・読み込み機能を提供 4 | """ 5 | 6 | import os 7 | import json 8 | import traceback 9 | from datetime import datetime 10 | 11 | from locales.i18n_extended import translate 12 | 13 | def get_lora_presets_folder_path(): 14 | """LoRAプリセットフォルダの絶対パスを取得する""" 15 | # eichi_utils直下からwebuiフォルダに移動し、presetsフォルダを使用 16 | webui_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | return os.path.join(webui_path, 'presets') 18 | 19 | def initialize_lora_presets(): 20 | """初期LoRAプリセットファイルがない場合に作成する関数""" 21 | presets_folder = get_lora_presets_folder_path() 22 | os.makedirs(presets_folder, exist_ok=True) 23 | preset_file = os.path.join(presets_folder, 'lora_presets.json') 24 | 25 | # デフォルトのLoRA設定 26 | default_lora_configs = [ 27 | { 28 | "name": translate("デフォルト設定1"), 29 | "lora1": translate("なし"), 30 | "lora2": translate("なし"), 31 | "lora3": translate("なし"), 32 | "scales": "0.8,0.8,0.8" 33 | }, 34 | { 35 | "name": translate("デフォルト設定2"), 36 | "lora1": translate("なし"), 37 | "lora2": translate("なし"), 38 | "lora3": translate("なし"), 39 | "scales": "1.0,0.5,0.3" 40 | } 41 | ] 42 | 43 | # 既存ファイルがあり、正常に読み込める場合は終了 44 | if os.path.exists(preset_file): 45 | try: 46 | with open(preset_file, 'r', encoding='utf-8') as f: 47 | presets_data = json.load(f) 48 | return 49 | except: 50 | # エラーが発生した場合は新規作成 51 | pass 52 | 53 | # 新規作成 54 | presets_data = { 55 | "presets": [], 56 | "default_preset_index": 0 57 | } 58 | 59 | # デフォルトのプリセットを追加 60 | for i, config in enumerate(default_lora_configs): 61 | presets_data["presets"].append({ 62 | "name": config["name"], 63 | "lora1": config["lora1"], 64 | "lora2": config["lora2"], 65 | "lora3": config["lora3"], 66 | "scales": config["scales"], 67 | "timestamp": datetime.now().isoformat(), 68 | "is_default": True 69 | }) 70 | 71 | # 保存 72 | try: 73 | with open(preset_file, 'w', encoding='utf-8') as f: 74 | json.dump(presets_data, f, ensure_ascii=False, indent=2) 75 | except: 76 | # 保存に失敗してもエラーは出さない(次回起動時に再試行される) 77 | pass 78 | 79 | def load_lora_presets(): 80 | """LoRAプリセットを読み込む関数""" 81 | presets_folder = get_lora_presets_folder_path() 82 | os.makedirs(presets_folder, exist_ok=True) 83 | preset_file = os.path.join(presets_folder, 'lora_presets.json') 84 | 85 | # 初期化(必要に応じて) 86 | initialize_lora_presets() 87 | 88 | # プリセットファイルを読み込む 89 | try: 90 | with open(preset_file, 'r', encoding='utf-8') as f: 91 | data = json.load(f) 92 | return data["presets"], data.get("default_preset_index", 0) 93 | except: 94 | # エラーの場合は空のプリセットリストを返す 95 | return [], 0 96 | 97 | def save_lora_preset(preset_index, lora1, lora2, lora3, scales): 98 | """LoRAプリセットを保存する関数""" 99 | presets_folder = get_lora_presets_folder_path() 100 | os.makedirs(presets_folder, exist_ok=True) 101 | preset_file = os.path.join(presets_folder, 'lora_presets.json') 102 | 103 | # 既存のプリセットを読み込む 104 | try: 105 | with open(preset_file, 'r', encoding='utf-8') as f: 106 | data = json.load(f) 107 | except: 108 | data = {"presets": [], "default_preset_index": 0} 109 | 110 | # 5つのプリセットを確保 111 | while len(data["presets"]) < 5: 112 | data["presets"].append({ 113 | "name": translate("設定{0}").format(len(data["presets"]) + 1), 114 | "lora1": translate("なし"), 115 | "lora2": translate("なし"), 116 | "lora3": translate("なし"), 117 | "scales": "0.8,0.8,0.8", 118 | "timestamp": datetime.now().isoformat(), 119 | "is_default": False 120 | }) 121 | 122 | # 指定されたプリセットを更新 123 | if 0 <= preset_index < 5: 124 | data["presets"][preset_index] = { 125 | "name": translate("設定{0}").format(preset_index + 1), 126 | "lora1": lora1 or translate("なし"), 127 | "lora2": lora2 or translate("なし"), 128 | "lora3": lora3 or translate("なし"), 129 | "scales": scales or "0.8,0.8,0.8", 130 | "timestamp": datetime.now().isoformat(), 131 | "is_default": False 132 | } 133 | 134 | # 保存 135 | with open(preset_file, 'w', encoding='utf-8') as f: 136 | json.dump(data, f, ensure_ascii=False, indent=2) 137 | 138 | return True, translate("設定{0}を保存しました").format(preset_index + 1) 139 | else: 140 | return False, translate("無効なプリセット番号です") 141 | 142 | def load_lora_preset(preset_index): 143 | """指定されたプリセットを読み込む関数""" 144 | presets, _ = load_lora_presets() 145 | 146 | if 0 <= preset_index < len(presets): 147 | preset = presets[preset_index] 148 | return ( 149 | preset.get("lora1", translate("なし")), 150 | preset.get("lora2", translate("なし")), 151 | preset.get("lora3", translate("なし")), 152 | preset.get("scales", "0.8,0.8,0.8") 153 | ) 154 | else: 155 | # デフォルト値を返す 156 | return None 157 | 158 | def get_preset_names(): 159 | """プリセット名のリストを取得する関数""" 160 | presets, _ = load_lora_presets() 161 | names = [] 162 | for i in range(5): 163 | if i < len(presets): 164 | names.append(presets[i].get("name", translate("設定{0}").format(i + 1))) 165 | else: 166 | names.append(translate("設定{0}").format(i + 1)) 167 | return names -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/model_downloader.py: -------------------------------------------------------------------------------- 1 | import os 2 | from concurrent.futures import ThreadPoolExecutor, as_completed 3 | from huggingface_hub import snapshot_download 4 | 5 | class ModelDownloader: 6 | def __init__(self, max_workers_per_model=4): 7 | # max_parallel_models > 1 は tqdm._lock が競合し異常終了するため、当面は1に固定する 8 | self.max_parallel_models = 1 9 | self.max_workers_per_model = max_workers_per_model 10 | 11 | def _download_models(self, models_to_download): 12 | def download_model(model_info): 13 | kwargs = { 14 | "repo_id": model_info["repo_id"], 15 | "allow_patterns": model_info.get("allow_patterns", "*"), 16 | "max_workers": self.max_workers_per_model, 17 | } 18 | snapshot_download(**kwargs) 19 | 20 | with ThreadPoolExecutor(max_workers=self.max_parallel_models) as executor: 21 | futures = [executor.submit(download_model, model) for model in models_to_download] 22 | for future in as_completed(futures): 23 | future.result() 24 | 25 | def download_original(self): 26 | self._download_models([ 27 | {"repo_id": "hunyuanvideo-community/HunyuanVideo", "allow_patterns": ["tokenizer/*", "tokenizer_2/*", "vae/*", "text_encoder/*", "text_encoder_2/*"]}, 28 | {"repo_id": "lllyasviel/flux_redux_bfl", "allow_patterns": ["feature_extractor/*", "image_encoder/*"]}, 29 | {"repo_id": "lllyasviel/FramePackI2V_HY"}, 30 | ]) 31 | 32 | def download_f1(self): 33 | self._download_models([ 34 | {"repo_id": "hunyuanvideo-community/HunyuanVideo", "allow_patterns": ["tokenizer/*", "tokenizer_2/*", "vae/*", "text_encoder/*", "text_encoder_2/*"]}, 35 | {"repo_id": "lllyasviel/flux_redux_bfl", "allow_patterns": ["feature_extractor/*", "image_encoder/*"]}, 36 | {"repo_id": "lllyasviel/FramePack_F1_I2V_HY_20250503"}, 37 | ]) 38 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/png_metadata.py: -------------------------------------------------------------------------------- 1 | # png_metadata.py 2 | # FramePack-eichiのためのPNGメタデータ処理ユーティリティ 3 | 4 | import json 5 | import os 6 | from PIL import Image, PngImagePlugin 7 | import traceback 8 | 9 | from locales.i18n_extended import translate 10 | 11 | # メタデータキーの定義 12 | PROMPT_KEY = "prompt" 13 | SEED_KEY = "seed" 14 | SECTION_PROMPT_KEY = "section_prompt" 15 | SECTION_NUMBER_KEY = "section_number" 16 | PARAMETERS_KEY = "parameters" # SD系との互換性のため 17 | 18 | def embed_metadata_to_png(image_path, metadata_dict): 19 | """PNGファイルにメタデータを埋め込む 20 | 21 | Args: 22 | image_path (str): PNGファイルのパス 23 | metadata_dict (dict): 埋め込むメタデータの辞書 24 | 25 | Returns: 26 | str: 処理したファイルのパス 27 | """ 28 | try: 29 | # print(translate("[DEBUG] メタデータ埋め込み開始: {0}").format(image_path)) 30 | # print(translate("[DEBUG] 埋め込むメタデータ: {0}").format(metadata_dict)) 31 | 32 | img = Image.open(image_path) 33 | metadata = PngImagePlugin.PngInfo() 34 | 35 | # パラメータを結合したテキストも作成(SD系との互換性のため) 36 | parameters_text = "" 37 | 38 | # パラメータテキストの構築(個別キーは埋め込まない) 39 | for key, value in metadata_dict.items(): 40 | if value is not None: 41 | if key == PROMPT_KEY: 42 | parameters_text += f"{value}\n" 43 | elif key == SEED_KEY: 44 | parameters_text += f"Seed: {value}\n" 45 | elif key == SECTION_PROMPT_KEY: 46 | parameters_text += f"Section Prompt: {value}\n" 47 | elif key == SECTION_NUMBER_KEY: 48 | parameters_text += f"Section Number: {value}\n" 49 | 50 | # パラメータテキストがあれば追加 51 | if parameters_text: 52 | metadata.add_text(PARAMETERS_KEY, parameters_text.strip()) 53 | # print(translate("[DEBUG] parameters形式でメタデータ追加: {0}").format(parameters_text.strip())) 54 | 55 | # 保存(ファイル形式は変更せず) 56 | if image_path.lower().endswith('.png'): 57 | img.save(image_path, "PNG", pnginfo=metadata) 58 | else: 59 | # PNGでなければPNGに変換して保存 60 | png_path = os.path.splitext(image_path)[0] + '.png' 61 | img.save(png_path, "PNG", pnginfo=metadata) 62 | image_path = png_path 63 | 64 | # print(translate("[DEBUG] メタデータを埋め込みました: {0}").format(image_path)) 65 | return image_path 66 | 67 | except Exception as e: 68 | # print(translate("[ERROR] メタデータ埋め込みエラー: {0}").format(e)) 69 | # traceback.print_exc() 70 | return image_path 71 | 72 | def extract_metadata_from_png(image_path_or_object): 73 | """PNGファイルからメタデータを抽出する 74 | 75 | Args: 76 | image_path_or_object: PNGファイルのパスまたはPIL.Image.Imageオブジェクト 77 | 78 | Returns: 79 | dict: 抽出したメタデータの辞書 80 | """ 81 | try: 82 | # パスが文字列ならイメージを開く、イメージオブジェクトならそのまま使う 83 | if isinstance(image_path_or_object, str): 84 | if not os.path.exists(image_path_or_object): 85 | # print(translate("[DEBUG] ファイルが存在しません: {0}").format(image_path_or_object)) 86 | return {} 87 | # print(translate("[DEBUG] 画像ファイルを開いています: {0}").format(image_path_or_object)) 88 | img = Image.open(image_path_or_object) 89 | else: 90 | # print(translate("[DEBUG] 与えられた画像オブジェクトを使用します")) 91 | img = image_path_or_object 92 | 93 | # print(f"[DEBUG] PIL Image info: {img.info}") 94 | 95 | metadata = {} 96 | 97 | # 個別のキーを処理 98 | for key in [PROMPT_KEY, SEED_KEY, SECTION_PROMPT_KEY, SECTION_NUMBER_KEY, PARAMETERS_KEY]: 99 | if key in img.info: 100 | value = img.info[key] 101 | # print(translate("[DEBUG] メタデータ発見: {0}={1}").format(key, value)) 102 | try: 103 | # JSONとして解析を試みる 104 | metadata[key] = json.loads(value) 105 | # print(translate("[DEBUG] JSONとして解析: {0}={1}").format(key, metadata[key])) 106 | except (json.JSONDecodeError, TypeError): 107 | # JSONでなければそのまま格納 108 | metadata[key] = value 109 | # print(translate("[DEBUG] 文字列として解析: {0}={1}").format(key, value)) 110 | 111 | # parametersキーからの抽出処理(SD系との互換性のため) 112 | # 個別キーが無い場合でもparametersから抽出を試みる 113 | if PARAMETERS_KEY in img.info: 114 | params = img.info[PARAMETERS_KEY] 115 | # print(translate("[DEBUG] parameters形式のメタデータを処理: {0}").format(params)) 116 | 117 | # 行に分割して処理 118 | lines = params.split("\n") 119 | 120 | # プロンプト行を収集 121 | prompt_lines = [] 122 | 123 | # 全ての行を処理 124 | for line in lines: 125 | line_stripped = line.strip() 126 | if line_stripped.startswith("Seed:"): 127 | seed_str = line_stripped.replace("Seed:", "").strip() 128 | if seed_str.isdigit(): 129 | metadata[SEED_KEY] = int(seed_str) 130 | elif line_stripped.startswith("Section Number:"): 131 | section_num_str = line_stripped.replace("Section Number:", "").strip() 132 | if section_num_str.isdigit(): 133 | metadata[SECTION_NUMBER_KEY] = int(section_num_str) 134 | elif line_stripped.startswith("Section Prompt:"): 135 | section_prompt = line_stripped.replace("Section Prompt:", "").strip() 136 | if section_prompt: 137 | metadata[SECTION_PROMPT_KEY] = section_prompt 138 | else: 139 | # Seed: や Section: で始まらない行はプロンプトの一部 140 | if line.strip(): # 空行は除外 141 | prompt_lines.append(line.rstrip()) 142 | 143 | # 複数行のプロンプトを結合 144 | if prompt_lines: 145 | metadata[PROMPT_KEY] = "\n".join(prompt_lines) 146 | # print(translate("[DEBUG] プロンプト結合: {0}").format(metadata[PROMPT_KEY])) 147 | 148 | # print(translate("[DEBUG] 最終抽出メタデータ: {0}").format(metadata)) 149 | return metadata 150 | 151 | except Exception as e: 152 | # print(translate("[ERROR] メタデータ抽出エラー: {0}").format(e)) 153 | # traceback.print_exc() 154 | return {} 155 | 156 | def extract_metadata_from_numpy_array(numpy_image): 157 | """NumPy配列からメタデータを抽出する(PILを介して) 158 | 159 | Args: 160 | numpy_image: NumPy配列の画像データ 161 | 162 | Returns: 163 | dict: 抽出したメタデータの辞書 164 | """ 165 | try: 166 | if numpy_image is None: 167 | # print(translate("[DEBUG] extract_metadata_from_numpy_array: 入力がNoneです")) 168 | return {} 169 | 170 | # print(translate("[DEBUG] numpy_imageのサイズと形状: {0}, データ型: {1}").format(numpy_image.shape, numpy_image.dtype)) 171 | 172 | # NumPy配列からPIL.Imageに変換 173 | img = Image.fromarray(numpy_image) 174 | # print(translate("[DEBUG] PIL Imageサイズ: {0}, モード: {1}").format(img.size, img.mode)) 175 | # print(f"[DEBUG] PIL Image info: {img.info}") 176 | 177 | # メタデータの抽出を試行 178 | metadata = extract_metadata_from_png(img) 179 | # print(translate("[DEBUG] 抽出されたメタデータ: {0}").format(metadata)) 180 | 181 | return metadata 182 | 183 | except Exception as e: 184 | # print(translate("[ERROR] NumPy配列からのメタデータ抽出エラー: {0}").format(e)) 185 | # traceback.print_exc() 186 | return {} 187 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/settings_manager.py: -------------------------------------------------------------------------------- 1 | """ 2 | 設定ファイル管理モジュール 3 | endframe_ichi.pyから外出しした設定ファイル関連処理を含む 4 | """ 5 | 6 | import os 7 | import json 8 | import subprocess 9 | from locales.i18n_extended import translate 10 | 11 | def get_settings_file_path(): 12 | """設定ファイルの絶対パスを取得する""" 13 | # eichi_utils直下からwebuiフォルダに移動 14 | webui_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 15 | settings_folder = os.path.join(webui_path, 'settings') 16 | return os.path.join(settings_folder, 'app_settings.json') 17 | 18 | def get_output_folder_path(folder_name=None): 19 | """出力フォルダの絶対パスを取得する""" 20 | # eichi_utils直下からwebuiフォルダに移動 21 | webui_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 22 | if not folder_name or not folder_name.strip(): 23 | folder_name = "outputs" 24 | return os.path.join(webui_path, folder_name) 25 | 26 | def initialize_settings(): 27 | """設定ファイルを初期化する(存在しない場合のみ)""" 28 | settings_file = get_settings_file_path() 29 | settings_dir = os.path.dirname(settings_file) 30 | 31 | if not os.path.exists(settings_file): 32 | # 初期デフォルト設定 33 | default_settings = {'output_folder': 'outputs'} 34 | try: 35 | os.makedirs(settings_dir, exist_ok=True) 36 | with open(settings_file, 'w', encoding='utf-8') as f: 37 | json.dump(default_settings, f, ensure_ascii=False, indent=2) 38 | return True 39 | except Exception as e: 40 | print(translate("設定ファイル初期化エラー: {0}").format(e)) 41 | return False 42 | return True 43 | 44 | def load_settings(): 45 | """設定を読み込む関数""" 46 | settings_file = get_settings_file_path() 47 | default_settings = {'output_folder': 'outputs'} 48 | 49 | if os.path.exists(settings_file): 50 | try: 51 | with open(settings_file, 'r', encoding='utf-8') as f: 52 | file_content = f.read() 53 | if not file_content.strip(): 54 | return default_settings 55 | settings = json.loads(file_content) 56 | 57 | # デフォルト値とマージ 58 | for key, value in default_settings.items(): 59 | if key not in settings: 60 | settings[key] = value 61 | return settings 62 | except Exception as e: 63 | print(translate("設定読み込みエラー: {0}").format(e)) 64 | 65 | return default_settings 66 | 67 | def save_settings(settings): 68 | """設定を保存する関数""" 69 | settings_file = get_settings_file_path() 70 | 71 | try: 72 | # 保存前にディレクトリが存在するか確認 73 | os.makedirs(os.path.dirname(settings_file), exist_ok=True) 74 | 75 | # JSON書き込み 76 | with open(settings_file, 'w', encoding='utf-8') as f: 77 | json.dump(settings, f, ensure_ascii=False, indent=2) 78 | return True 79 | except Exception as e: 80 | print(translate("設定保存エラー: {0}").format(e)) 81 | return False 82 | 83 | def open_output_folder(folder_path): 84 | """指定されたフォルダをOSに依存せず開く""" 85 | if not os.path.exists(folder_path): 86 | os.makedirs(folder_path, exist_ok=True) 87 | 88 | try: 89 | if os.name == 'nt': # Windows 90 | subprocess.Popen(['explorer', folder_path]) 91 | elif os.name == 'posix': # Linux/Mac 92 | try: 93 | subprocess.Popen(['xdg-open', folder_path]) 94 | except: 95 | subprocess.Popen(['open', folder_path]) 96 | print(translate("フォルダを開きました: {0}").format(folder_path)) 97 | return True 98 | except Exception as e: 99 | print(translate("フォルダを開く際にエラーが発生しました: {0}").format(e)) 100 | return False 101 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/tensor_combiner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | import torch 5 | import traceback 6 | import safetensors.torch as sf 7 | from datetime import datetime 8 | import gradio as gr 9 | 10 | # ルートパスをシステムパスに追加 11 | root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 12 | if root_path not in sys.path: 13 | sys.path.append(root_path) 14 | 15 | # ルートパスを追加した後でインポート 16 | from locales.i18n_extended import translate 17 | 18 | def combine_tensor_files(file1_path, file2_path, output_path=None): 19 | """2つのsafetensorsファイルを読み込み、結合して新しいファイルに保存する 20 | 21 | Args: 22 | file1_path (str): 1つ目のsafetensorsファイルパス 23 | file2_path (str): 2つ目のsafetensorsファイルパス 24 | output_path (str, optional): 出力ファイルパス。指定しない場合は自動生成 25 | 26 | Returns: 27 | tuple: (成功したかどうかのbool, 出力ファイルパス, 結果メッセージ) 28 | """ 29 | try: 30 | # ファイル1を読み込み 31 | print(translate("ファイル1を読み込み中: {0}").format(os.path.basename(file1_path))) 32 | tensor_dict1 = sf.load_file(file1_path) 33 | 34 | # ファイル2を読み込み 35 | print(translate("ファイル2を読み込み中: {0}").format(os.path.basename(file2_path))) 36 | tensor_dict2 = sf.load_file(file2_path) 37 | 38 | # テンソルを取得 39 | if "history_latents" in tensor_dict1 and "history_latents" in tensor_dict2: 40 | tensor1 = tensor_dict1["history_latents"] 41 | tensor2 = tensor_dict2["history_latents"] 42 | 43 | # テンソル情報の表示 44 | print(translate("テンソル1: shape={0}, dtype={1}, フレーム数={2}").format(tensor1.shape, tensor1.dtype, tensor1.shape[2])) 45 | print(translate("テンソル2: shape={0}, dtype={1}, フレーム数={2}").format(tensor2.shape, tensor2.dtype, tensor2.shape[2])) 46 | 47 | # サイズチェック 48 | if tensor1.shape[3] != tensor2.shape[3] or tensor1.shape[4] != tensor2.shape[4]: 49 | error_msg = translate("エラー: テンソルサイズが異なります: {0} vs {1}").format(tensor1.shape, tensor2.shape) 50 | print(error_msg) 51 | return False, None, error_msg 52 | 53 | # データ型とデバイスの調整 54 | if tensor1.dtype != tensor2.dtype: 55 | print(translate("データ型の変換: {0} → {1}").format(tensor2.dtype, tensor1.dtype)) 56 | tensor2 = tensor2.to(dtype=tensor1.dtype) 57 | 58 | # 両方CPUに移動 59 | tensor1 = tensor1.cpu() 60 | tensor2 = tensor2.cpu() 61 | 62 | # 結合(テンソル1の後にテンソル2を追加) 63 | combined_tensor = torch.cat([tensor1, tensor2], dim=2) 64 | 65 | # 結合されたテンソルの情報を表示 66 | tensor1_frames = tensor1.shape[2] 67 | tensor2_frames = tensor2.shape[2] 68 | combined_frames = combined_tensor.shape[2] 69 | print(translate("結合成功: 結合後のフレーム数={0} ({1}+{2}フレーム)").format(combined_frames, tensor1_frames, tensor2_frames)) 70 | 71 | # メタデータを更新 72 | height, width = tensor1.shape[3], tensor1.shape[4] 73 | metadata = torch.tensor([height, width, combined_frames], dtype=torch.int32) 74 | 75 | # 出力ファイルパスが指定されていない場合は自動生成 76 | if output_path is None: 77 | timestamp = datetime.now().strftime("%y%m%d_%H%M%S") 78 | output_dir = os.path.dirname(file1_path) 79 | output_path = os.path.join(output_dir, f"combined_{timestamp}.safetensors") 80 | 81 | # 結合したテンソルをファイルに保存 82 | tensor_dict = { 83 | "history_latents": combined_tensor, 84 | "metadata": metadata 85 | } 86 | 87 | # ファイル保存 88 | sf.save_file(tensor_dict, output_path) 89 | 90 | # テンソルデータの保存サイズの概算 91 | tensor_size_mb = (combined_tensor.element_size() * combined_tensor.nelement()) / (1024 * 1024) 92 | 93 | success_msg = translate("結合テンソルを保存しました: {0}\n").format(os.path.basename(output_path)) 94 | success_msg += translate("フレーム数: {0}フレーム ({1}+{2}フレーム)\n").format(combined_frames, tensor1_frames, tensor2_frames) 95 | success_msg += translate("サイズ: {0:.2f}MB, 形状: {1}").format(tensor_size_mb, combined_tensor.shape) 96 | print(success_msg) 97 | 98 | return True, output_path, success_msg 99 | else: 100 | error_msg = translate("エラー: テンソルファイルに必要なキー'history_latents'がありません") 101 | print(error_msg) 102 | return False, None, error_msg 103 | 104 | except Exception as e: 105 | error_msg = translate("テンソル結合中にエラーが発生: {0}").format(e) 106 | print(error_msg) 107 | traceback.print_exc() 108 | return False, None, error_msg 109 | 110 | def create_ui(): 111 | """Gradio UIを作成""" 112 | with gr.Blocks(title=translate("テンソル結合ツール")) as app: 113 | gr.Markdown(translate("## テンソルデータ結合ツール")) 114 | gr.Markdown(translate("safetensors形式のテンソルデータファイルを2つ選択して結合します。結合順序は「テンソル1 + テンソル2」です。")) 115 | 116 | with gr.Row(): 117 | with gr.Column(scale=1): 118 | tensor_file1 = gr.File(label=translate("テンソルファイル1 (.safetensors)"), file_types=[".safetensors"]) 119 | with gr.Column(scale=1): 120 | tensor_file2 = gr.File(label=translate("テンソルファイル2 (.safetensors)"), file_types=[".safetensors"]) 121 | 122 | with gr.Row(): 123 | output_file = gr.Textbox(label=translate("出力ファイル名 (空欄で自動生成)"), placeholder=translate("例: combined.safetensors")) 124 | 125 | with gr.Row(): 126 | combine_btn = gr.Button(translate("テンソルファイルを結合"), variant="primary") 127 | 128 | with gr.Row(): 129 | result_output = gr.Textbox(label=translate("結果"), lines=5) 130 | 131 | def combine_tensors(file1, file2, output_path): 132 | if file1 is None or file2 is None: 133 | return translate("エラー: 2つのテンソルファイルを選択してください") 134 | 135 | file1_path = file1.name 136 | file2_path = file2.name 137 | 138 | # 出力パスの決定 139 | if output_path and output_path.strip(): 140 | # 拡張子のチェックと追加 141 | if not output_path.lower().endswith('.safetensors'): 142 | output_path += '.safetensors' 143 | # ディレクトリパスの決定(入力ファイルと同じ場所) 144 | output_dir = os.path.dirname(file1_path) 145 | full_output_path = os.path.join(output_dir, output_path) 146 | else: 147 | # 自動生成の場合はNoneのまま(関数内で自動生成) 148 | full_output_path = None 149 | 150 | success, result_path, message = combine_tensor_files(file1_path, file2_path, full_output_path) 151 | if success: 152 | return message 153 | else: 154 | return translate("結合失敗: {0}").format(message) 155 | 156 | combine_btn.click( 157 | fn=combine_tensors, 158 | inputs=[tensor_file1, tensor_file2, output_file], 159 | outputs=[result_output] 160 | ) 161 | 162 | return app 163 | 164 | def main(): 165 | """コマンドライン引数を解析して実行""" 166 | parser = argparse.ArgumentParser(description=translate("2つのsafetensorsファイルを結合するツール")) 167 | parser.add_argument('--file1', type=str, help=translate("1つ目のsafetensorsファイルパス")) 168 | parser.add_argument('--file2', type=str, help=translate("2つ目のsafetensorsファイルパス")) 169 | parser.add_argument('--output', type=str, default=None, help=translate("出力ファイルパス (省略可能)")) 170 | parser.add_argument('--ui', action='store_true', help=translate("GradioのUIモードで起動")) 171 | 172 | args = parser.parse_args() 173 | 174 | if args.ui: 175 | # UIモードで起動 176 | app = create_ui() 177 | app.launch() 178 | elif args.file1 and args.file2: 179 | # コマンドラインモードで実行 180 | success, output_path, message = combine_tensor_files(args.file1, args.file2, args.output) 181 | if success: 182 | print(translate("結合成功:")) 183 | print(message) 184 | return 0 185 | else: 186 | print(translate("結合失敗:")) 187 | print(message) 188 | return 1 189 | else: 190 | parser.print_help() 191 | return 1 192 | 193 | if __name__ == "__main__": 194 | sys.exit(main()) -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/text_encoder_manager.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import traceback 3 | import gc 4 | from diffusers_helper.memory import DynamicSwapInstaller 5 | from locales.i18n_extended import translate 6 | 7 | class TextEncoderManager: 8 | """text_encoderとtext_encoder_2の状態管理を行うクラス 9 | 10 | このクラスは以下の責務を持ちます: 11 | - text_encoderとtext_encoder_2のライフサイクル管理 12 | 13 | 設定の変更はすぐには適用されず、次回のリロード時に適用されます。 14 | """ 15 | 16 | def __init__(self, device, high_vram_mode=False): 17 | self.text_encoder = None 18 | self.text_encoder_2 = None 19 | self.device = device 20 | 21 | # 現在適用されている設定 22 | self.current_state = { 23 | 'is_loaded': False, 24 | 'high_vram': high_vram_mode 25 | } 26 | 27 | # 次回のロード時に適用する設定 28 | self.next_state = self.current_state.copy() 29 | 30 | def set_next_settings(self, high_vram_mode=False): 31 | """次回のロード時に使用する設定をセット(即時のリロードは行わない) 32 | 33 | Args: 34 | high_vram_mode: High-VRAMモードの有効/無効 35 | """ 36 | self.next_state = { 37 | 'high_vram': high_vram_mode, 38 | 'is_loaded': self.current_state['is_loaded'] 39 | } 40 | print(translate("次回のtext_encoder設定を設定しました:")) 41 | print(f" - High-VRAM mode: {high_vram_mode}") 42 | 43 | def _needs_reload(self): 44 | """現在の状態と次回の設定を比較し、リロードが必要かどうかを判断""" 45 | if not self._is_loaded(): 46 | return True 47 | 48 | # High-VRAMモードの比較 49 | if self.current_state['high_vram'] != self.next_state['high_vram']: 50 | return True 51 | 52 | return False 53 | 54 | def _is_loaded(self): 55 | """text_encoderとtext_encoder_2が読み込まれているかどうかを確認""" 56 | return (self.text_encoder is not None and 57 | self.text_encoder_2 is not None and 58 | self.current_state['is_loaded']) 59 | 60 | def get_text_encoders(self): 61 | """現在のtext_encoderとtext_encoder_2インスタンスを取得""" 62 | return self.text_encoder, self.text_encoder_2 63 | 64 | def dispose_text_encoders(self): 65 | """text_encoderとtext_encoder_2のインスタンスを破棄し、メモリを完全に解放""" 66 | try: 67 | print(translate("\ntext_encoderとtext_encoder_2のメモリを解放します...")) 68 | 69 | # text_encoderの破棄 70 | if hasattr(self, 'text_encoder') and self.text_encoder is not None: 71 | try: 72 | self.text_encoder.cpu() 73 | del self.text_encoder 74 | self.text_encoder = None 75 | except Exception as e: 76 | print(translate("text_encoderの破棄中にエラー: {0}").format(e)) 77 | 78 | # text_encoder_2の破棄 79 | if hasattr(self, 'text_encoder_2') and self.text_encoder_2 is not None: 80 | try: 81 | self.text_encoder_2.cpu() 82 | del self.text_encoder_2 83 | self.text_encoder_2 = None 84 | except Exception as e: 85 | print(translate("text_encoder_2の破棄中にエラー: {0}").format(e)) 86 | 87 | # 明示的なメモリ解放 88 | if torch.cuda.is_available(): 89 | torch.cuda.empty_cache() 90 | gc.collect() 91 | 92 | # 状態を更新 93 | self.current_state['is_loaded'] = False 94 | self.next_state['is_loaded'] = False 95 | 96 | print(translate("text_encoderとtext_encoder_2のメモリ解放が完了しました")) 97 | return True 98 | 99 | except Exception as e: 100 | print(translate("text_encoderとtext_encoder_2のメモリ解放中にエラー: {0}").format(e)) 101 | traceback.print_exc() 102 | return False 103 | 104 | def ensure_text_encoder_state(self): 105 | """text_encoderとtext_encoder_2の状態を確認し、必要に応じてリロード""" 106 | if self._needs_reload(): 107 | print(translate("text_encoderとtext_encoder_2をリロードします")) 108 | return self._reload_text_encoders() 109 | print(translate("ロード済みのtext_encoderとtext_encoder_2を再度利用します")) 110 | return True 111 | 112 | def _reload_text_encoders(self): 113 | """next_stateの設定でtext_encoderとtext_encoder_2をリロード""" 114 | try: 115 | # 既存のモデルが存在する場合は先にメモリを解放 116 | if self._is_loaded(): 117 | self.dispose_text_encoders() 118 | 119 | print(translate("\ntext_encoderとtext_encoder_2をリロードします...")) 120 | print(translate("適用する設定:")) 121 | print(f" - High-VRAM mode: {self.next_state['high_vram']}") 122 | 123 | # 新しいtext_encoderとtext_encoder_2インスタンスを作成 124 | from transformers import LlamaModel, CLIPTextModel 125 | self.text_encoder = LlamaModel.from_pretrained( 126 | "hunyuanvideo-community/HunyuanVideo", 127 | subfolder='text_encoder', 128 | torch_dtype=torch.float16 129 | ).cpu() 130 | 131 | self.text_encoder_2 = CLIPTextModel.from_pretrained( 132 | "hunyuanvideo-community/HunyuanVideo", 133 | subfolder='text_encoder_2', 134 | torch_dtype=torch.float16 135 | ).cpu() 136 | 137 | self.text_encoder.eval() 138 | self.text_encoder_2.eval() 139 | 140 | self.text_encoder.to(dtype=torch.float16) 141 | self.text_encoder_2.to(dtype=torch.float16) 142 | 143 | self.text_encoder.requires_grad_(False) 144 | self.text_encoder_2.requires_grad_(False) 145 | 146 | # VRAMモードに応じた設定 147 | if not self.next_state['high_vram']: 148 | DynamicSwapInstaller.install_model(self.text_encoder, device=self.device) 149 | DynamicSwapInstaller.install_model(self.text_encoder_2, device=self.device) 150 | else: 151 | self.text_encoder.to(self.device) 152 | self.text_encoder_2.to(self.device) 153 | 154 | # 状態を更新 155 | self.next_state['is_loaded'] = True 156 | self.current_state = self.next_state.copy() 157 | 158 | print(translate("text_encoderとtext_encoder_2のリロードが完了しました")) 159 | return True 160 | 161 | except Exception as e: 162 | print(translate("text_encoderとtext_encoder_2リロードエラー: {0}").format(e)) 163 | traceback.print_exc() 164 | self.current_state['is_loaded'] = False 165 | return False 166 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/eichi_utils/ui_styles.py: -------------------------------------------------------------------------------- 1 | """ 2 | UI関連のスタイルを定義するモジュール 3 | """ 4 | from diffusers_helper.gradio.progress_bar import make_progress_bar_css 5 | 6 | from locales.i18n import translate 7 | 8 | def get_app_css(): 9 | """ 10 | アプリケーションのCSSスタイルを返す 11 | 12 | Returns: 13 | str: CSSスタイル定義 14 | """ 15 | return make_progress_bar_css() + """ 16 | .title-suffix { 17 | color: currentColor; 18 | opacity: 0.05; 19 | } 20 | 21 | /* 赤枠のキーフレーム - 偶数パターン用 */ 22 | .highlighted-keyframe-red { 23 | border: 4px solid #ff3860 !important; 24 | box-shadow: 0 0 10px rgba(255, 56, 96, 0.5) !important; 25 | background-color: rgba(255, 56, 96, 0.05) !important; 26 | position: relative; 27 | } 28 | 29 | /* 赤枠キーフレームに「偶数番号」のラベルを追加 */ 30 | .highlighted-keyframe-red::after { 31 | """ + 'content: "' + translate("偶数セクションのコピー元") + '"' + """; 32 | position: absolute; 33 | top: 5px; 34 | right: 5px; 35 | background: rgba(255, 56, 96, 0.8); 36 | color: white; 37 | padding: 2px 6px; 38 | font-size: 10px; 39 | border-radius: 4px; 40 | pointer-events: none; 41 | } 42 | 43 | /* 青枠のキーフレーム - 奇数パターン用 */ 44 | .highlighted-keyframe-blue { 45 | border: 4px solid #3273dc !important; 46 | box-shadow: 0 0 10px rgba(50, 115, 220, 0.5) !important; 47 | background-color: rgba(50, 115, 220, 0.05) !important; 48 | position: relative; 49 | } 50 | 51 | /* 青枠キーフレームに「奇数番号」のラベルを追加 */ 52 | .highlighted-keyframe-blue::after { 53 | """ + 'content: "' + translate("奇数セクションのコピー元") + '"' + """; 54 | position: absolute; 55 | top: 5px; 56 | right: 5px; 57 | background: rgba(50, 115, 220, 0.8); 58 | color: white; 59 | padding: 2px 6px; 60 | font-size: 10px; 61 | border-radius: 4px; 62 | pointer-events: none; 63 | } 64 | 65 | /* 引き続きサポート(古いクラス名)- 前方互換性用 */ 66 | .highlighted-keyframe { 67 | border: 4px solid #ff3860 !important; 68 | box-shadow: 0 0 10px rgba(255, 56, 96, 0.5) !important; 69 | background-color: rgba(255, 56, 96, 0.05) !important; 70 | } 71 | 72 | /* 赤枠用セクション番号ラベル */ 73 | .highlighted-label-red label { 74 | color: #ff3860 !important; 75 | font-weight: bold !important; 76 | } 77 | 78 | /* 青枠用セクション番号ラベル */ 79 | .highlighted-label-blue label { 80 | color: #3273dc !important; 81 | font-weight: bold !important; 82 | } 83 | 84 | /* 引き続きサポート(古いクラス名)- 前方互換性用 */ 85 | .highlighted-label label { 86 | color: #ff3860 !important; 87 | font-weight: bold !important; 88 | } 89 | 90 | /* オールパディングの高さ調整 */ 91 | #all_padding_checkbox { 92 | padding-top: 1.5rem; 93 | min-height: 5.8rem; 94 | } 95 | 96 | #all_padding_checkbox .wrap { 97 | align-items: flex-start; 98 | } 99 | 100 | #all_padding_checkbox .label-wrap { 101 | margin-bottom: 0.8rem; 102 | font-weight: 500; 103 | font-size: 14px; 104 | } 105 | 106 | #all_padding_checkbox .info { 107 | margin-top: 0.2rem; 108 | } 109 | 110 | /* セクション間の区切り線を太くする */ 111 | .section-row { 112 | border-bottom: 4px solid #3273dc; 113 | margin-bottom: 20px; 114 | padding-bottom: 15px; 115 | margin-top: 10px; 116 | position: relative; 117 | } 118 | 119 | /* セクション番号を目立たせる */ 120 | .section-row .gr-form:first-child label { 121 | font-weight: bold; 122 | font-size: 1.1em; 123 | color: #3273dc; 124 | background-color: rgba(50, 115, 220, 0.1); 125 | padding: 5px 10px; 126 | border-radius: 4px; 127 | margin-bottom: 10px; 128 | display: inline-block; 129 | } 130 | 131 | /* セクションの背景を少し強調 */ 132 | .section-row { 133 | background-color: rgba(50, 115, 220, 0.03); 134 | border-radius: 8px; 135 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 136 | } 137 | 138 | /* セクション間の余白を増やす */ 139 | .section-container > .gr-block:not(:first-child) { 140 | margin-top: 10px; 141 | } 142 | 143 | /* アコーディオンセクションのスタイル */ 144 | .section-accordion { 145 | margin-top: 15px; 146 | margin-bottom: 15px; 147 | border-left: 4px solid #3273dc; 148 | padding-left: 10px; 149 | } 150 | 151 | .section-accordion h3 button { 152 | font-weight: bold; 153 | color: #3273dc; 154 | } 155 | 156 | .section-accordion .gr-block { 157 | border-radius: 8px; 158 | } 159 | """ -------------------------------------------------------------------------------- /version/v1.9.3/webui/locales/i18n.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os.path 3 | 4 | # デフォルト言語設定 5 | # 注意: init関数を呼び出すまでは翻訳機能は使用できません 6 | lang = "ja" # 明示的にデフォルト言語を日本語(ja)に設定 7 | translateContext = None 8 | 9 | class I18nString(str): 10 | def __new__(cls, value): 11 | result = translateContext.get(lang, {}).get(value, value) 12 | return result 13 | 14 | def __init__(self, value): 15 | if isinstance(value, I18nString): 16 | self.add_values = value.add_values 17 | self.radd_values = value.radd_values 18 | else: 19 | self.add_values = [] 20 | self.radd_values = [] 21 | 22 | def __str__(self): 23 | result = translateContext.get(lang, {}).get(self, super().__str__()) 24 | 25 | for v in self.radd_values: 26 | result = str(v) + result 27 | 28 | for v in self.add_values: 29 | result = result + str(v) 30 | 31 | # hotfix, remove unexpected single quotes 32 | while len(result) >= 2 and result.startswith("'") and result.endswith("'"): 33 | result = result[1:-1] 34 | 35 | return result 36 | 37 | def __add__(self, other): 38 | v = str(self) 39 | if isinstance(v, I18nString): 40 | self.add_values.append(other) 41 | return self 42 | return v.__add__(other) 43 | 44 | def __radd__(self, other): 45 | v = str(self) 46 | if isinstance(v, I18nString): 47 | self.radd_values.append(other) 48 | return self 49 | return other.__add__(v) 50 | 51 | def __hash__(self) -> int: 52 | return super().__hash__() 53 | 54 | def format(self, *args, **kwargs) -> str: 55 | v = str(self) 56 | if isinstance(v, I18nString): 57 | return super().format(*args, **kwargs) 58 | return v.format(*args, **kwargs) 59 | 60 | def unwrap(self): 61 | return super().__str__() 62 | 63 | @staticmethod 64 | def unwrap_strings(obj): 65 | """Unwrap all keys in I18nStrings in the object""" 66 | if isinstance(obj, I18nString): 67 | yield obj.unwrap() 68 | for v in obj.add_values: 69 | yield from I18nString.unwrap_strings(v) 70 | for v in obj.radd_values: 71 | yield from I18nString.unwrap_strings(v) 72 | return 73 | yield obj 74 | 75 | def translate(key: str): 76 | """指定されたキーに対応する翻訳文字列を返します。 77 | 78 | Args: 79 | key: 翻訳したい文字列のキー 80 | 81 | Returns: 82 | I18nString: 現在の言語設定に基づいた翻訳文字列 83 | """ 84 | # デバッグ用:translateContextがロードされていない場合に自動的にロード 85 | global translateContext 86 | if translateContext is None: 87 | # 自動的にinitializeを呼び出す 88 | init(lang) 89 | 90 | return I18nString(key) 91 | 92 | def load_translations(): 93 | translations = {} 94 | locales_dir = os.path.join(os.path.dirname(__file__), './') 95 | 96 | for locale in ["en", "ja", "zh-tw", "ru"]: 97 | json_file = os.path.join(locales_dir, f"{locale}.json") 98 | if os.path.exists(json_file): 99 | with open(json_file, 'r', encoding='utf-8') as f: 100 | translations[locale] = json.load(f) 101 | else: 102 | print(f"Warning: Translation file {json_file} not found") 103 | translations[locale] = {} 104 | 105 | return translations 106 | 107 | def init(locale="ja"): 108 | """言語を初期化します。 109 | 110 | Args: 111 | locale: 使用する言語コード(例: 'ja', 'en', 'zh-tw')。 112 | 未対応の言語の場合は自動的に'ja'が使用されます。 113 | """ 114 | global lang 115 | global translateContext 116 | 117 | # 対応言語のリスト 118 | supported_locales = ["ja", "en", "zh-tw", "ru"] 119 | 120 | # 対応していない言語の場合はデフォルト言語(ja)を使用 121 | if locale not in supported_locales: 122 | print(f"[WARNING] Unsupported language: {locale}. Falling back to 'ja'") 123 | locale = "ja" 124 | 125 | print(f"[DEBUG] Initializing language to: {locale}") 126 | lang = locale 127 | translateContext = load_translations() 128 | print(f"[DEBUG] Loaded translations for: {', '.join(translateContext.keys())}") 129 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/locales/i18n_extended.py: -------------------------------------------------------------------------------- 1 | def set_lang(language): 2 | """言語設定を行う""" 3 | from locales import i18n 4 | print(f"[DEBUG] Setting language from i18n_extended to: {language}") 5 | i18n.lang = language 6 | i18n.init(language) # 言語設定を反映(明示的に言語を渡す) 7 | print(f"[DEBUG] Language initialized successfully: {language}") 8 | 9 | # i18nモジュールからtranslate関数をインポート 10 | from locales.i18n import translate 11 | 12 | """ 13 | FramePack-eichi 拡張i18nモジュール 14 | 各言語間の変換と内部キーの管理を行います 15 | """ 16 | 17 | import json 18 | import os.path 19 | from locales import i18n 20 | 21 | # 逆マッピング用辞書 22 | _reverse_mapping = { 23 | # 英語→内部キー 24 | "0.5 seconds (17 frames)": "_KEY_FRAME_SIZE_05SEC", 25 | "1 second (33 frames)": "_KEY_FRAME_SIZE_1SEC", 26 | "Normal": "_KEY_MODE_NORMAL", 27 | "Normal mode": "_KEY_MODE_NORMAL_FULL", 28 | "Loop": "_KEY_MODE_LOOP", 29 | "Loop mode": "_KEY_MODE_LOOP_FULL", 30 | "1 second": "_KEY_VIDEO_LENGTH_1SEC", 31 | "2s": "_KEY_VIDEO_LENGTH_2SEC", 32 | "3s": "_KEY_VIDEO_LENGTH_3SEC", 33 | "4s": "_KEY_VIDEO_LENGTH_4SEC", 34 | "6s": "_KEY_VIDEO_LENGTH_6SEC", 35 | "8s": "_KEY_VIDEO_LENGTH_8SEC", 36 | "10s": "_KEY_VIDEO_LENGTH_10SEC", 37 | "12s": "_KEY_VIDEO_LENGTH_12SEC", 38 | "16s": "_KEY_VIDEO_LENGTH_16SEC", 39 | "20s": "_KEY_VIDEO_LENGTH_20SEC", 40 | 41 | # 中国語→内部キー 42 | "0.5秒 (17幀)": "_KEY_FRAME_SIZE_05SEC", 43 | "1秒 (33幀)": "_KEY_FRAME_SIZE_1SEC", 44 | "常規": "_KEY_MODE_NORMAL", 45 | "常規模式": "_KEY_MODE_NORMAL_FULL", 46 | "循環": "_KEY_MODE_LOOP", 47 | "循環模式": "_KEY_MODE_LOOP_FULL", 48 | "1秒": "_KEY_VIDEO_LENGTH_1SEC", 49 | "2秒": "_KEY_VIDEO_LENGTH_2SEC", 50 | "3秒": "_KEY_VIDEO_LENGTH_3SEC", 51 | "4秒": "_KEY_VIDEO_LENGTH_4SEC", 52 | "6秒": "_KEY_VIDEO_LENGTH_6SEC", 53 | "8秒": "_KEY_VIDEO_LENGTH_8SEC", 54 | "10秒": "_KEY_VIDEO_LENGTH_10SEC", 55 | "12秒": "_KEY_VIDEO_LENGTH_12SEC", 56 | "16秒": "_KEY_VIDEO_LENGTH_16SEC", 57 | "20秒": "_KEY_VIDEO_LENGTH_20SEC", 58 | 59 | # 日本語→内部キー 60 | "0.5秒 (17フレーム)": "_KEY_FRAME_SIZE_05SEC", 61 | "1秒 (33フレーム)": "_KEY_FRAME_SIZE_1SEC", 62 | "通常": "_KEY_MODE_NORMAL", 63 | "通常モード": "_KEY_MODE_NORMAL_FULL", 64 | "ループ": "_KEY_MODE_LOOP", 65 | "ループモード": "_KEY_MODE_LOOP_FULL", 66 | "1秒": "_KEY_VIDEO_LENGTH_1SEC", 67 | "2秒": "_KEY_VIDEO_LENGTH_2SEC", 68 | "3秒": "_KEY_VIDEO_LENGTH_3SEC", 69 | "4秒": "_KEY_VIDEO_LENGTH_4SEC", 70 | "6秒": "_KEY_VIDEO_LENGTH_6SEC", 71 | "8秒": "_KEY_VIDEO_LENGTH_8SEC", 72 | "10秒": "_KEY_VIDEO_LENGTH_10SEC", 73 | "12秒": "_KEY_VIDEO_LENGTH_12SEC", 74 | "16秒": "_KEY_VIDEO_LENGTH_16SEC", 75 | "20秒": "_KEY_VIDEO_LENGTH_20SEC", 76 | 77 | # ロシア語→内部キー 78 | "0.5 секунды (17 кадров)": "_KEY_FRAME_SIZE_05SEC", 79 | "1 секунда (33 кадра)": "_KEY_FRAME_SIZE_1SEC", 80 | "Нормальный": "_KEY_MODE_NORMAL", 81 | "Нормальный режим": "_KEY_MODE_NORMAL_FULL", 82 | "Цикл": "_KEY_MODE_LOOP", 83 | "Циклический режим": "_KEY_MODE_LOOP_FULL", 84 | "1 секунда": "_KEY_VIDEO_LENGTH_1SEC", 85 | "2 сек": "_KEY_VIDEO_LENGTH_2SEC", 86 | "3 сек": "_KEY_VIDEO_LENGTH_3SEC", 87 | "4 сек": "_KEY_VIDEO_LENGTH_4SEC", 88 | "6 сек": "_KEY_VIDEO_LENGTH_6SEC", 89 | "8 сек": "_KEY_VIDEO_LENGTH_8SEC", 90 | "10 сек": "_KEY_VIDEO_LENGTH_10SEC", 91 | "12 сек": "_KEY_VIDEO_LENGTH_12SEC", 92 | "16 сек": "_KEY_VIDEO_LENGTH_16SEC", 93 | "20 сек": "_KEY_VIDEO_LENGTH_20SEC", 94 | } 95 | 96 | # 内部キーから各言語への変換マップ 97 | _internal_to_lang = { 98 | "ja": {}, 99 | "en": {}, 100 | "zh-tw": {}, 101 | "ru": {} 102 | } 103 | 104 | def init(): 105 | """逆マッピングを初期化""" 106 | global _reverse_mapping 107 | global _internal_to_lang 108 | 109 | # 各言語ファイルを読み込み 110 | locales_dir = os.path.join(os.path.dirname(__file__), './') 111 | for locale in ["en", "ja", "zh-tw", "ru"]: 112 | json_file = os.path.join(locales_dir, f"{locale}.json") 113 | if os.path.exists(json_file): 114 | with open(json_file, 'r', encoding='utf-8') as f: 115 | translations = json.load(f) 116 | 117 | # 内部キー(_KEY_で始まるもの)の逆マッピングを構築 118 | for key, value in translations.items(): 119 | if key.startswith("_KEY_"): 120 | # 逆マッピング: 翻訳文字列→内部キー 121 | _reverse_mapping[value] = key 122 | # 正マッピング: 内部キー→翻訳文字列 123 | _internal_to_lang[locale][key] = value 124 | 125 | def get_internal_key(translated_text): 126 | """翻訳された文字列から内部キーを取得""" 127 | return _reverse_mapping.get(translated_text, translated_text) 128 | 129 | def get_original_japanese(translated_text): 130 | """翻訳された文字列から元の日本語を取得""" 131 | internal_key = get_internal_key(translated_text) 132 | # 内部キーが見つからない場合は元の文字列を返す 133 | if internal_key == translated_text: 134 | return translated_text 135 | 136 | # 内部キーから日本語訳を取得 137 | return _internal_to_lang.get("ja", {}).get(internal_key, translated_text) 138 | 139 | def convert_between_languages(text, from_lang, to_lang): 140 | """ある言語の文字列を別の言語に変換""" 141 | # 内部キーを取得 142 | internal_key = get_internal_key(text) 143 | 144 | # 内部キーが見つからない場合は元の文字列を返す 145 | if internal_key == text: 146 | return text 147 | 148 | # 目的言語の翻訳を取得 149 | return _internal_to_lang.get(to_lang, {}).get(internal_key, text) 150 | 151 | # 初期化を実行 152 | init() 153 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/lora_utils/__init__.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi LoRA Utilities 2 | # 3 | # LoRAの適用、FP8最適化、LoRAフォーマット検出と変換のための機能を提供します。 4 | 5 | from .lora_utils import ( 6 | merge_lora_to_state_dict, 7 | load_safetensors_with_lora_and_fp8, 8 | load_safetensors_with_fp8_optimization, 9 | convert_hunyuan_to_framepack, 10 | convert_from_diffusion_pipe_or_something 11 | ) 12 | 13 | from .fp8_optimization_utils import ( 14 | calculate_fp8_maxval, 15 | quantize_tensor_to_fp8, 16 | optimize_state_dict_with_fp8_on_the_fly, 17 | fp8_linear_forward_patch, 18 | apply_fp8_monkey_patch, 19 | check_fp8_support 20 | ) 21 | 22 | from .lora_loader import ( 23 | load_and_apply_lora 24 | ) 25 | 26 | from .safetensors_utils import ( 27 | MemoryEfficientSafeOpen 28 | ) 29 | 30 | # 国際化対応ヘルパー 31 | try: 32 | from locales import i18n 33 | HAS_I18N = True 34 | except ImportError: 35 | HAS_I18N = False 36 | print("Warning: i18n module not found, using fallback translations") 37 | 38 | # 翻訳ヘルパー関数 39 | def _(text): 40 | """国際化対応のためのヘルパー関数""" 41 | if HAS_I18N: 42 | return i18n.translate(text) 43 | return text 44 | 45 | # バージョン情報 46 | __version__ = "1.0.0" 47 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/lora_utils/dynamic_swap_lora.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi Dynamic Swap LoRA 2 | # 3 | # このモジュールは後方互換性のために残されていますが、 4 | # 実際にはdirect_applicationによるLoRA適用が使用されています。 5 | 6 | import os 7 | import torch 8 | import warnings 9 | 10 | # 国際化対応 11 | from locales.i18n_extended import translate as _ 12 | 13 | 14 | class DynamicSwapLoRAManager: 15 | """ 16 | この旧式のLoRA管理クラスは後方互換性のために残されていますが、 17 | 実際の処理では使用されません。代わりに直接的なLoRA適用が行われます。 18 | """ 19 | 20 | def __init__(self): 21 | """初期化""" 22 | self.is_active = False 23 | self.lora_path = None 24 | self.lora_scale = 0.8 25 | warnings.warn( 26 | _("DynamicSwapLoRAManagerは非推奨です。代わりにlora_loader.load_and_apply_lora()を使用してください。"), 27 | DeprecationWarning, 28 | stacklevel=2 29 | ) 30 | 31 | def load_lora(self, lora_path, is_diffusers=False): 32 | """ 33 | LoRAファイルをロードする (実際には、パスの記録のみ) 34 | 35 | Args: 36 | lora_path: LoRAファイルのパス 37 | is_diffusers: 互換性のために残されたパラメータ(使用されない) 38 | """ 39 | if not os.path.exists(lora_path): 40 | raise FileNotFoundError(_("LoRAファイルが見つかりません: {0}").format(lora_path)) 41 | 42 | self.lora_path = lora_path 43 | self.is_active = True 44 | 45 | print(_("LoRAファイルがロードされました (非推奨インターフェース): {0}").format(lora_path)) 46 | print(_("注意: ") + _("DynamicSwapLoRAManagerは非推奨です。代わりにlora_loader.load_and_apply_lora()を使用してください。")) 47 | 48 | def set_scale(self, scale): 49 | """ 50 | LoRA適用スケールを設定する 51 | 52 | Args: 53 | scale: LoRAの適用強度 54 | """ 55 | self.lora_scale = scale 56 | 57 | def install_hooks(self, model): 58 | """ 59 | モデルにLoRAフックをインストールする (実際には、直接適用を行う) 60 | 61 | Args: 62 | model: フックをインストールするモデル 63 | """ 64 | # 直接適用モードを使用してLoRAを適用 65 | from .lora_loader import load_and_apply_lora 66 | 67 | print(_("警告: DynamicSwapLoRAManagerは非推奨です。直接適用モードにリダイレクトします。")) 68 | 69 | load_and_apply_lora( 70 | model, 71 | self.lora_path, 72 | self.lora_scale, 73 | device=torch.device("cuda" if torch.cuda.is_available() else "cpu") 74 | ) 75 | 76 | print(_("LoRAは直接適用モードで適用されました。")) 77 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/lora_utils/lora_check_helper.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi LoRA Check Helper 2 | # 3 | # LoRAの適用状態確認のための機能を提供します。 4 | 5 | import torch 6 | 7 | # 国際化対応 8 | from locales.i18n_extended import translate as _ 9 | 10 | def check_lora_applied(model): 11 | """ 12 | モデルにLoRAが適用されているかをチェック 13 | 14 | Args: 15 | model: チェック対象のモデル 16 | 17 | Returns: 18 | (bool, str): LoRAが適用されているかどうかとその適用方法 19 | """ 20 | # _lora_appliedフラグのチェック 21 | has_flag = hasattr(model, '_lora_applied') and model._lora_applied 22 | 23 | if has_flag: 24 | return True, "direct_application" 25 | 26 | # モデル内の名前付きモジュールをチェックして、LoRAフックがあるかを確認 27 | has_hooks = False 28 | for name, module in model.named_modules(): 29 | if hasattr(module, '_lora_hooks'): 30 | has_hooks = True 31 | break 32 | 33 | if has_hooks: 34 | return True, "hooks" 35 | 36 | return False, "none" 37 | 38 | def analyze_lora_application(model): 39 | """ 40 | モデルのLoRA適用率と影響を詳細に分析 41 | 42 | Args: 43 | model: 分析対象のモデル 44 | 45 | Returns: 46 | dict: 分析結果の辞書 47 | """ 48 | total_params = 0 49 | lora_affected_params = 0 50 | 51 | # トータルパラメータ数とLoRAの影響を受けるパラメータ数をカウント 52 | for name, module in model.named_modules(): 53 | if hasattr(module, 'weight') and isinstance(module.weight, torch.Tensor): 54 | param_count = module.weight.numel() 55 | total_params += param_count 56 | 57 | # LoRA適用されたモジュールかチェック 58 | if hasattr(module, '_lora_hooks') or hasattr(module, '_lora_applied'): 59 | lora_affected_params += param_count 60 | 61 | # 適用率の計算 62 | application_rate = 0.0 63 | if total_params > 0: 64 | application_rate = lora_affected_params / total_params * 100.0 65 | 66 | return { 67 | "total_params": total_params, 68 | "lora_affected_params": lora_affected_params, 69 | "application_rate": application_rate, 70 | "has_lora": lora_affected_params > 0 71 | } 72 | 73 | def print_lora_status(model): 74 | """ 75 | モデルのLoRA適用状況を出力 76 | 77 | Args: 78 | model: 出力対象のモデル 79 | """ 80 | has_lora, source = check_lora_applied(model) 81 | 82 | if has_lora: 83 | print(_("LoRAステータス: {0}").format(_("適用済み"))) 84 | print(_("適用方法: {0}").format(_(source))) 85 | 86 | # 詳細な分析 87 | analysis = analyze_lora_application(model) 88 | application_rate = analysis["application_rate"] 89 | 90 | print(_("LoRA適用状況: {0}/{1} パラメータ ({2:.2f}%)").format( 91 | analysis["lora_affected_params"], 92 | analysis["total_params"], 93 | application_rate 94 | )) 95 | else: 96 | print(_("LoRAステータス: {0}").format(_("未適用"))) 97 | print(_("モデルにLoRAは適用されていません")) 98 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/lora_utils/lora_loader.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi LoRA Loader 2 | # 3 | # LoRAモデルの読み込みと適用のための機能を提供します。 4 | 5 | import os 6 | import torch 7 | from tqdm import tqdm 8 | from .lora_utils import merge_lora_to_state_dict 9 | 10 | # 国際化対応 11 | from locales.i18n_extended import translate as _ 12 | 13 | def load_and_apply_lora( 14 | model_files, 15 | lora_paths, 16 | lora_scales=None, 17 | fp8_enabled=False, 18 | device=None 19 | ): 20 | """ 21 | LoRA重みをロードして重みに適用する 22 | 23 | Args: 24 | model_files: モデルファイルのリスト 25 | lora_paths: LoRAファイルのパスのリスト 26 | lora_scales: LoRAの適用強度のリスト 27 | fp8_enabled: FP8最適化の有効/無効 28 | device: 計算に使用するデバイス 29 | 30 | Returns: 31 | LoRAが適用されたモデルの状態辞書 32 | """ 33 | if lora_paths is None: 34 | lora_paths = [] 35 | for lora_path in lora_paths: 36 | if not os.path.exists(lora_path): 37 | raise FileNotFoundError(_("LoRAファイルが見つかりません: {0}").format(lora_path)) 38 | 39 | if lora_scales is None: 40 | lora_scales = [0.8] * len(lora_paths) 41 | if len(lora_scales)> len(lora_paths): 42 | lora_scales = lora_scales[:len(lora_paths)] 43 | if len(lora_scales) < len(lora_paths): 44 | lora_scales += [0.8] * (len(lora_paths) - len(lora_scales)) 45 | 46 | if device is None: 47 | device = torch.device("cpu") # CPUに fall back 48 | 49 | for lora_path, lora_scale in zip(lora_paths, lora_scales): 50 | print(_("LoRAを読み込み中: {0} (スケール: {1})").format(os.path.basename(lora_path), lora_scale)) 51 | 52 | # LoRA重みを状態辞書にマージ 53 | print(_("フォーマット: HunyuanVideo")) 54 | 55 | # LoRAをマージ 56 | merged_state_dict = merge_lora_to_state_dict(model_files, lora_paths, lora_scales, fp8_enabled, device) 57 | 58 | # # LoRAが適用されたことを示すフラグを設定 59 | # model._lora_applied = True 60 | 61 | print(_("LoRAの適用が完了しました")) 62 | return merged_state_dict 63 | 64 | def check_lora_applied(model): 65 | """ 66 | モデルにLoRAが適用されているかをチェック 67 | 68 | Args: 69 | model: チェック対象のモデル 70 | 71 | Returns: 72 | (bool, str): LoRAが適用されているかどうかとその適用方法 73 | """ 74 | # _lora_appliedフラグのチェック 75 | has_flag = hasattr(model, '_lora_applied') and model._lora_applied 76 | 77 | if has_flag: 78 | return True, "direct_application" 79 | 80 | return False, "none" 81 | -------------------------------------------------------------------------------- /version/v1.9.3/webui/lora_utils/safetensors_utils.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | import json 3 | import struct 4 | import torch 5 | 6 | class MemoryEfficientSafeOpen: 7 | """ 8 | A class to read tensors from a .safetensors file in a memory-efficient way. 9 | """ 10 | # does not support metadata loading 11 | def __init__(self, filename): 12 | self.filename = filename 13 | self.file = open(filename, "rb") 14 | self.header, self.header_size = self._read_header() 15 | 16 | def __enter__(self): 17 | return self 18 | 19 | def __exit__(self, exc_type, exc_val, exc_tb): 20 | self.file.close() 21 | 22 | def keys(self): 23 | return [k for k in self.header.keys() if k != "__metadata__"] 24 | 25 | def metadata(self) -> Dict[str, str]: 26 | return self.header.get("__metadata__", {}) 27 | 28 | def get_tensor(self, key): 29 | if key not in self.header: 30 | raise KeyError(f"Tensor '{key}' not found in the file") 31 | 32 | metadata = self.header[key] 33 | offset_start, offset_end = metadata["data_offsets"] 34 | 35 | if offset_start == offset_end: 36 | tensor_bytes = None 37 | else: 38 | # adjust offset by header size 39 | self.file.seek(self.header_size + 8 + offset_start) 40 | tensor_bytes = self.file.read(offset_end - offset_start) 41 | 42 | return self._deserialize_tensor(tensor_bytes, metadata) 43 | 44 | def _read_header(self): 45 | header_size = struct.unpack("= preserved_memory_gb: 105 | torch.cuda.empty_cache() 106 | return 107 | 108 | if hasattr(m, 'weight'): 109 | m.to(device=cpu) 110 | 111 | model.to(device=cpu) 112 | torch.cuda.empty_cache() 113 | return 114 | 115 | 116 | def unload_complete_models(*args): 117 | for m in gpu_complete_modules + list(args): 118 | if m is None: 119 | continue 120 | m.to(device=cpu) 121 | print(f'Unloaded {m.__class__.__name__} as complete.') 122 | 123 | gpu_complete_modules.clear() 124 | torch.cuda.empty_cache() 125 | return 126 | 127 | 128 | def load_model_as_complete(model, target_device, unload=True): 129 | if unload: 130 | unload_complete_models() 131 | 132 | model.to(device=target_device) 133 | print(f'Loaded {model.__class__.__name__} to {target_device} as complete.') 134 | 135 | gpu_complete_modules.append(model) 136 | return 137 | -------------------------------------------------------------------------------- /webui/eichi_utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | eichi_utils - FramePack-eichiのユーティリティモジュール 3 | プリセット管理、設定管理、キーフレーム処理、動画モード設定などを含む 4 | """ 5 | 6 | __version__ = "1.0.0" 7 | 8 | # 外部モジュールからアクセスできるようにエクスポート 9 | from .vae_settings import ( 10 | load_vae_settings, 11 | save_vae_settings, 12 | apply_vae_settings, 13 | create_vae_settings_ui, 14 | get_current_vae_settings_display 15 | ) 16 | -------------------------------------------------------------------------------- /webui/eichi_utils/combine_mode.py: -------------------------------------------------------------------------------- 1 | from locales.i18n_extended import translate 2 | from enum import Enum 3 | 4 | 5 | # 結合箇所 6 | class COMBINE_MODE(Enum): 7 | FIRST = 0 8 | LAST = 1 9 | 10 | 11 | # 検証用なので後方のみ 12 | COMBINE_MODE_OPTIONS = { 13 | # 先頭側への結合評価はあまり必要ないと思われるのでコメント 14 | # translate("テンソルデータの前方(先頭)"): COMBINE_MODE.FIRST, 15 | translate("テンソルデータの後方(末尾)"): COMBINE_MODE.LAST, 16 | } 17 | COMBINE_MODE_OPTIONS_KEYS = list(COMBINE_MODE_OPTIONS.keys()) 18 | COMBINE_MODE_DEFAULT = COMBINE_MODE_OPTIONS_KEYS[0] 19 | 20 | 21 | def is_combine_mode(combine_mode, check_mode): 22 | if COMBINE_MODE_OPTIONS[combine_mode] == check_mode: 23 | return True 24 | else: 25 | return False 26 | 27 | 28 | def get_combine_mode(combine_mode): 29 | """COMBINE_MODEのEnumからCOMBINE_MODE_OPTIONSのキーの値を取得する 30 | 31 | Args: 32 | combine_mode (COMBINE_MODE): 結合モードのEnum値 33 | 34 | Returns: 35 | str: COMBINE_MODE_OPTIONSのキー。見つからない場合はデフォルト値 36 | """ 37 | for key, value in COMBINE_MODE_OPTIONS.items(): 38 | if value == combine_mode: 39 | return key 40 | return COMBINE_MODE_DEFAULT # 見つからない場合はデフォルト値を返す 41 | -------------------------------------------------------------------------------- /webui/eichi_utils/frame_calculator.py: -------------------------------------------------------------------------------- 1 | """ 2 | フレームサイズに基づくセクション数計算モジュール 3 | 0.5秒/1秒モードに対応したセクション数の計算機能を提供します 4 | """ 5 | 6 | import math 7 | from eichi_utils.video_mode_settings import VIDEO_MODE_SETTINGS 8 | 9 | from locales import i18n_extended 10 | 11 | def calculate_frames_per_section(latent_window_size=9): 12 | """1セクションあたりのフレーム数を計算""" 13 | return latent_window_size * 4 - 3 14 | 15 | 16 | def calculate_sections_from_frames(total_frames, latent_window_size=9): 17 | """フレーム数から必要なセクション数を計算""" 18 | frames_per_section = calculate_frames_per_section(latent_window_size) 19 | return math.ceil(total_frames / frames_per_section) 20 | 21 | 22 | def calculate_total_frame_count(sections, latent_window_size=9): 23 | """セクション数から総フレーム数を計算""" 24 | frames_per_section = calculate_frames_per_section(latent_window_size) 25 | return sections * frames_per_section 26 | 27 | 28 | def calculate_total_second_length(frames, fps=30): 29 | """フレーム数から秒数を計算""" 30 | return frames / fps 31 | 32 | 33 | def calculate_sections_for_mode_and_size(mode_key, frame_size_setting=None): 34 | """動画モードとフレームサイズ設定から必要なセクション数を計算""" 35 | # 動画モードからフレーム数を取得 36 | if mode_key not in VIDEO_MODE_SETTINGS: 37 | return 15 # デフォルト値 38 | 39 | total_frames = VIDEO_MODE_SETTINGS[mode_key]["frames"] 40 | 41 | # フレームサイズ設定からlatent_window_sizeを判定 42 | frame_size_internal_key = i18n_extended.get_internal_key(frame_size_setting) 43 | if frame_size_internal_key == "_KEY_FRAME_SIZE_05SEC": 44 | latent_window_size = 4.5 # 0.5秒モード 45 | else: 46 | latent_window_size = 9 # 1秒モードがデフォルト 47 | 48 | # 必要なセクション数を計算 49 | required_sections = calculate_sections_from_frames(total_frames, latent_window_size) 50 | 51 | # デバッグ情報 52 | frames_per_section = calculate_frames_per_section(latent_window_size) 53 | print(i18n_extended.translate("計算詳細: モード={mode_key}, フレームサイズ={frame_size_setting}, 総フレーム数={total_frames}, セクションあたり={frames_per_section}フレーム, 必要セクション数={required_sections}").format(mode_key=mode_key, frame_size_setting=frame_size_setting, total_frames=total_frames, frames_per_section=frames_per_section, required_sections=required_sections)) 54 | 55 | # 結果を返す 56 | return required_sections 57 | -------------------------------------------------------------------------------- /webui/eichi_utils/keyframe_handler_extended.py: -------------------------------------------------------------------------------- 1 | """ 2 | 拡張キーフレーム処理モジュール 3 | キーフレーム処理に関する拡張機能を提供します 4 | """ 5 | 6 | import gradio as gr 7 | 8 | from locales.i18n_extended import translate 9 | 10 | from eichi_utils.video_mode_settings import ( 11 | get_total_sections, 12 | get_important_keyframes, 13 | get_video_seconds, 14 | MODE_TYPE_LOOP 15 | ) 16 | from eichi_utils.keyframe_handler import code_to_ui_index, get_max_keyframes_count 17 | from eichi_utils.frame_calculator import calculate_sections_for_mode_and_size 18 | 19 | def extended_mode_length_change_handler(mode, length, section_number_inputs, section_row_groups=None, frame_size_setting="_KEY_FRAME_SIZE_1SEC", enable_keyframe_copy=None): 20 | """モードと動画長の変更を統一的に処理する関数(セクション行の表示/非表示制御を追加) 21 | 22 | Args: 23 | mode: モード ("通常" or "ループ") 24 | length: 動画長 ("6秒", "8秒", "10秒", "12秒", "16秒", "20秒") 25 | section_number_inputs: セクション番号入力欄のリスト 26 | section_row_groups: セクション行のUIグループリスト(オプション) 27 | frame_size_setting: フレームサイズ設定 ("1秒 (33フレーム)" or "0.5秒 (17フレーム)") 28 | enable_keyframe_copy: キーフレーム自動コピー機能の有効/無効状態(オプション) 29 | 30 | Returns: 31 | 更新リスト: 各UI要素の更新情報のリスト 32 | """ 33 | # 通常モードでは全ての赤枠青枠を強制的に非表示にする処理を追加 34 | is_loop_mode = (mode == MODE_TYPE_LOOP) 35 | 36 | # キーフレーム自動コピー機能の状態を確認 37 | # モードとコピー有効フラグの両方が指定されている場合のみコピーを有効にする 38 | if is_loop_mode and enable_keyframe_copy is not None: 39 | is_copy_enabled = enable_keyframe_copy 40 | print(translate("[keyframe_handler_extended] ループモードでキーフレーム自動コピー機能の状態: {state}").format(state=is_copy_enabled)) 41 | else: 42 | # デフォルト状態を使用 - ループモードならTrue、それ以外はFalse 43 | is_copy_enabled = is_loop_mode 44 | print(translate("[keyframe_handler_extended] モード変更によるキーフレーム自動コピー機能のデフォルト状態: {state}").format(state=is_copy_enabled)) 45 | 46 | if not is_loop_mode: 47 | print(translate("[keyframe_handler_extended] 通常モードで強制的に赤枠/青枠を非表示に設定")) 48 | else: 49 | print(translate("[keyframe_handler_extended] ループモードで赤枠/青枠を表示可能に設定")) 50 | 51 | # 基本要素のクリア(入力画像と終了フレーム) 52 | updates = [gr.update(value=None) for _ in range(2)] 53 | 54 | # すべてのキーフレーム画像をクリア 55 | section_image_count = get_max_keyframes_count() 56 | for _ in range(section_image_count): 57 | updates.append(gr.update(value=None, elem_classes="")) 58 | 59 | # セクション番号ラベルをリセット 60 | for i in range(len(section_number_inputs)): 61 | section_number_inputs[i].elem_classes = "" 62 | 63 | # 重要なキーフレームを強調表示 64 | important_kfs = get_important_keyframes(length) 65 | for idx in important_kfs: 66 | ui_idx = code_to_ui_index(idx) 67 | update_idx = ui_idx + 1 # 入力画像と終了フレームの2つを考慮 68 | if update_idx < len(updates): 69 | # 通常モードの場合はすべての枠を非表示にする 70 | if not is_loop_mode: 71 | updates[update_idx] = gr.update(value=None, elem_classes="") 72 | if idx < len(section_number_inputs): 73 | section_number_inputs[idx].elem_classes = "" 74 | continue 75 | 76 | # ループモードでもキーフレーム自動コピー機能が無効なら枠を表示しない 77 | if not is_copy_enabled: 78 | updates[update_idx] = gr.update(value=None, elem_classes="") 79 | if idx < len(section_number_inputs): 80 | section_number_inputs[idx].elem_classes = "" 81 | continue 82 | 83 | # ループモードでキーフレーム自動コピー機能が有効の場合のみセクションによって枠の色を変える 84 | if idx == 0: 85 | # セクション0は赤枠 86 | updates[update_idx] = gr.update(value=None, elem_classes="highlighted-keyframe-red") 87 | if idx < len(section_number_inputs): 88 | section_number_inputs[idx].elem_classes = "highlighted-label-red" 89 | elif idx == 1: 90 | # セクション1は青枠 91 | updates[update_idx] = gr.update(value=None, elem_classes="highlighted-keyframe-blue") 92 | if idx < len(section_number_inputs): 93 | section_number_inputs[idx].elem_classes = "highlighted-label-blue" 94 | else: 95 | # その他のセクションは通常の枠 96 | updates[update_idx] = gr.update(value=None, elem_classes="highlighted-keyframe") 97 | if idx < len(section_number_inputs): 98 | section_number_inputs[idx].elem_classes = "highlighted-label" 99 | 100 | # ループモードの場合はキーフレーム0も強調(まだ強調されていない場合) 101 | # セクション0は赤枠にする - ループモードでコピー機能が有効の場合のみ 102 | if is_loop_mode and is_copy_enabled and 0 not in important_kfs: 103 | print(translate("[keyframe_handler_extended] ループモードでセクション0に赤枠を適用")) 104 | updates[2] = gr.update(value=None, elem_classes="highlighted-keyframe-red") 105 | if 0 < len(section_number_inputs): 106 | section_number_inputs[0].elem_classes = "highlighted-label-red" 107 | elif not (is_loop_mode and is_copy_enabled): 108 | print(translate("[keyframe_handler_extended] モードまたはコピー機能状態により、セクション0の赤枠を適用せず")) 109 | # 通常モードまたはコピー機能が無効の場合は強制的にクリア 110 | updates[2] = gr.update(value=None, elem_classes="") 111 | 112 | # 動画長の設定 113 | video_length = get_video_seconds(length) 114 | 115 | # 最終的な動画長設定を追加 116 | updates.append(gr.update(value=video_length)) 117 | 118 | # セクション行の表示/非表示を制御 119 | if section_row_groups is not None: 120 | # 動画モードとフレームサイズから必要なセクション数を計算 121 | required_sections = calculate_sections_for_mode_and_size(length, frame_size_setting) 122 | print(translate("動画モード '{length}' とフレームサイズ '{frame_size_setting}' で必要なセクション数: {required_sections}").format(length=length, frame_size_setting=frame_size_setting, required_sections=required_sections)) 123 | 124 | # 各セクション行の表示/非表示を設定 125 | row_updates = [] 126 | for i, _ in enumerate(section_row_groups): 127 | if i < required_sections: 128 | row_updates.append(gr.update(visible=True)) 129 | else: 130 | row_updates.append(gr.update(visible=False)) 131 | 132 | # 更新リストに行の表示/非表示設定を追加 133 | updates.extend(row_updates) 134 | 135 | # キーフレーム自動コピー機能のチェックボックス更新は直接出力に追加しない 136 | # 呼び出し元で必要なら別途追加する方が安全 137 | # (既存の出力要素数が変更されると表示崩れの原因になる) 138 | 139 | return updates 140 | -------------------------------------------------------------------------------- /webui/eichi_utils/lora_preset_manager.py: -------------------------------------------------------------------------------- 1 | """ 2 | LoRAプリセット管理モジュール 3 | LoRA選択とスケール値のプリセット保存・読み込み機能を提供 4 | """ 5 | 6 | import os 7 | import json 8 | import traceback 9 | from datetime import datetime 10 | 11 | from locales.i18n_extended import translate 12 | 13 | def get_lora_presets_folder_path(): 14 | """LoRAプリセットフォルダの絶対パスを取得する""" 15 | # eichi_utils直下からwebuiフォルダに移動し、presetsフォルダを使用 16 | webui_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | return os.path.join(webui_path, 'presets') 18 | 19 | def initialize_lora_presets(): 20 | """初期LoRAプリセットファイルがない場合に作成する関数""" 21 | presets_folder = get_lora_presets_folder_path() 22 | os.makedirs(presets_folder, exist_ok=True) 23 | preset_file = os.path.join(presets_folder, 'lora_presets.json') 24 | 25 | # デフォルトのLoRA設定 26 | default_lora_configs = [ 27 | { 28 | "name": translate("デフォルト設定1"), 29 | "lora1": translate("なし"), 30 | "lora2": translate("なし"), 31 | "lora3": translate("なし"), 32 | "scales": "0.8,0.8,0.8" 33 | }, 34 | { 35 | "name": translate("デフォルト設定2"), 36 | "lora1": translate("なし"), 37 | "lora2": translate("なし"), 38 | "lora3": translate("なし"), 39 | "scales": "1.0,0.5,0.3" 40 | } 41 | ] 42 | 43 | # 既存ファイルがあり、正常に読み込める場合は終了 44 | if os.path.exists(preset_file): 45 | try: 46 | with open(preset_file, 'r', encoding='utf-8') as f: 47 | presets_data = json.load(f) 48 | return 49 | except: 50 | # エラーが発生した場合は新規作成 51 | pass 52 | 53 | # 新規作成 54 | presets_data = { 55 | "presets": [], 56 | "default_preset_index": 0 57 | } 58 | 59 | # デフォルトのプリセットを追加 60 | for i, config in enumerate(default_lora_configs): 61 | presets_data["presets"].append({ 62 | "name": config["name"], 63 | "lora1": config["lora1"], 64 | "lora2": config["lora2"], 65 | "lora3": config["lora3"], 66 | "scales": config["scales"], 67 | "timestamp": datetime.now().isoformat(), 68 | "is_default": True 69 | }) 70 | 71 | # 保存 72 | try: 73 | with open(preset_file, 'w', encoding='utf-8') as f: 74 | json.dump(presets_data, f, ensure_ascii=False, indent=2) 75 | except: 76 | # 保存に失敗してもエラーは出さない(次回起動時に再試行される) 77 | pass 78 | 79 | def load_lora_presets(): 80 | """LoRAプリセットを読み込む関数""" 81 | presets_folder = get_lora_presets_folder_path() 82 | os.makedirs(presets_folder, exist_ok=True) 83 | preset_file = os.path.join(presets_folder, 'lora_presets.json') 84 | 85 | # 初期化(必要に応じて) 86 | initialize_lora_presets() 87 | 88 | # プリセットファイルを読み込む 89 | try: 90 | with open(preset_file, 'r', encoding='utf-8') as f: 91 | data = json.load(f) 92 | return data["presets"], data.get("default_preset_index", 0) 93 | except: 94 | # エラーの場合は空のプリセットリストを返す 95 | return [], 0 96 | 97 | def save_lora_preset(preset_index, lora1, lora2, lora3, scales): 98 | """LoRAプリセットを保存する関数""" 99 | presets_folder = get_lora_presets_folder_path() 100 | os.makedirs(presets_folder, exist_ok=True) 101 | preset_file = os.path.join(presets_folder, 'lora_presets.json') 102 | 103 | # 既存のプリセットを読み込む 104 | try: 105 | with open(preset_file, 'r', encoding='utf-8') as f: 106 | data = json.load(f) 107 | except: 108 | data = {"presets": [], "default_preset_index": 0} 109 | 110 | # 5つのプリセットを確保 111 | while len(data["presets"]) < 5: 112 | data["presets"].append({ 113 | "name": translate("設定{0}").format(len(data["presets"]) + 1), 114 | "lora1": translate("なし"), 115 | "lora2": translate("なし"), 116 | "lora3": translate("なし"), 117 | "scales": "0.8,0.8,0.8", 118 | "timestamp": datetime.now().isoformat(), 119 | "is_default": False 120 | }) 121 | 122 | # 指定されたプリセットを更新 123 | if 0 <= preset_index < 5: 124 | data["presets"][preset_index] = { 125 | "name": translate("設定{0}").format(preset_index + 1), 126 | "lora1": lora1 or translate("なし"), 127 | "lora2": lora2 or translate("なし"), 128 | "lora3": lora3 or translate("なし"), 129 | "scales": scales or "0.8,0.8,0.8", 130 | "timestamp": datetime.now().isoformat(), 131 | "is_default": False 132 | } 133 | 134 | # 保存 135 | with open(preset_file, 'w', encoding='utf-8') as f: 136 | json.dump(data, f, ensure_ascii=False, indent=2) 137 | 138 | return True, translate("設定{0}を保存しました").format(preset_index + 1) 139 | else: 140 | return False, translate("無効なプリセット番号です") 141 | 142 | def load_lora_preset(preset_index): 143 | """指定されたプリセットを読み込む関数""" 144 | presets, _ = load_lora_presets() 145 | 146 | if 0 <= preset_index < len(presets): 147 | preset = presets[preset_index] 148 | return ( 149 | preset.get("lora1", translate("なし")), 150 | preset.get("lora2", translate("なし")), 151 | preset.get("lora3", translate("なし")), 152 | preset.get("scales", "0.8,0.8,0.8") 153 | ) 154 | else: 155 | # デフォルト値を返す 156 | return None 157 | 158 | def get_preset_names(): 159 | """プリセット名のリストを取得する関数""" 160 | presets, _ = load_lora_presets() 161 | names = [] 162 | for i in range(5): 163 | if i < len(presets): 164 | names.append(presets[i].get("name", translate("設定{0}").format(i + 1))) 165 | else: 166 | names.append(translate("設定{0}").format(i + 1)) 167 | return names -------------------------------------------------------------------------------- /webui/eichi_utils/model_downloader.py: -------------------------------------------------------------------------------- 1 | import os 2 | from concurrent.futures import ThreadPoolExecutor, as_completed 3 | from huggingface_hub import snapshot_download 4 | 5 | class ModelDownloader: 6 | def __init__(self, max_workers_per_model=4): 7 | # max_parallel_models > 1 は tqdm._lock が競合し異常終了するため、当面は1に固定する 8 | self.max_parallel_models = 1 9 | self.max_workers_per_model = max_workers_per_model 10 | 11 | def _download_models(self, models_to_download): 12 | def download_model(model_info): 13 | kwargs = { 14 | "repo_id": model_info["repo_id"], 15 | "allow_patterns": model_info.get("allow_patterns", "*"), 16 | "max_workers": self.max_workers_per_model, 17 | } 18 | snapshot_download(**kwargs) 19 | 20 | with ThreadPoolExecutor(max_workers=self.max_parallel_models) as executor: 21 | futures = [executor.submit(download_model, model) for model in models_to_download] 22 | for future in as_completed(futures): 23 | future.result() 24 | 25 | def download_original(self): 26 | self._download_models([ 27 | {"repo_id": "hunyuanvideo-community/HunyuanVideo", "allow_patterns": ["tokenizer/*", "tokenizer_2/*", "vae/*", "text_encoder/*", "text_encoder_2/*"]}, 28 | {"repo_id": "lllyasviel/flux_redux_bfl", "allow_patterns": ["feature_extractor/*", "image_encoder/*"]}, 29 | {"repo_id": "lllyasviel/FramePackI2V_HY"}, 30 | ]) 31 | 32 | def download_f1(self): 33 | self._download_models([ 34 | {"repo_id": "hunyuanvideo-community/HunyuanVideo", "allow_patterns": ["tokenizer/*", "tokenizer_2/*", "vae/*", "text_encoder/*", "text_encoder_2/*"]}, 35 | {"repo_id": "lllyasviel/flux_redux_bfl", "allow_patterns": ["feature_extractor/*", "image_encoder/*"]}, 36 | {"repo_id": "lllyasviel/FramePack_F1_I2V_HY_20250503"}, 37 | ]) 38 | -------------------------------------------------------------------------------- /webui/eichi_utils/png_metadata.py: -------------------------------------------------------------------------------- 1 | # png_metadata.py 2 | # FramePack-eichiのためのPNGメタデータ処理ユーティリティ 3 | 4 | import json 5 | import os 6 | from PIL import Image, PngImagePlugin 7 | import traceback 8 | 9 | from locales.i18n_extended import translate 10 | 11 | # メタデータキーの定義 12 | PROMPT_KEY = "prompt" 13 | SEED_KEY = "seed" 14 | SECTION_PROMPT_KEY = "section_prompt" 15 | SECTION_NUMBER_KEY = "section_number" 16 | PARAMETERS_KEY = "parameters" # SD系との互換性のため 17 | 18 | def embed_metadata_to_png(image_path, metadata_dict): 19 | """PNGファイルにメタデータを埋め込む 20 | 21 | Args: 22 | image_path (str): PNGファイルのパス 23 | metadata_dict (dict): 埋め込むメタデータの辞書 24 | 25 | Returns: 26 | str: 処理したファイルのパス 27 | """ 28 | try: 29 | img = Image.open(image_path) 30 | metadata = PngImagePlugin.PngInfo() 31 | 32 | # パラメータを結合したテキストも作成(SD系との互換性のため) 33 | parameters_text = "" 34 | 35 | # パラメータテキストの構築(個別キーは埋め込まない) 36 | for key, value in metadata_dict.items(): 37 | if value is not None: 38 | if key == PROMPT_KEY: 39 | parameters_text += f"{value}\n" 40 | elif key == SEED_KEY: 41 | parameters_text += f"Seed: {value}\n" 42 | elif key == SECTION_PROMPT_KEY: 43 | parameters_text += f"Section Prompt: {value}\n" 44 | elif key == SECTION_NUMBER_KEY: 45 | parameters_text += f"Section Number: {value}\n" 46 | 47 | # パラメータテキストがあれば追加 48 | if parameters_text: 49 | metadata.add_text(PARAMETERS_KEY, parameters_text.strip()) 50 | 51 | # 保存(ファイル形式は変更せず) 52 | if image_path.lower().endswith('.png'): 53 | img.save(image_path, "PNG", pnginfo=metadata) 54 | else: 55 | # PNGでなければPNGに変換して保存 56 | png_path = os.path.splitext(image_path)[0] + '.png' 57 | img.save(png_path, "PNG", pnginfo=metadata) 58 | image_path = png_path 59 | 60 | return image_path 61 | 62 | except Exception as e: 63 | return image_path 64 | 65 | def extract_metadata_from_png(image_path_or_object): 66 | """PNGファイルからメタデータを抽出する 67 | 68 | Args: 69 | image_path_or_object: PNGファイルのパスまたはPIL.Image.Imageオブジェクト 70 | 71 | Returns: 72 | dict: 抽出したメタデータの辞書 73 | """ 74 | try: 75 | # パスが文字列ならイメージを開く、イメージオブジェクトならそのまま使う 76 | if isinstance(image_path_or_object, str): 77 | if not os.path.exists(image_path_or_object): 78 | return {} 79 | img = Image.open(image_path_or_object) 80 | else: 81 | img = image_path_or_object 82 | 83 | metadata = {} 84 | 85 | # 個別のキーを処理 86 | for key in [PROMPT_KEY, SEED_KEY, SECTION_PROMPT_KEY, SECTION_NUMBER_KEY, PARAMETERS_KEY]: 87 | if key in img.info: 88 | value = img.info[key] 89 | try: 90 | # JSONとして解析を試みる 91 | metadata[key] = json.loads(value) 92 | except (json.JSONDecodeError, TypeError): 93 | # JSONでなければそのまま格納 94 | metadata[key] = value 95 | 96 | # parametersキーからの抽出処理(SD系との互換性のため) 97 | # 個別キーが無い場合でもparametersから抽出を試みる 98 | if PARAMETERS_KEY in img.info: 99 | params = img.info[PARAMETERS_KEY] 100 | 101 | # 行に分割して処理 102 | lines = params.split("\n") 103 | 104 | # プロンプト行を収集 105 | prompt_lines = [] 106 | 107 | # 全ての行を処理 108 | for line in lines: 109 | line_stripped = line.strip() 110 | if line_stripped.startswith("Seed:"): 111 | seed_str = line_stripped.replace("Seed:", "").strip() 112 | if seed_str.isdigit(): 113 | metadata[SEED_KEY] = int(seed_str) 114 | elif line_stripped.startswith("Section Number:"): 115 | section_num_str = line_stripped.replace("Section Number:", "").strip() 116 | if section_num_str.isdigit(): 117 | metadata[SECTION_NUMBER_KEY] = int(section_num_str) 118 | elif line_stripped.startswith("Section Prompt:"): 119 | section_prompt = line_stripped.replace("Section Prompt:", "").strip() 120 | if section_prompt: 121 | metadata[SECTION_PROMPT_KEY] = section_prompt 122 | else: 123 | # Seed: や Section: で始まらない行はプロンプトの一部 124 | if line.strip(): # 空行は除外 125 | prompt_lines.append(line.rstrip()) 126 | 127 | # 複数行のプロンプトを結合 128 | if prompt_lines: 129 | metadata[PROMPT_KEY] = "\n".join(prompt_lines) 130 | 131 | return metadata 132 | 133 | except Exception as e: 134 | return {} 135 | 136 | def extract_metadata_from_numpy_array(numpy_image): 137 | """NumPy配列からメタデータを抽出する(PILを介して) 138 | 139 | Args: 140 | numpy_image: NumPy配列の画像データ 141 | 142 | Returns: 143 | dict: 抽出したメタデータの辞書 144 | """ 145 | try: 146 | if numpy_image is None: 147 | return {} 148 | 149 | # NumPy配列からPIL.Imageに変換 150 | img = Image.fromarray(numpy_image) 151 | 152 | # メタデータの抽出を試行 153 | metadata = extract_metadata_from_png(img) 154 | 155 | return metadata 156 | 157 | except Exception as e: 158 | return {} 159 | -------------------------------------------------------------------------------- /webui/eichi_utils/tensor_combiner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | import torch 5 | import traceback 6 | import safetensors.torch as sf 7 | from datetime import datetime 8 | import gradio as gr 9 | 10 | # ルートパスをシステムパスに追加 11 | root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 12 | if root_path not in sys.path: 13 | sys.path.append(root_path) 14 | 15 | # ルートパスを追加した後でインポート 16 | from locales.i18n_extended import translate 17 | 18 | def combine_tensor_files(file1_path, file2_path, output_path=None): 19 | """2つのsafetensorsファイルを読み込み、結合して新しいファイルに保存する 20 | 21 | Args: 22 | file1_path (str): 1つ目のsafetensorsファイルパス 23 | file2_path (str): 2つ目のsafetensorsファイルパス 24 | output_path (str, optional): 出力ファイルパス。指定しない場合は自動生成 25 | 26 | Returns: 27 | tuple: (成功したかどうかのbool, 出力ファイルパス, 結果メッセージ) 28 | """ 29 | try: 30 | # ファイル1を読み込み 31 | print(translate("ファイル1を読み込み中: {0}").format(os.path.basename(file1_path))) 32 | tensor_dict1 = sf.load_file(file1_path) 33 | 34 | # ファイル2を読み込み 35 | print(translate("ファイル2を読み込み中: {0}").format(os.path.basename(file2_path))) 36 | tensor_dict2 = sf.load_file(file2_path) 37 | 38 | # テンソルを取得 39 | if "history_latents" in tensor_dict1 and "history_latents" in tensor_dict2: 40 | tensor1 = tensor_dict1["history_latents"] 41 | tensor2 = tensor_dict2["history_latents"] 42 | 43 | # テンソル情報の表示 44 | print(translate("テンソル1: shape={0}, dtype={1}, フレーム数={2}").format(tensor1.shape, tensor1.dtype, tensor1.shape[2])) 45 | print(translate("テンソル2: shape={0}, dtype={1}, フレーム数={2}").format(tensor2.shape, tensor2.dtype, tensor2.shape[2])) 46 | 47 | # サイズチェック 48 | if tensor1.shape[3] != tensor2.shape[3] or tensor1.shape[4] != tensor2.shape[4]: 49 | error_msg = translate("エラー: テンソルサイズが異なります: {0} vs {1}").format(tensor1.shape, tensor2.shape) 50 | print(error_msg) 51 | return False, None, error_msg 52 | 53 | # データ型とデバイスの調整 54 | if tensor1.dtype != tensor2.dtype: 55 | print(translate("データ型の変換: {0} → {1}").format(tensor2.dtype, tensor1.dtype)) 56 | tensor2 = tensor2.to(dtype=tensor1.dtype) 57 | 58 | # 両方CPUに移動 59 | tensor1 = tensor1.cpu() 60 | tensor2 = tensor2.cpu() 61 | 62 | # 結合(テンソル1の後にテンソル2を追加) 63 | combined_tensor = torch.cat([tensor1, tensor2], dim=2) 64 | 65 | # 結合されたテンソルの情報を表示 66 | tensor1_frames = tensor1.shape[2] 67 | tensor2_frames = tensor2.shape[2] 68 | combined_frames = combined_tensor.shape[2] 69 | print(translate("結合成功: 結合後のフレーム数={0} ({1}+{2}フレーム)").format(combined_frames, tensor1_frames, tensor2_frames)) 70 | 71 | # メタデータを更新 72 | height, width = tensor1.shape[3], tensor1.shape[4] 73 | metadata = torch.tensor([height, width, combined_frames], dtype=torch.int32) 74 | 75 | # 出力ファイルパスが指定されていない場合は自動生成 76 | if output_path is None: 77 | timestamp = datetime.now().strftime("%y%m%d_%H%M%S") 78 | output_dir = os.path.dirname(file1_path) 79 | output_path = os.path.join(output_dir, f"combined_{timestamp}.safetensors") 80 | 81 | # 結合したテンソルをファイルに保存 82 | tensor_dict = { 83 | "history_latents": combined_tensor, 84 | "metadata": metadata 85 | } 86 | 87 | # ファイル保存 88 | sf.save_file(tensor_dict, output_path) 89 | 90 | # テンソルデータの保存サイズの概算 91 | tensor_size_mb = (combined_tensor.element_size() * combined_tensor.nelement()) / (1024 * 1024) 92 | 93 | success_msg = translate("結合テンソルを保存しました: {0}\n").format(os.path.basename(output_path)) 94 | success_msg += translate("フレーム数: {0}フレーム ({1}+{2}フレーム)\n").format(combined_frames, tensor1_frames, tensor2_frames) 95 | success_msg += translate("サイズ: {0:.2f}MB, 形状: {1}").format(tensor_size_mb, combined_tensor.shape) 96 | print(success_msg) 97 | 98 | return True, output_path, success_msg 99 | else: 100 | error_msg = translate("エラー: テンソルファイルに必要なキー'history_latents'がありません") 101 | print(error_msg) 102 | return False, None, error_msg 103 | 104 | except Exception as e: 105 | error_msg = translate("テンソル結合中にエラーが発生: {0}").format(e) 106 | print(error_msg) 107 | traceback.print_exc() 108 | return False, None, error_msg 109 | 110 | def create_ui(): 111 | """Gradio UIを作成""" 112 | with gr.Blocks(title=translate("テンソル結合ツール")) as app: 113 | gr.Markdown(translate("## テンソルデータ結合ツール")) 114 | gr.Markdown(translate("safetensors形式のテンソルデータファイルを2つ選択して結合します。結合順序は「テンソル1 + テンソル2」です。")) 115 | 116 | with gr.Row(): 117 | with gr.Column(scale=1): 118 | tensor_file1 = gr.File(label=translate("テンソルファイル1 (.safetensors)"), file_types=[".safetensors"]) 119 | with gr.Column(scale=1): 120 | tensor_file2 = gr.File(label=translate("テンソルファイル2 (.safetensors)"), file_types=[".safetensors"]) 121 | 122 | with gr.Row(): 123 | output_file = gr.Textbox(label=translate("出力ファイル名 (空欄で自動生成)"), placeholder=translate("例: combined.safetensors")) 124 | 125 | with gr.Row(): 126 | combine_btn = gr.Button(translate("テンソルファイルを結合"), variant="primary") 127 | 128 | with gr.Row(): 129 | result_output = gr.Textbox(label=translate("結果"), lines=5) 130 | 131 | def combine_tensors(file1, file2, output_path): 132 | if file1 is None or file2 is None: 133 | return translate("エラー: 2つのテンソルファイルを選択してください") 134 | 135 | file1_path = file1.name 136 | file2_path = file2.name 137 | 138 | # 出力パスの決定 139 | if output_path and output_path.strip(): 140 | # 拡張子のチェックと追加 141 | if not output_path.lower().endswith('.safetensors'): 142 | output_path += '.safetensors' 143 | # ディレクトリパスの決定(入力ファイルと同じ場所) 144 | output_dir = os.path.dirname(file1_path) 145 | full_output_path = os.path.join(output_dir, output_path) 146 | else: 147 | # 自動生成の場合はNoneのまま(関数内で自動生成) 148 | full_output_path = None 149 | 150 | success, result_path, message = combine_tensor_files(file1_path, file2_path, full_output_path) 151 | if success: 152 | return message 153 | else: 154 | return translate("結合失敗: {0}").format(message) 155 | 156 | combine_btn.click( 157 | fn=combine_tensors, 158 | inputs=[tensor_file1, tensor_file2, output_file], 159 | outputs=[result_output] 160 | ) 161 | 162 | return app 163 | 164 | def main(): 165 | """コマンドライン引数を解析して実行""" 166 | parser = argparse.ArgumentParser(description=translate("2つのsafetensorsファイルを結合するツール")) 167 | parser.add_argument('--file1', type=str, help=translate("1つ目のsafetensorsファイルパス")) 168 | parser.add_argument('--file2', type=str, help=translate("2つ目のsafetensorsファイルパス")) 169 | parser.add_argument('--output', type=str, default=None, help=translate("出力ファイルパス (省略可能)")) 170 | parser.add_argument('--ui', action='store_true', help=translate("GradioのUIモードで起動")) 171 | 172 | args = parser.parse_args() 173 | 174 | if args.ui: 175 | # UIモードで起動 176 | app = create_ui() 177 | app.launch() 178 | elif args.file1 and args.file2: 179 | # コマンドラインモードで実行 180 | success, output_path, message = combine_tensor_files(args.file1, args.file2, args.output) 181 | if success: 182 | print(translate("結合成功:")) 183 | print(message) 184 | return 0 185 | else: 186 | print(translate("結合失敗:")) 187 | print(message) 188 | return 1 189 | else: 190 | parser.print_help() 191 | return 1 192 | 193 | if __name__ == "__main__": 194 | sys.exit(main()) -------------------------------------------------------------------------------- /webui/eichi_utils/text_encoder_manager.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import traceback 3 | import gc 4 | from diffusers_helper.memory import DynamicSwapInstaller 5 | from locales.i18n_extended import translate 6 | 7 | class TextEncoderManager: 8 | """text_encoderとtext_encoder_2の状態管理を行うクラス 9 | 10 | このクラスは以下の責務を持ちます: 11 | - text_encoderとtext_encoder_2のライフサイクル管理 12 | 13 | 設定の変更はすぐには適用されず、次回のリロード時に適用されます。 14 | """ 15 | 16 | def __init__(self, device, high_vram_mode=False): 17 | self.text_encoder = None 18 | self.text_encoder_2 = None 19 | self.device = device 20 | 21 | # 現在適用されている設定 22 | self.current_state = { 23 | 'is_loaded': False, 24 | 'high_vram': high_vram_mode 25 | } 26 | 27 | # 次回のロード時に適用する設定 28 | self.next_state = self.current_state.copy() 29 | 30 | def set_next_settings(self, high_vram_mode=False): 31 | """次回のロード時に使用する設定をセット(即時のリロードは行わない) 32 | 33 | Args: 34 | high_vram_mode: High-VRAMモードの有効/無効 35 | """ 36 | self.next_state = { 37 | 'high_vram': high_vram_mode, 38 | 'is_loaded': self.current_state['is_loaded'] 39 | } 40 | print(translate("次回のtext_encoder設定を設定しました:")) 41 | print(translate(" - High-VRAM mode: {0}").format(high_vram_mode)) 42 | 43 | def _needs_reload(self): 44 | """現在の状態と次回の設定を比較し、リロードが必要かどうかを判断""" 45 | if not self._is_loaded(): 46 | return True 47 | 48 | # High-VRAMモードの比較 49 | if self.current_state['high_vram'] != self.next_state['high_vram']: 50 | return True 51 | 52 | return False 53 | 54 | def _is_loaded(self): 55 | """text_encoderとtext_encoder_2が読み込まれているかどうかを確認""" 56 | return (self.text_encoder is not None and 57 | self.text_encoder_2 is not None and 58 | self.current_state['is_loaded']) 59 | 60 | def get_text_encoders(self): 61 | """現在のtext_encoderとtext_encoder_2インスタンスを取得""" 62 | return self.text_encoder, self.text_encoder_2 63 | 64 | def dispose_text_encoders(self): 65 | """text_encoderとtext_encoder_2のインスタンスを破棄し、メモリを完全に解放""" 66 | try: 67 | print(translate("text_encoderとtext_encoder_2のメモリを解放します...")) 68 | 69 | # text_encoderの破棄 70 | if hasattr(self, 'text_encoder') and self.text_encoder is not None: 71 | try: 72 | self.text_encoder.cpu() 73 | del self.text_encoder 74 | self.text_encoder = None 75 | except Exception as e: 76 | print(translate("text_encoderの破棄中にエラー: {0}").format(e)) 77 | 78 | # text_encoder_2の破棄 79 | if hasattr(self, 'text_encoder_2') and self.text_encoder_2 is not None: 80 | try: 81 | self.text_encoder_2.cpu() 82 | del self.text_encoder_2 83 | self.text_encoder_2 = None 84 | except Exception as e: 85 | print(translate("text_encoder_2の破棄中にエラー: {0}").format(e)) 86 | 87 | # 明示的なメモリ解放 88 | if torch.cuda.is_available(): 89 | torch.cuda.empty_cache() 90 | gc.collect() 91 | 92 | # 状態を更新 93 | self.current_state['is_loaded'] = False 94 | self.next_state['is_loaded'] = False 95 | 96 | print(translate("text_encoderとtext_encoder_2のメモリ解放が完了しました")) 97 | return True 98 | 99 | except Exception as e: 100 | print(translate("text_encoderとtext_encoder_2のメモリ解放中にエラー: {0}").format(e)) 101 | traceback.print_exc() 102 | return False 103 | 104 | def ensure_text_encoder_state(self): 105 | """text_encoderとtext_encoder_2の状態を確認し、必要に応じてリロード""" 106 | if self._needs_reload(): 107 | print(translate("text_encoderとtext_encoder_2をリロードします")) 108 | return self._reload_text_encoders() 109 | print(translate("ロード済みのtext_encoderとtext_encoder_2を再度利用します")) 110 | return True 111 | 112 | def _reload_text_encoders(self): 113 | """next_stateの設定でtext_encoderとtext_encoder_2をリロード""" 114 | try: 115 | # 既存のモデルが存在する場合は先にメモリを解放 116 | if self._is_loaded(): 117 | self.dispose_text_encoders() 118 | 119 | # 新しいtext_encoderとtext_encoder_2インスタンスを作成 120 | from transformers import LlamaModel, CLIPTextModel 121 | self.text_encoder = LlamaModel.from_pretrained( 122 | "hunyuanvideo-community/HunyuanVideo", 123 | subfolder='text_encoder', 124 | torch_dtype=torch.float16 125 | ).cpu() 126 | 127 | self.text_encoder_2 = CLIPTextModel.from_pretrained( 128 | "hunyuanvideo-community/HunyuanVideo", 129 | subfolder='text_encoder_2', 130 | torch_dtype=torch.float16 131 | ).cpu() 132 | 133 | self.text_encoder.eval() 134 | self.text_encoder_2.eval() 135 | 136 | self.text_encoder.to(dtype=torch.float16) 137 | self.text_encoder_2.to(dtype=torch.float16) 138 | 139 | self.text_encoder.requires_grad_(False) 140 | self.text_encoder_2.requires_grad_(False) 141 | 142 | # VRAMモードに応じた設定 143 | if not self.next_state['high_vram']: 144 | DynamicSwapInstaller.install_model(self.text_encoder, device=self.device) 145 | DynamicSwapInstaller.install_model(self.text_encoder_2, device=self.device) 146 | else: 147 | self.text_encoder.to(self.device) 148 | self.text_encoder_2.to(self.device) 149 | 150 | # 状態を更新 151 | self.next_state['is_loaded'] = True 152 | self.current_state = self.next_state.copy() 153 | 154 | print(translate("text_encoderとtext_encoder_2のリロードが完了しました")) 155 | return True 156 | 157 | except Exception as e: 158 | print(translate("text_encoderとtext_encoder_2リロードエラー: {0}").format(e)) 159 | traceback.print_exc() 160 | self.current_state['is_loaded'] = False 161 | return False 162 | -------------------------------------------------------------------------------- /webui/eichi_utils/ui_styles.py: -------------------------------------------------------------------------------- 1 | """ 2 | UI関連のスタイルを定義するモジュール 3 | """ 4 | from diffusers_helper.gradio.progress_bar import make_progress_bar_css 5 | 6 | from locales.i18n import translate 7 | 8 | def get_app_css(): 9 | """ 10 | アプリケーションのCSSスタイルを返す 11 | 12 | Returns: 13 | str: CSSスタイル定義 14 | """ 15 | return make_progress_bar_css() + """ 16 | .title-suffix { 17 | color: currentColor; 18 | opacity: 0.05; 19 | } 20 | 21 | /* 赤枠のキーフレーム - 偶数パターン用 */ 22 | .highlighted-keyframe-red { 23 | border: 4px solid #ff3860 !important; 24 | box-shadow: 0 0 10px rgba(255, 56, 96, 0.5) !important; 25 | background-color: rgba(255, 56, 96, 0.05) !important; 26 | position: relative; 27 | } 28 | 29 | /* 赤枠キーフレームに「偶数番号」のラベルを追加 */ 30 | .highlighted-keyframe-red::after { 31 | """ + 'content: "' + translate("偶数セクションのコピー元") + '"' + """; 32 | position: absolute; 33 | top: 5px; 34 | right: 5px; 35 | background: rgba(255, 56, 96, 0.8); 36 | color: white; 37 | padding: 2px 6px; 38 | font-size: 10px; 39 | border-radius: 4px; 40 | pointer-events: none; 41 | } 42 | 43 | /* 青枠のキーフレーム - 奇数パターン用 */ 44 | .highlighted-keyframe-blue { 45 | border: 4px solid #3273dc !important; 46 | box-shadow: 0 0 10px rgba(50, 115, 220, 0.5) !important; 47 | background-color: rgba(50, 115, 220, 0.05) !important; 48 | position: relative; 49 | } 50 | 51 | /* 青枠キーフレームに「奇数番号」のラベルを追加 */ 52 | .highlighted-keyframe-blue::after { 53 | """ + 'content: "' + translate("奇数セクションのコピー元") + '"' + """; 54 | position: absolute; 55 | top: 5px; 56 | right: 5px; 57 | background: rgba(50, 115, 220, 0.8); 58 | color: white; 59 | padding: 2px 6px; 60 | font-size: 10px; 61 | border-radius: 4px; 62 | pointer-events: none; 63 | } 64 | 65 | /* 引き続きサポート(古いクラス名)- 前方互換性用 */ 66 | .highlighted-keyframe { 67 | border: 4px solid #ff3860 !important; 68 | box-shadow: 0 0 10px rgba(255, 56, 96, 0.5) !important; 69 | background-color: rgba(255, 56, 96, 0.05) !important; 70 | } 71 | 72 | /* 赤枠用セクション番号ラベル */ 73 | .highlighted-label-red label { 74 | color: #ff3860 !important; 75 | font-weight: bold !important; 76 | } 77 | 78 | /* 青枠用セクション番号ラベル */ 79 | .highlighted-label-blue label { 80 | color: #3273dc !important; 81 | font-weight: bold !important; 82 | } 83 | 84 | /* 引き続きサポート(古いクラス名)- 前方互換性用 */ 85 | .highlighted-label label { 86 | color: #ff3860 !important; 87 | font-weight: bold !important; 88 | } 89 | 90 | /* オールパディングの高さ調整 */ 91 | #all_padding_checkbox { 92 | padding-top: 1.5rem; 93 | min-height: 5.8rem; 94 | } 95 | 96 | #all_padding_checkbox .wrap { 97 | align-items: flex-start; 98 | } 99 | 100 | #all_padding_checkbox .label-wrap { 101 | margin-bottom: 0.8rem; 102 | font-weight: 500; 103 | font-size: 14px; 104 | } 105 | 106 | #all_padding_checkbox .info { 107 | margin-top: 0.2rem; 108 | } 109 | 110 | /* セクション間の区切り線を太くする */ 111 | .section-row { 112 | border-bottom: 4px solid #3273dc; 113 | margin-bottom: 20px; 114 | padding-bottom: 15px; 115 | margin-top: 10px; 116 | position: relative; 117 | } 118 | 119 | /* セクション番号を目立たせる */ 120 | .section-row .gr-form:first-child label { 121 | font-weight: bold; 122 | font-size: 1.1em; 123 | color: #3273dc; 124 | background-color: rgba(50, 115, 220, 0.1); 125 | padding: 5px 10px; 126 | border-radius: 4px; 127 | margin-bottom: 10px; 128 | display: inline-block; 129 | } 130 | 131 | /* セクションの背景を少し強調 */ 132 | .section-row { 133 | background-color: rgba(50, 115, 220, 0.03); 134 | border-radius: 8px; 135 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 136 | } 137 | 138 | /* セクション間の余白を増やす */ 139 | .section-container > .gr-block:not(:first-child) { 140 | margin-top: 10px; 141 | } 142 | 143 | /* アコーディオンセクションのスタイル */ 144 | .section-accordion { 145 | margin-top: 15px; 146 | margin-bottom: 15px; 147 | border-left: 4px solid #3273dc; 148 | padding-left: 10px; 149 | } 150 | 151 | .section-accordion h3 button { 152 | font-weight: bold; 153 | color: #3273dc; 154 | } 155 | 156 | .section-accordion .gr-block { 157 | border-radius: 8px; 158 | } 159 | 160 | /* 保存対象の設定項目を薄い青色でハイライト(ライト/ダークモード対応) */ 161 | .saveable-setting { 162 | background-color: rgba(240, 248, 255, 0.5) !important; /* 薄い青色を透過指定(ライトモード) */ 163 | border-left: 3px solid #90caf9 !important; /* 薄いボーダー色 */ 164 | } 165 | 166 | /* システムのダークモード対応 */ 167 | @media (prefers-color-scheme: dark) { 168 | .saveable-setting { 169 | background-color: rgba(25, 35, 60, 0.4) !important; /* ダークモードでの背景色 */ 170 | border-left: 3px solid #64b5f6 !important; /* ダークモードでのボーダー色(少し明るめ) */ 171 | } 172 | } 173 | 174 | /* Gradioのダークテーマ対応 */ 175 | .dark .saveable-setting { 176 | background-color: rgba(25, 35, 60, 0.4) !important; /* ダークモードでの背景色 */ 177 | border-left: 3px solid #64b5f6 !important; /* ダークモードでのボーダー色(少し明るめ) */ 178 | } 179 | 180 | /* 保存対象項目のラベルにアイコンを追加 */ 181 | .saveable-setting label::before { 182 | content: "💾 "; 183 | margin-right: 5px; 184 | } 185 | 186 | /* ダークモードでのラベル色調整 */ 187 | .dark .saveable-setting label { 188 | color: #90caf9 !important; /* ダークモードで少し明るい青に */ 189 | } 190 | 191 | /* markdownタイトル用 */ 192 | .markdown-title { 193 | padding: 3px; 194 | } 195 | 196 | /* markdownサブタイトル用 */ 197 | .markdown-subtitle { 198 | padding: 2px; 199 | } 200 | 201 | /* markdown領域用 */ 202 | .markdown-desc { 203 | padding: 2px; 204 | } 205 | 206 | /* グルーピング用ボーダー */ 207 | .group-border { 208 | border: solid 1px; 209 | } 210 | """ -------------------------------------------------------------------------------- /webui/locales/i18n.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os.path 3 | 4 | # デフォルト言語設定 5 | # 注意: init関数を呼び出すまでは翻訳機能は使用できません 6 | lang = "ja" # 明示的にデフォルト言語を日本語(ja)に設定 7 | translateContext = None 8 | 9 | class I18nString(str): 10 | def __new__(cls, value): 11 | result = translateContext.get(lang, {}).get(value, value) 12 | return result 13 | 14 | def __init__(self, value): 15 | if isinstance(value, I18nString): 16 | self.add_values = value.add_values 17 | self.radd_values = value.radd_values 18 | else: 19 | self.add_values = [] 20 | self.radd_values = [] 21 | 22 | def __str__(self): 23 | result = translateContext.get(lang, {}).get(self, super().__str__()) 24 | 25 | for v in self.radd_values: 26 | result = str(v) + result 27 | 28 | for v in self.add_values: 29 | result = result + str(v) 30 | 31 | # hotfix, remove unexpected single quotes 32 | while len(result) >= 2 and result.startswith("'") and result.endswith("'"): 33 | result = result[1:-1] 34 | 35 | return result 36 | 37 | def __add__(self, other): 38 | v = str(self) 39 | if isinstance(v, I18nString): 40 | self.add_values.append(other) 41 | return self 42 | return v.__add__(other) 43 | 44 | def __radd__(self, other): 45 | v = str(self) 46 | if isinstance(v, I18nString): 47 | self.radd_values.append(other) 48 | return self 49 | return other.__add__(v) 50 | 51 | def __hash__(self) -> int: 52 | return super().__hash__() 53 | 54 | def format(self, *args, **kwargs) -> str: 55 | v = str(self) 56 | if isinstance(v, I18nString): 57 | return super().format(*args, **kwargs) 58 | return v.format(*args, **kwargs) 59 | 60 | def unwrap(self): 61 | return super().__str__() 62 | 63 | @staticmethod 64 | def unwrap_strings(obj): 65 | """Unwrap all keys in I18nStrings in the object""" 66 | if isinstance(obj, I18nString): 67 | yield obj.unwrap() 68 | for v in obj.add_values: 69 | yield from I18nString.unwrap_strings(v) 70 | for v in obj.radd_values: 71 | yield from I18nString.unwrap_strings(v) 72 | return 73 | yield obj 74 | 75 | def translate(key: str): 76 | """指定されたキーに対応する翻訳文字列を返します。 77 | 78 | Args: 79 | key: 翻訳したい文字列のキー 80 | 81 | Returns: 82 | I18nString: 現在の言語設定に基づいた翻訳文字列 83 | """ 84 | # デバッグ用:translateContextがロードされていない場合に自動的にロード 85 | global translateContext 86 | if translateContext is None: 87 | # 自動的にinitializeを呼び出す 88 | init(lang) 89 | 90 | return I18nString(key) 91 | 92 | def load_translations(): 93 | translations = {} 94 | locales_dir = os.path.join(os.path.dirname(__file__), './') 95 | 96 | for locale in ["en", "ja", "zh-tw", "ru"]: 97 | json_file = os.path.join(locales_dir, f"{locale}.json") 98 | if os.path.exists(json_file): 99 | with open(json_file, 'r', encoding='utf-8') as f: 100 | translations[locale] = json.load(f) 101 | else: 102 | print("Warning: Translation file {0} not found".format(json_file)) 103 | translations[locale] = {} 104 | 105 | return translations 106 | 107 | def init(locale="ja"): 108 | """言語を初期化します。 109 | 110 | Args: 111 | locale: 使用する言語コード(例: 'ja', 'en', 'zh-tw')。 112 | 未対応の言語の場合は自動的に'ja'が使用されます。 113 | """ 114 | global lang 115 | global translateContext 116 | 117 | # 対応言語のリスト 118 | supported_locales = ["ja", "en", "zh-tw", "ru"] 119 | 120 | # 対応していない言語の場合はデフォルト言語(ja)を使用 121 | if locale not in supported_locales: 122 | print("Unsupported language: {0}. Falling back to 'ja'".format(locale)) 123 | locale = "ja" 124 | 125 | lang = locale 126 | translateContext = load_translations() 127 | -------------------------------------------------------------------------------- /webui/locales/i18n_extended.py: -------------------------------------------------------------------------------- 1 | def set_lang(language): 2 | """言語設定を行う""" 3 | from locales import i18n 4 | i18n.lang = language 5 | i18n.init(language) # 言語設定を反映(明示的に言語を渡す) 6 | 7 | # i18nモジュールからtranslate関数をインポート 8 | from locales.i18n import translate 9 | 10 | """ 11 | FramePack-eichi 拡張i18nモジュール 12 | 各言語間の変換と内部キーの管理を行います 13 | """ 14 | 15 | import json 16 | import os.path 17 | from locales import i18n 18 | 19 | # 逆マッピング用辞書 20 | _reverse_mapping = { 21 | # 英語→内部キー 22 | "0.5 seconds (17 frames)": "_KEY_FRAME_SIZE_05SEC", 23 | "1 second (33 frames)": "_KEY_FRAME_SIZE_1SEC", 24 | "Normal": "_KEY_MODE_NORMAL", 25 | "Normal mode": "_KEY_MODE_NORMAL_FULL", 26 | "Loop": "_KEY_MODE_LOOP", 27 | "Loop mode": "_KEY_MODE_LOOP_FULL", 28 | "1 second": "_KEY_VIDEO_LENGTH_1SEC", 29 | "2s": "_KEY_VIDEO_LENGTH_2SEC", 30 | "3s": "_KEY_VIDEO_LENGTH_3SEC", 31 | "4s": "_KEY_VIDEO_LENGTH_4SEC", 32 | "6s": "_KEY_VIDEO_LENGTH_6SEC", 33 | "8s": "_KEY_VIDEO_LENGTH_8SEC", 34 | "10s": "_KEY_VIDEO_LENGTH_10SEC", 35 | "12s": "_KEY_VIDEO_LENGTH_12SEC", 36 | "16s": "_KEY_VIDEO_LENGTH_16SEC", 37 | "20s": "_KEY_VIDEO_LENGTH_20SEC", 38 | 39 | # 中国語→内部キー 40 | "0.5秒 (17幀)": "_KEY_FRAME_SIZE_05SEC", 41 | "1秒 (33幀)": "_KEY_FRAME_SIZE_1SEC", 42 | "常規": "_KEY_MODE_NORMAL", 43 | "常規模式": "_KEY_MODE_NORMAL_FULL", 44 | "循環": "_KEY_MODE_LOOP", 45 | "循環模式": "_KEY_MODE_LOOP_FULL", 46 | "1秒": "_KEY_VIDEO_LENGTH_1SEC", 47 | "2秒": "_KEY_VIDEO_LENGTH_2SEC", 48 | "3秒": "_KEY_VIDEO_LENGTH_3SEC", 49 | "4秒": "_KEY_VIDEO_LENGTH_4SEC", 50 | "6秒": "_KEY_VIDEO_LENGTH_6SEC", 51 | "8秒": "_KEY_VIDEO_LENGTH_8SEC", 52 | "10秒": "_KEY_VIDEO_LENGTH_10SEC", 53 | "12秒": "_KEY_VIDEO_LENGTH_12SEC", 54 | "16秒": "_KEY_VIDEO_LENGTH_16SEC", 55 | "20秒": "_KEY_VIDEO_LENGTH_20SEC", 56 | 57 | # 日本語→内部キー 58 | "0.5秒 (17フレーム)": "_KEY_FRAME_SIZE_05SEC", 59 | "1秒 (33フレーム)": "_KEY_FRAME_SIZE_1SEC", 60 | "通常": "_KEY_MODE_NORMAL", 61 | "通常モード": "_KEY_MODE_NORMAL_FULL", 62 | "ループ": "_KEY_MODE_LOOP", 63 | "ループモード": "_KEY_MODE_LOOP_FULL", 64 | "1秒": "_KEY_VIDEO_LENGTH_1SEC", 65 | "2秒": "_KEY_VIDEO_LENGTH_2SEC", 66 | "3秒": "_KEY_VIDEO_LENGTH_3SEC", 67 | "4秒": "_KEY_VIDEO_LENGTH_4SEC", 68 | "6秒": "_KEY_VIDEO_LENGTH_6SEC", 69 | "8秒": "_KEY_VIDEO_LENGTH_8SEC", 70 | "10秒": "_KEY_VIDEO_LENGTH_10SEC", 71 | "12秒": "_KEY_VIDEO_LENGTH_12SEC", 72 | "16秒": "_KEY_VIDEO_LENGTH_16SEC", 73 | "20秒": "_KEY_VIDEO_LENGTH_20SEC", 74 | 75 | # ロシア語→内部キー 76 | "0.5 секунды (17 кадров)": "_KEY_FRAME_SIZE_05SEC", 77 | "1 секунда (33 кадра)": "_KEY_FRAME_SIZE_1SEC", 78 | "Нормальный": "_KEY_MODE_NORMAL", 79 | "Нормальный режим": "_KEY_MODE_NORMAL_FULL", 80 | "Цикл": "_KEY_MODE_LOOP", 81 | "Циклический режим": "_KEY_MODE_LOOP_FULL", 82 | "1 секунда": "_KEY_VIDEO_LENGTH_1SEC", 83 | "2 сек": "_KEY_VIDEO_LENGTH_2SEC", 84 | "3 сек": "_KEY_VIDEO_LENGTH_3SEC", 85 | "4 сек": "_KEY_VIDEO_LENGTH_4SEC", 86 | "6 сек": "_KEY_VIDEO_LENGTH_6SEC", 87 | "8 сек": "_KEY_VIDEO_LENGTH_8SEC", 88 | "10 сек": "_KEY_VIDEO_LENGTH_10SEC", 89 | "12 сек": "_KEY_VIDEO_LENGTH_12SEC", 90 | "16 сек": "_KEY_VIDEO_LENGTH_16SEC", 91 | "20 сек": "_KEY_VIDEO_LENGTH_20SEC", 92 | } 93 | 94 | # 内部キーから各言語への変換マップ 95 | _internal_to_lang = { 96 | "ja": {}, 97 | "en": {}, 98 | "zh-tw": {}, 99 | "ru": {} 100 | } 101 | 102 | def init(): 103 | """逆マッピングを初期化""" 104 | global _reverse_mapping 105 | global _internal_to_lang 106 | 107 | # 各言語ファイルを読み込み 108 | locales_dir = os.path.join(os.path.dirname(__file__), './') 109 | for locale in ["en", "ja", "zh-tw", "ru"]: 110 | json_file = os.path.join(locales_dir, f"{locale}.json") 111 | if os.path.exists(json_file): 112 | with open(json_file, 'r', encoding='utf-8') as f: 113 | translations = json.load(f) 114 | 115 | # 内部キー(_KEY_で始まるもの)の逆マッピングを構築 116 | for key, value in translations.items(): 117 | if key.startswith("_KEY_"): 118 | # 逆マッピング: 翻訳文字列→内部キー 119 | _reverse_mapping[value] = key 120 | # 正マッピング: 内部キー→翻訳文字列 121 | _internal_to_lang[locale][key] = value 122 | 123 | def get_internal_key(translated_text): 124 | """翻訳された文字列から内部キーを取得""" 125 | return _reverse_mapping.get(translated_text, translated_text) 126 | 127 | def get_original_japanese(translated_text): 128 | """翻訳された文字列から元の日本語を取得""" 129 | internal_key = get_internal_key(translated_text) 130 | # 内部キーが見つからない場合は元の文字列を返す 131 | if internal_key == translated_text: 132 | return translated_text 133 | 134 | # 内部キーから日本語訳を取得 135 | return _internal_to_lang.get("ja", {}).get(internal_key, translated_text) 136 | 137 | def convert_between_languages(text, from_lang, to_lang): 138 | """ある言語の文字列を別の言語に変換""" 139 | # 内部キーを取得 140 | internal_key = get_internal_key(text) 141 | 142 | # 内部キーが見つからない場合は元の文字列を返す 143 | if internal_key == text: 144 | return text 145 | 146 | # 目的言語の翻訳を取得 147 | return _internal_to_lang.get(to_lang, {}).get(internal_key, text) 148 | 149 | # 初期化を実行 150 | init() 151 | -------------------------------------------------------------------------------- /webui/lora_utils/__init__.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi LoRA Utilities 2 | # 3 | # LoRAの適用、FP8最適化、LoRAフォーマット検出と変換のための機能を提供します。 4 | 5 | from .lora_utils import ( 6 | merge_lora_to_state_dict, 7 | load_safetensors_with_lora_and_fp8, 8 | load_safetensors_with_fp8_optimization, 9 | convert_hunyuan_to_framepack, 10 | convert_from_diffusion_pipe_or_something 11 | ) 12 | 13 | from .fp8_optimization_utils import ( 14 | calculate_fp8_maxval, 15 | quantize_tensor_to_fp8, 16 | optimize_state_dict_with_fp8_on_the_fly, 17 | fp8_linear_forward_patch, 18 | apply_fp8_monkey_patch, 19 | check_fp8_support 20 | ) 21 | 22 | from .lora_loader import ( 23 | load_and_apply_lora 24 | ) 25 | 26 | from .safetensors_utils import ( 27 | MemoryEfficientSafeOpen 28 | ) 29 | 30 | # 国際化対応ヘルパー 31 | try: 32 | from locales import i18n 33 | HAS_I18N = True 34 | except ImportError: 35 | HAS_I18N = False 36 | print("Warning: i18n module not found, using fallback translations") 37 | 38 | # 翻訳ヘルパー関数 39 | def _(text): 40 | """国際化対応のためのヘルパー関数""" 41 | if HAS_I18N: 42 | return i18n.translate(text) 43 | return text 44 | 45 | # バージョン情報 46 | __version__ = "1.0.0" 47 | -------------------------------------------------------------------------------- /webui/lora_utils/dynamic_swap_lora.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi Dynamic Swap LoRA 2 | # 3 | # このモジュールは後方互換性のために残されていますが、 4 | # 実際にはdirect_applicationによるLoRA適用が使用されています。 5 | 6 | import os 7 | import torch 8 | import warnings 9 | 10 | # 国際化対応 11 | from locales.i18n_extended import translate as _ 12 | 13 | 14 | class DynamicSwapLoRAManager: 15 | """ 16 | この旧式のLoRA管理クラスは後方互換性のために残されていますが、 17 | 実際の処理では使用されません。代わりに直接的なLoRA適用が行われます。 18 | """ 19 | 20 | def __init__(self): 21 | """初期化""" 22 | self.is_active = False 23 | self.lora_path = None 24 | self.lora_scale = 0.8 25 | warnings.warn( 26 | _("DynamicSwapLoRAManagerは非推奨です。代わりにlora_loader.load_and_apply_lora()を使用してください。"), 27 | DeprecationWarning, 28 | stacklevel=2 29 | ) 30 | 31 | def load_lora(self, lora_path, is_diffusers=False): 32 | """ 33 | LoRAファイルをロードする (実際には、パスの記録のみ) 34 | 35 | Args: 36 | lora_path: LoRAファイルのパス 37 | is_diffusers: 互換性のために残されたパラメータ(使用されない) 38 | """ 39 | if not os.path.exists(lora_path): 40 | raise FileNotFoundError(_("LoRAファイルが見つかりません: {0}").format(lora_path)) 41 | 42 | self.lora_path = lora_path 43 | self.is_active = True 44 | 45 | print(_("LoRAファイルがロードされました (非推奨インターフェース): {0}").format(lora_path)) 46 | print(_("注意: ") + _("DynamicSwapLoRAManagerは非推奨です。代わりにlora_loader.load_and_apply_lora()を使用してください。")) 47 | 48 | def set_scale(self, scale): 49 | """ 50 | LoRA適用スケールを設定する 51 | 52 | Args: 53 | scale: LoRAの適用強度 54 | """ 55 | self.lora_scale = scale 56 | 57 | def install_hooks(self, model): 58 | """ 59 | モデルにLoRAフックをインストールする (実際には、直接適用を行う) 60 | 61 | Args: 62 | model: フックをインストールするモデル 63 | """ 64 | # 直接適用モードを使用してLoRAを適用 65 | from .lora_loader import load_and_apply_lora 66 | 67 | print(_("警告: DynamicSwapLoRAManagerは非推奨です。直接適用モードにリダイレクトします。")) 68 | 69 | load_and_apply_lora( 70 | model, 71 | self.lora_path, 72 | self.lora_scale, 73 | device=torch.device("cuda" if torch.cuda.is_available() else "cpu") 74 | ) 75 | 76 | print(_("LoRAは直接適用モードで適用されました。")) 77 | -------------------------------------------------------------------------------- /webui/lora_utils/lora_check_helper.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi LoRA Check Helper 2 | # 3 | # LoRAの適用状態確認のための機能を提供します。 4 | 5 | import torch 6 | 7 | # 国際化対応 8 | from locales.i18n_extended import translate as _ 9 | 10 | def check_lora_applied(model): 11 | """ 12 | モデルにLoRAが適用されているかをチェック 13 | 14 | Args: 15 | model: チェック対象のモデル 16 | 17 | Returns: 18 | (bool, str): LoRAが適用されているかどうかとその適用方法 19 | """ 20 | # _lora_appliedフラグのチェック 21 | has_flag = hasattr(model, '_lora_applied') and model._lora_applied 22 | 23 | if has_flag: 24 | return True, "direct_application" 25 | 26 | # モデル内の名前付きモジュールをチェックして、LoRAフックがあるかを確認 27 | has_hooks = False 28 | for name, module in model.named_modules(): 29 | if hasattr(module, '_lora_hooks'): 30 | has_hooks = True 31 | break 32 | 33 | if has_hooks: 34 | return True, "hooks" 35 | 36 | return False, "none" 37 | 38 | def analyze_lora_application(model): 39 | """ 40 | モデルのLoRA適用率と影響を詳細に分析 41 | 42 | Args: 43 | model: 分析対象のモデル 44 | 45 | Returns: 46 | dict: 分析結果の辞書 47 | """ 48 | total_params = 0 49 | lora_affected_params = 0 50 | 51 | # トータルパラメータ数とLoRAの影響を受けるパラメータ数をカウント 52 | for name, module in model.named_modules(): 53 | if hasattr(module, 'weight') and isinstance(module.weight, torch.Tensor): 54 | param_count = module.weight.numel() 55 | total_params += param_count 56 | 57 | # LoRA適用されたモジュールかチェック 58 | if hasattr(module, '_lora_hooks') or hasattr(module, '_lora_applied'): 59 | lora_affected_params += param_count 60 | 61 | # 適用率の計算 62 | application_rate = 0.0 63 | if total_params > 0: 64 | application_rate = lora_affected_params / total_params * 100.0 65 | 66 | return { 67 | "total_params": total_params, 68 | "lora_affected_params": lora_affected_params, 69 | "application_rate": application_rate, 70 | "has_lora": lora_affected_params > 0 71 | } 72 | 73 | def print_lora_status(model): 74 | """ 75 | モデルのLoRA適用状況を出力 76 | 77 | Args: 78 | model: 出力対象のモデル 79 | """ 80 | has_lora, source = check_lora_applied(model) 81 | 82 | if has_lora: 83 | print(_("LoRAステータス: {0}").format(_("適用済み"))) 84 | print(_("適用方法: {0}").format(_(source))) 85 | 86 | # 詳細な分析 87 | analysis = analyze_lora_application(model) 88 | application_rate = analysis["application_rate"] 89 | 90 | print(_("LoRA適用状況: {0}/{1} パラメータ ({2:.2f}%)").format( 91 | analysis["lora_affected_params"], 92 | analysis["total_params"], 93 | application_rate 94 | )) 95 | else: 96 | print(_("LoRAステータス: {0}").format(_("未適用"))) 97 | print(_("モデルにLoRAは適用されていません")) 98 | -------------------------------------------------------------------------------- /webui/lora_utils/lora_loader.py: -------------------------------------------------------------------------------- 1 | # FramePack-eichi LoRA Loader 2 | # 3 | # LoRAモデルの読み込みと適用のための機能を提供します。 4 | 5 | import os 6 | import torch 7 | from tqdm import tqdm 8 | from .lora_utils import merge_lora_to_state_dict 9 | 10 | # 国際化対応 11 | from locales.i18n_extended import translate as _ 12 | 13 | def load_and_apply_lora( 14 | model_files, 15 | lora_paths, 16 | lora_scales=None, 17 | fp8_enabled=False, 18 | device=None 19 | ): 20 | """ 21 | LoRA重みをロードして重みに適用する 22 | 23 | Args: 24 | model_files: モデルファイルのリスト 25 | lora_paths: LoRAファイルのパスのリスト 26 | lora_scales: LoRAの適用強度のリスト 27 | fp8_enabled: FP8最適化の有効/無効 28 | device: 計算に使用するデバイス 29 | 30 | Returns: 31 | LoRAが適用されたモデルの状態辞書 32 | """ 33 | if lora_paths is None: 34 | lora_paths = [] 35 | for lora_path in lora_paths: 36 | if not os.path.exists(lora_path): 37 | raise FileNotFoundError(_("LoRAファイルが見つかりません: {0}").format(lora_path)) 38 | 39 | if lora_scales is None: 40 | lora_scales = [0.8] * len(lora_paths) 41 | if len(lora_scales)> len(lora_paths): 42 | lora_scales = lora_scales[:len(lora_paths)] 43 | if len(lora_scales) < len(lora_paths): 44 | lora_scales += [0.8] * (len(lora_paths) - len(lora_scales)) 45 | 46 | if device is None: 47 | device = torch.device("cpu") # CPUに fall back 48 | 49 | for lora_path, lora_scale in zip(lora_paths, lora_scales): 50 | print(_("LoRAを読み込み中: {0} (スケール: {1})").format(os.path.basename(lora_path), lora_scale)) 51 | 52 | # LoRA重みを状態辞書にマージ 53 | print(_("フォーマット: HunyuanVideo")) 54 | 55 | # LoRAをマージ 56 | merged_state_dict = merge_lora_to_state_dict(model_files, lora_paths, lora_scales, fp8_enabled, device) 57 | 58 | # # LoRAが適用されたことを示すフラグを設定 59 | # model._lora_applied = True 60 | 61 | print(_("LoRAの適用が完了しました")) 62 | return merged_state_dict 63 | 64 | def check_lora_applied(model): 65 | """ 66 | モデルにLoRAが適用されているかをチェック 67 | 68 | Args: 69 | model: チェック対象のモデル 70 | 71 | Returns: 72 | (bool, str): LoRAが適用されているかどうかとその適用方法 73 | """ 74 | # _lora_appliedフラグのチェック 75 | has_flag = hasattr(model, '_lora_applied') and model._lora_applied 76 | 77 | if has_flag: 78 | return True, "direct_application" 79 | 80 | return False, "none" 81 | -------------------------------------------------------------------------------- /webui/lora_utils/safetensors_utils.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | import json 3 | import struct 4 | import torch 5 | 6 | class MemoryEfficientSafeOpen: 7 | """ 8 | A class to read tensors from a .safetensors file in a memory-efficient way. 9 | """ 10 | # does not support metadata loading 11 | def __init__(self, filename): 12 | self.filename = filename 13 | self.file = open(filename, "rb") 14 | self.header, self.header_size = self._read_header() 15 | 16 | def __enter__(self): 17 | return self 18 | 19 | def __exit__(self, exc_type, exc_val, exc_tb): 20 | self.file.close() 21 | 22 | def keys(self): 23 | return [k for k in self.header.keys() if k != "__metadata__"] 24 | 25 | def metadata(self) -> Dict[str, str]: 26 | return self.header.get("__metadata__", {}) 27 | 28 | def get_tensor(self, key): 29 | if key not in self.header: 30 | raise KeyError(f"Tensor '{key}' not found in the file") 31 | 32 | metadata = self.header[key] 33 | offset_start, offset_end = metadata["data_offsets"] 34 | 35 | if offset_start == offset_end: 36 | tensor_bytes = None 37 | else: 38 | # adjust offset by header size 39 | self.file.seek(self.header_size + 8 + offset_start) 40 | tensor_bytes = self.file.read(offset_end - offset_start) 41 | 42 | return self._deserialize_tensor(tensor_bytes, metadata) 43 | 44 | def _read_header(self): 45 | header_size = struct.unpack("