Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -7,6 +7,7 @@ import requests
|
|
7 |
from requests.adapters import HTTPAdapter
|
8 |
from urllib3.util.retry import Retry
|
9 |
import json
|
|
|
10 |
|
11 |
os.environ['HF_HOME'] = os.path.abspath(os.path.realpath(os.path.join(os.path.dirname(__file__), './hf_download')))
|
12 |
|
@@ -109,6 +110,76 @@ else:
|
|
109 |
models = {}
|
110 |
cpu_fallback_mode = not GPU_AVAILABLE # GPUが利用できない場合、CPU代替モードを使用
|
111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
# モデルロード関数を使用
|
113 |
def load_models():
|
114 |
global models, cpu_fallback_mode, GPU_INITIALIZED
|
@@ -419,6 +490,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
419 |
|
420 |
job_id = generate_timestamp()
|
421 |
last_output_filename = None
|
|
|
422 |
history_pixels = None
|
423 |
history_latents = None
|
424 |
total_generated_latent_frames = 0
|
@@ -588,7 +660,14 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
588 |
try:
|
589 |
output_filename = os.path.join(outputs_folder, f'{job_id}_final_{total_generated_latent_frames}.mp4')
|
590 |
save_bcthw_as_mp4(history_pixels, output_filename, fps=30, crf=18)
|
591 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
592 |
except Exception as e:
|
593 |
print(f"最終動画保存中にエラーが発生しました: {e}")
|
594 |
|
@@ -611,7 +690,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
611 |
traceback.print_exc()
|
612 |
# 完全に終了せずに次のイテレーションを試みる
|
613 |
if last_output_filename:
|
614 |
-
stream.output_queue.push(('file', last_output_filename))
|
615 |
continue
|
616 |
|
617 |
if not high_vram and not cpu_fallback_mode:
|
@@ -724,7 +803,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
724 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
725 |
if last_output_filename:
|
726 |
print(f"【デバッグ】部分的に生成された動画あり: {last_output_filename}、この動画を返します")
|
727 |
-
stream.output_queue.push(('file', last_output_filename))
|
728 |
error_msg = "ユーザーにより生成プロセスが中断されましたが、部分的な動画は生成されています"
|
729 |
else:
|
730 |
print("【デバッグ】部分的に生成された動画なし、中断メッセージを返します")
|
@@ -742,7 +821,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
742 |
|
743 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
744 |
if last_output_filename:
|
745 |
-
stream.output_queue.push(('file', last_output_filename))
|
746 |
|
747 |
# エラーメッセージを作成
|
748 |
error_msg = f"サンプリングプロセス中にエラーが発生しましたが、部分的に生成された動画を返します: {e}"
|
@@ -767,7 +846,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
767 |
traceback.print_exc()
|
768 |
|
769 |
if last_output_filename:
|
770 |
-
stream.output_queue.push(('file', last_output_filename))
|
771 |
stream.output_queue.push(('error', error_msg))
|
772 |
stream.output_queue.push(('end', None))
|
773 |
return
|
@@ -787,7 +866,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
787 |
print(error_msg)
|
788 |
|
789 |
if last_output_filename:
|
790 |
-
stream.output_queue.push(('file', last_output_filename))
|
791 |
continue
|
792 |
|
793 |
try:
|
@@ -816,18 +895,24 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
816 |
save_start_time = time.time()
|
817 |
save_bcthw_as_mp4(history_pixels, output_filename, fps=30, crf=18)
|
818 |
print(f"動画保存完了、所要時間: {time.time() - save_start_time:.2f}秒")
|
819 |
-
|
|
|
|
|
|
|
|
|
820 |
print(f'デコード完了。現在の潜在変数形状 {real_history_latents.shape}; ピクセル形状 {history_pixels.shape}')
|
821 |
|
822 |
last_output_filename = output_filename
|
823 |
-
|
|
|
|
|
824 |
except Exception as e:
|
825 |
print(f"動画のデコードまたは保存中にエラーが発生しました: {e}")
|
826 |
traceback.print_exc()
|
827 |
|
828 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
829 |
if last_output_filename:
|
830 |
-
stream.output_queue.push(('file', last_output_filename))
|
831 |
|
832 |
# エラー情報を記録
|
833 |
error_msg = f"動画のデコードまたは保存中にエラーが発生しました: {e}"
|
@@ -861,7 +946,7 @@ def worker(input_image, prompt, n_prompt, seed, total_second_length, latent_wind
|
|
861 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
862 |
if last_output_filename:
|
863 |
print(f"【デバッグ】外部例外処理: 生成済み部分動画を返す {last_output_filename}")
|
864 |
-
stream.output_queue.push(('file', last_output_filename))
|
865 |
else:
|
866 |
print("【デバッグ】外部例外処理: 生成済み動画が見つかりません")
|
867 |
|
@@ -891,8 +976,8 @@ if IN_HF_SPACE and 'spaces' in globals():
|
|
891 |
gpu_memory_preservation = 6
|
892 |
|
893 |
|
894 |
-
# UI状態の初期化
|
895 |
-
yield None, None, '', '', gr.update(interactive=False), gr.update(interactive=True)
|
896 |
|
897 |
try:
|
898 |
stream = AsyncStream()
|
@@ -901,7 +986,9 @@ if IN_HF_SPACE and 'spaces' in globals():
|
|
901 |
async_run(worker, input_image, prompt, n_prompt, seed, total_second_length, latent_window_size, steps, cfg, gs, rs, gpu_memory_preservation, use_teacache)
|
902 |
|
903 |
output_filename = None
|
|
|
904 |
prev_output_filename = None
|
|
|
905 |
error_message = None
|
906 |
|
907 |
# ワーカーの出力を継続的にチェック
|
@@ -910,15 +997,17 @@ if IN_HF_SPACE and 'spaces' in globals():
|
|
910 |
flag, data = stream.output_queue.next()
|
911 |
|
912 |
if flag == 'file':
|
913 |
-
|
|
|
914 |
prev_output_filename = output_filename
|
915 |
-
|
916 |
-
|
|
|
917 |
|
918 |
if flag == 'progress':
|
919 |
preview, desc, html = data
|
920 |
# 進捗更新時にエラーメッセージを変更せず、停止ボタンがインタラクティブであることを確認
|
921 |
-
yield gr.update(), gr.update(visible=True, value=preview), desc, html, gr.update(interactive=False), gr.update(interactive=True)
|
922 |
|
923 |
if flag == 'error':
|
924 |
error_message = data
|
@@ -926,16 +1015,17 @@ if IN_HF_SPACE and 'spaces' in globals():
|
|
926 |
# 即時表示せず、end信号を待機
|
927 |
|
928 |
if flag == 'end':
|
929 |
-
#
|
930 |
if output_filename is None and prev_output_filename is not None:
|
931 |
output_filename = prev_output_filename
|
|
|
932 |
|
933 |
# エラーメッセージがある場合、わかりやすいエラー表示を作成
|
934 |
if error_message:
|
935 |
-
yield output_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
936 |
else:
|
937 |
# 成功時にエラー表示をしない
|
938 |
-
yield output_filename, gr.update(visible=False), gr.update(), '', gr.update(interactive=True), gr.update(interactive=False)
|
939 |
break
|
940 |
except Exception as e:
|
941 |
print(f"出力処理中にエラーが発生しました: {e}")
|
@@ -944,11 +1034,11 @@ if IN_HF_SPACE and 'spaces' in globals():
|
|
944 |
if current_time - last_update_time > 60: # 60秒間更新がない場合、処理がフリーズした可能性
|
945 |
print(f"処理がフリーズした可能性があります。{current_time - last_update_time:.1f}秒間更新がありません")
|
946 |
|
947 |
-
#
|
948 |
if prev_output_filename:
|
949 |
-
yield prev_output_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
950 |
else:
|
951 |
-
yield None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
952 |
break
|
953 |
|
954 |
except Exception as e:
|
@@ -956,7 +1046,7 @@ if IN_HF_SPACE and 'spaces' in globals():
|
|
956 |
traceback.print_exc()
|
957 |
error_msg = str(e)
|
958 |
|
959 |
-
yield None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
960 |
|
961 |
process = process_with_gpu
|
962 |
else:
|
@@ -971,8 +1061,8 @@ else:
|
|
971 |
rs = 0.0
|
972 |
gpu_memory_preservation = 6
|
973 |
|
974 |
-
# UI状態の初期化
|
975 |
-
yield None, None, '', '', gr.update(interactive=False), gr.update(interactive=True)
|
976 |
|
977 |
try:
|
978 |
stream = AsyncStream()
|
@@ -981,7 +1071,9 @@ else:
|
|
981 |
async_run(worker, input_image, prompt, n_prompt, seed, total_second_length, latent_window_size, steps, cfg, gs, rs, gpu_memory_preservation, use_teacache)
|
982 |
|
983 |
output_filename = None
|
|
|
984 |
prev_output_filename = None
|
|
|
985 |
error_message = None
|
986 |
|
987 |
# ワーカーの出力を継続的にチェック
|
@@ -990,15 +1082,17 @@ else:
|
|
990 |
flag, data = stream.output_queue.next()
|
991 |
|
992 |
if flag == 'file':
|
993 |
-
|
|
|
994 |
prev_output_filename = output_filename
|
995 |
-
|
996 |
-
|
|
|
997 |
|
998 |
if flag == 'progress':
|
999 |
preview, desc, html = data
|
1000 |
-
#
|
1001 |
-
yield gr.update(), gr.update(visible=True, value=preview), desc, html, gr.update(interactive=False), gr.update(interactive=True)
|
1002 |
|
1003 |
if flag == 'error':
|
1004 |
error_message = data
|
@@ -1006,16 +1100,17 @@ else:
|
|
1006 |
# 即時表示せず、end信号を待機
|
1007 |
|
1008 |
if flag == 'end':
|
1009 |
-
#
|
1010 |
if output_filename is None and prev_output_filename is not None:
|
1011 |
output_filename = prev_output_filename
|
|
|
1012 |
|
1013 |
# エラーメッセージがある場合、わかりやすいエラー表示を作成
|
1014 |
if error_message:
|
1015 |
-
yield output_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1016 |
else:
|
1017 |
# 成功時にエラー表示をしない
|
1018 |
-
yield output_filename, gr.update(visible=False), gr.update(), '', gr.update(interactive=True), gr.update(interactive=False)
|
1019 |
break
|
1020 |
except Exception as e:
|
1021 |
print(f"出力処理中にエラーが発生しました: {e}")
|
@@ -1024,11 +1119,11 @@ else:
|
|
1024 |
if current_time - last_update_time > 60: # 60秒間更新がない場合、処理がフリーズした可能性
|
1025 |
print(f"処理がフリーズした可能性があります。{current_time - last_update_time:.1f}秒間更新がありません")
|
1026 |
|
1027 |
-
#
|
1028 |
if prev_output_filename:
|
1029 |
-
yield prev_output_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1030 |
else:
|
1031 |
-
yield None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1032 |
break
|
1033 |
|
1034 |
except Exception as e:
|
@@ -1036,7 +1131,7 @@ else:
|
|
1036 |
traceback.print_exc()
|
1037 |
error_msg = str(e)
|
1038 |
|
1039 |
-
yield None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1040 |
|
1041 |
|
1042 |
def end_process():
|
@@ -1219,6 +1314,32 @@ def make_custom_css():
|
|
1219 |
.error {
|
1220 |
display: none !important;
|
1221 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1222 |
"""
|
1223 |
|
1224 |
# CSSを結合
|
@@ -1285,16 +1406,16 @@ with block:
|
|
1285 |
|
1286 |
seed = gr.Number(
|
1287 |
label="シード値 / Seed",
|
1288 |
-
value=
|
1289 |
precision=0
|
1290 |
)
|
1291 |
|
1292 |
# タッチ操作を最適化するためにslider-containerクラスを追加
|
1293 |
with gr.Group(elem_classes="slider-container"):
|
1294 |
total_second_length = gr.Slider(
|
1295 |
-
label="動画の長さ(最大3秒) / Video Length (max
|
1296 |
-
minimum=0
|
1297 |
-
maximum=
|
1298 |
value=1,
|
1299 |
step=0.1
|
1300 |
)
|
@@ -1309,16 +1430,27 @@ with block:
|
|
1309 |
elem_classes="preview-container"
|
1310 |
)
|
1311 |
|
1312 |
-
#
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1322 |
|
1323 |
gr.HTML("<div ='sampling_note' class='note'>注意:逆順サンプリングのため、終了動作が開始動作より先に生成されます。開始動作が動画に表示されていない場合は、しばらくお待ちください。後で生成されます。</div>")
|
1324 |
gr.HTML("<div ='sampling_note' class='note'>Note that the ending actions will be generated before the starting actions due to the inverted sampling. If the starting action is not in the video, you just need to wait, and it will be generated later.</div>")
|
@@ -1328,16 +1460,16 @@ with block:
|
|
1328 |
progress_desc = gr.Markdown('', elem_classes='no-generating-animation')
|
1329 |
progress_bar = gr.HTML('', elem_classes='no-generating-animation')
|
1330 |
|
1331 |
-
# エラーメッセージエリア
|
1332 |
error_message = gr.HTML('', elem_id='error-message', visible=True)
|
1333 |
|
1334 |
-
#
|
1335 |
ips = [input_image, prompt, n_prompt, seed, total_second_length, use_teacache]
|
|
|
1336 |
|
1337 |
# 開始と終了ボタンのイベント
|
1338 |
-
start_button.click(fn=process, inputs=ips, outputs=
|
1339 |
end_button.click(fn=end_process)
|
1340 |
|
1341 |
|
1342 |
-
block.launch()
|
1343 |
-
|
|
|
7 |
from requests.adapters import HTTPAdapter
|
8 |
from urllib3.util.retry import Retry
|
9 |
import json
|
10 |
+
import subprocess # FFmpeg実行用に追加
|
11 |
|
12 |
os.environ['HF_HOME'] = os.path.abspath(os.path.realpath(os.path.join(os.path.dirname(__file__), './hf_download')))
|
13 |
|
|
|
110 |
models = {}
|
111 |
cpu_fallback_mode = not GPU_AVAILABLE # GPUが利用できない場合、CPU代替モードを使用
|
112 |
|
113 |
+
# 最後のフレームを抽出する関数を追加
|
114 |
+
def extract_last_frame(video_path, output_path):
|
115 |
+
"""
|
116 |
+
FFmpegを使用して動画の最後のフレームを抽出します
|
117 |
+
|
118 |
+
Args:
|
119 |
+
video_path (str): 対象動画のパス
|
120 |
+
output_path (str): 出力画像のパス
|
121 |
+
|
122 |
+
Returns:
|
123 |
+
bool: 抽出に成功した場合はTrue、失敗した場合はFalse
|
124 |
+
"""
|
125 |
+
try:
|
126 |
+
# 動画からフレーム数を取得するコマンド
|
127 |
+
probe_cmd = [
|
128 |
+
'ffprobe',
|
129 |
+
'-v', 'error',
|
130 |
+
'-select_streams', 'v:0',
|
131 |
+
'-count_packets',
|
132 |
+
'-show_entries', 'stream=nb_read_packets',
|
133 |
+
'-of', 'csv=p=0',
|
134 |
+
video_path
|
135 |
+
]
|
136 |
+
|
137 |
+
# コマンドを実行してフレーム数を取得
|
138 |
+
frame_count = int(subprocess.check_output(probe_cmd).decode('utf-8').strip())
|
139 |
+
|
140 |
+
# 最後のフレームを抽出するコマンド(0から始まるので-1する)
|
141 |
+
extract_cmd = [
|
142 |
+
'ffmpeg',
|
143 |
+
'-y', # 既存ファイルを上書き
|
144 |
+
'-i', video_path,
|
145 |
+
'-vf', f'select=eq(n\\,{frame_count-1})',
|
146 |
+
'-vframes', '1',
|
147 |
+
output_path
|
148 |
+
]
|
149 |
+
|
150 |
+
# コマンドを実行
|
151 |
+
subprocess.run(extract_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
152 |
+
|
153 |
+
# ファイルが生成されたか確認
|
154 |
+
if os.path.exists(output_path):
|
155 |
+
print(f"最後のフレームを抽出しました: {output_path}")
|
156 |
+
return True
|
157 |
+
else:
|
158 |
+
print(f"最後のフレーム抽出に失敗しました")
|
159 |
+
return False
|
160 |
+
except Exception as e:
|
161 |
+
print(f"フレーム抽出中にエラーが発生しました: {e}")
|
162 |
+
# 別の方法を試す(単純にシーク)
|
163 |
+
try:
|
164 |
+
simple_extract_cmd = [
|
165 |
+
'ffmpeg',
|
166 |
+
'-y',
|
167 |
+
'-sseof', '-1', # 最後のフレームにシーク
|
168 |
+
'-i', video_path,
|
169 |
+
'-update', '1',
|
170 |
+
'-q:v', '1',
|
171 |
+
output_path
|
172 |
+
]
|
173 |
+
subprocess.run(simple_extract_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
174 |
+
|
175 |
+
if os.path.exists(output_path):
|
176 |
+
print(f"代替方法で最後のフレームを抽出しました: {output_path}")
|
177 |
+
return True
|
178 |
+
except Exception as e2:
|
179 |
+
print(f"代替フレーム抽出方法でもエラーが発生しました: {e2}")
|
180 |
+
|
181 |
+
return False
|
182 |
+
|
183 |
# モデルロード関数を使用
|
184 |
def load_models():
|
185 |
global models, cpu_fallback_mode, GPU_INITIALIZED
|
|
|
490 |
|
491 |
job_id = generate_timestamp()
|
492 |
last_output_filename = None
|
493 |
+
last_frame_filename = None
|
494 |
history_pixels = None
|
495 |
history_latents = None
|
496 |
total_generated_latent_frames = 0
|
|
|
660 |
try:
|
661 |
output_filename = os.path.join(outputs_folder, f'{job_id}_final_{total_generated_latent_frames}.mp4')
|
662 |
save_bcthw_as_mp4(history_pixels, output_filename, fps=30, crf=18)
|
663 |
+
|
664 |
+
# 最後のフレームを抽出して保存
|
665 |
+
last_frame_filename = os.path.join(outputs_folder, f'{job_id}_final_{total_generated_latent_frames}_last_frame.png')
|
666 |
+
extract_success = extract_last_frame(output_filename, last_frame_filename)
|
667 |
+
|
668 |
+
# 抽出成功したかどうかに応じたファイル名を送信
|
669 |
+
last_frame_path = last_frame_filename if extract_success else None
|
670 |
+
stream.output_queue.push(('file', (output_filename, last_frame_path)))
|
671 |
except Exception as e:
|
672 |
print(f"最終動画保存中にエラーが発生しました: {e}")
|
673 |
|
|
|
690 |
traceback.print_exc()
|
691 |
# 完全に終了せずに次のイテレーションを試みる
|
692 |
if last_output_filename:
|
693 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
694 |
continue
|
695 |
|
696 |
if not high_vram and not cpu_fallback_mode:
|
|
|
803 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
804 |
if last_output_filename:
|
805 |
print(f"【デバッグ】部分的に生成された動画あり: {last_output_filename}、この動画を返します")
|
806 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
807 |
error_msg = "ユーザーにより生成プロセスが中断されましたが、部分的な動画は生成されています"
|
808 |
else:
|
809 |
print("【デバッグ】部分的に生成された動画なし、中断メッセージを返します")
|
|
|
821 |
|
822 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
823 |
if last_output_filename:
|
824 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
825 |
|
826 |
# エラーメッセージを作成
|
827 |
error_msg = f"サンプリングプロセス中にエラーが発生しましたが、部分的に生成された動画を返します: {e}"
|
|
|
846 |
traceback.print_exc()
|
847 |
|
848 |
if last_output_filename:
|
849 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
850 |
stream.output_queue.push(('error', error_msg))
|
851 |
stream.output_queue.push(('end', None))
|
852 |
return
|
|
|
866 |
print(error_msg)
|
867 |
|
868 |
if last_output_filename:
|
869 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
870 |
continue
|
871 |
|
872 |
try:
|
|
|
895 |
save_start_time = time.time()
|
896 |
save_bcthw_as_mp4(history_pixels, output_filename, fps=30, crf=18)
|
897 |
print(f"動画保存完了、所要時間: {time.time() - save_start_time:.2f}秒")
|
898 |
+
|
899 |
+
# 最後のフレームを抽出して保存
|
900 |
+
last_frame_filename = os.path.join(outputs_folder, f'{job_id}_{total_generated_latent_frames}_last_frame.png')
|
901 |
+
extract_success = extract_last_frame(output_filename, last_frame_filename)
|
902 |
+
|
903 |
print(f'デコード完了。現在の潜在変数形状 {real_history_latents.shape}; ピクセル形状 {history_pixels.shape}')
|
904 |
|
905 |
last_output_filename = output_filename
|
906 |
+
# 抽出成功したらファイル名も送信、失敗した場合はNoneを送信
|
907 |
+
last_frame_path = last_frame_filename if extract_success else None
|
908 |
+
stream.output_queue.push(('file', (output_filename, last_frame_path)))
|
909 |
except Exception as e:
|
910 |
print(f"動画のデコードまたは保存中にエラーが発生しました: {e}")
|
911 |
traceback.print_exc()
|
912 |
|
913 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
914 |
if last_output_filename:
|
915 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
916 |
|
917 |
# エラー情報を記録
|
918 |
error_msg = f"動画のデコードまたは保存中にエラーが発生しました: {e}"
|
|
|
946 |
# 既に生成された動画がある場合、最後に生成された動画を返す
|
947 |
if last_output_filename:
|
948 |
print(f"【デバッグ】外部例外処理: 生成済み部分動画を返す {last_output_filename}")
|
949 |
+
stream.output_queue.push(('file', (last_output_filename, last_frame_filename)))
|
950 |
else:
|
951 |
print("【デバッグ】外部例外処理: 生成済み動画が見つかりません")
|
952 |
|
|
|
976 |
gpu_memory_preservation = 6
|
977 |
|
978 |
|
979 |
+
# UI状態の初期化 - last_frame_image用の更新を追加
|
980 |
+
yield None, None, None, '', '', gr.update(interactive=False), gr.update(interactive=True)
|
981 |
|
982 |
try:
|
983 |
stream = AsyncStream()
|
|
|
986 |
async_run(worker, input_image, prompt, n_prompt, seed, total_second_length, latent_window_size, steps, cfg, gs, rs, gpu_memory_preservation, use_teacache)
|
987 |
|
988 |
output_filename = None
|
989 |
+
last_frame_filename = None
|
990 |
prev_output_filename = None
|
991 |
+
prev_last_frame_filename = None
|
992 |
error_message = None
|
993 |
|
994 |
# ワーカーの出力を継続的にチェック
|
|
|
997 |
flag, data = stream.output_queue.next()
|
998 |
|
999 |
if flag == 'file':
|
1000 |
+
# data形式が(動画パス, 最後のフレームパス)に変更
|
1001 |
+
output_filename, last_frame_filename = data
|
1002 |
prev_output_filename = output_filename
|
1003 |
+
prev_last_frame_filename = last_frame_filename
|
1004 |
+
# ファイル成功時に出力を更新
|
1005 |
+
yield output_filename, last_frame_filename, gr.update(), gr.update(), '', gr.update(interactive=False), gr.update(interactive=True)
|
1006 |
|
1007 |
if flag == 'progress':
|
1008 |
preview, desc, html = data
|
1009 |
# 進捗更新時にエラーメッセージを変更せず、停止ボタンがインタラクティブであることを確認
|
1010 |
+
yield gr.update(), gr.update(), gr.update(visible=True, value=preview), desc, html, gr.update(interactive=False), gr.update(interactive=True)
|
1011 |
|
1012 |
if flag == 'error':
|
1013 |
error_message = data
|
|
|
1015 |
# 即時表示せず、end信号を待機
|
1016 |
|
1017 |
if flag == 'end':
|
1018 |
+
# 最後の動画ファイルと最後のフレーム画像がある場合、確実に返す
|
1019 |
if output_filename is None and prev_output_filename is not None:
|
1020 |
output_filename = prev_output_filename
|
1021 |
+
last_frame_filename = prev_last_frame_filename
|
1022 |
|
1023 |
# エラーメッセージがある場合、わかりやすいエラー表示を作成
|
1024 |
if error_message:
|
1025 |
+
yield output_filename, last_frame_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1026 |
else:
|
1027 |
# 成功時にエラー表示をしない
|
1028 |
+
yield output_filename, last_frame_filename, gr.update(visible=False), gr.update(), '', gr.update(interactive=True), gr.update(interactive=False)
|
1029 |
break
|
1030 |
except Exception as e:
|
1031 |
print(f"出力処理中にエラーが発生しました: {e}")
|
|
|
1034 |
if current_time - last_update_time > 60: # 60秒間更新がない場合、処理がフリーズした可能性
|
1035 |
print(f"処理がフリーズした可能性があります。{current_time - last_update_time:.1f}秒間更新がありません")
|
1036 |
|
1037 |
+
# 部分的に生成された動画と最後のフレーム画像がある場合、それを返す
|
1038 |
if prev_output_filename:
|
1039 |
+
yield prev_output_filename, prev_last_frame_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1040 |
else:
|
1041 |
+
yield None, None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1042 |
break
|
1043 |
|
1044 |
except Exception as e:
|
|
|
1046 |
traceback.print_exc()
|
1047 |
error_msg = str(e)
|
1048 |
|
1049 |
+
yield None, None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1050 |
|
1051 |
process = process_with_gpu
|
1052 |
else:
|
|
|
1061 |
rs = 0.0
|
1062 |
gpu_memory_preservation = 6
|
1063 |
|
1064 |
+
# UI状態の初期化 - last_frame_image用の更新を追加
|
1065 |
+
yield None, None, None, '', '', gr.update(interactive=False), gr.update(interactive=True)
|
1066 |
|
1067 |
try:
|
1068 |
stream = AsyncStream()
|
|
|
1071 |
async_run(worker, input_image, prompt, n_prompt, seed, total_second_length, latent_window_size, steps, cfg, gs, rs, gpu_memory_preservation, use_teacache)
|
1072 |
|
1073 |
output_filename = None
|
1074 |
+
last_frame_filename = None
|
1075 |
prev_output_filename = None
|
1076 |
+
prev_last_frame_filename = None
|
1077 |
error_message = None
|
1078 |
|
1079 |
# ワーカーの出力を継続的にチェック
|
|
|
1082 |
flag, data = stream.output_queue.next()
|
1083 |
|
1084 |
if flag == 'file':
|
1085 |
+
# data形式が(動画パス, 最後のフレームパス)に変更
|
1086 |
+
output_filename, last_frame_filename = data
|
1087 |
prev_output_filename = output_filename
|
1088 |
+
prev_last_frame_filename = last_frame_filename
|
1089 |
+
# ファイル成功時の出力更新
|
1090 |
+
yield output_filename, last_frame_filename, gr.update(), gr.update(), '', gr.update(interactive=False), gr.update(interactive=True)
|
1091 |
|
1092 |
if flag == 'progress':
|
1093 |
preview, desc, html = data
|
1094 |
+
# 進捗更新時
|
1095 |
+
yield gr.update(), gr.update(), gr.update(visible=True, value=preview), desc, html, gr.update(interactive=False), gr.update(interactive=True)
|
1096 |
|
1097 |
if flag == 'error':
|
1098 |
error_message = data
|
|
|
1100 |
# 即時表示せず、end信号を待機
|
1101 |
|
1102 |
if flag == 'end':
|
1103 |
+
# 最後の動画ファイルと最後のフレーム画像がある場合、確実に返す
|
1104 |
if output_filename is None and prev_output_filename is not None:
|
1105 |
output_filename = prev_output_filename
|
1106 |
+
last_frame_filename = prev_last_frame_filename
|
1107 |
|
1108 |
# エラーメッセージがある場合、わかりやすいエラー表示を作成
|
1109 |
if error_message:
|
1110 |
+
yield output_filename, last_frame_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1111 |
else:
|
1112 |
# 成功時にエラー表示をしない
|
1113 |
+
yield output_filename, last_frame_filename, gr.update(visible=False), gr.update(), '', gr.update(interactive=True), gr.update(interactive=False)
|
1114 |
break
|
1115 |
except Exception as e:
|
1116 |
print(f"出力処理中にエラーが発生しました: {e}")
|
|
|
1119 |
if current_time - last_update_time > 60: # 60秒間更新がない場合、処理がフリーズした可能性
|
1120 |
print(f"処理がフリーズした可能性があります。{current_time - last_update_time:.1f}秒間更新がありません")
|
1121 |
|
1122 |
+
# 部分的に生成された動画と最後のフレーム画像がある場合、それを返す
|
1123 |
if prev_output_filename:
|
1124 |
+
yield prev_output_filename, prev_last_frame_filename, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1125 |
else:
|
1126 |
+
yield None, None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1127 |
break
|
1128 |
|
1129 |
except Exception as e:
|
|
|
1131 |
traceback.print_exc()
|
1132 |
error_msg = str(e)
|
1133 |
|
1134 |
+
yield None, None, gr.update(visible=False), gr.update(), gr.update(interactive=True), gr.update(interactive=False)
|
1135 |
|
1136 |
|
1137 |
def end_process():
|
|
|
1314 |
.error {
|
1315 |
display: none !important;
|
1316 |
}
|
1317 |
+
|
1318 |
+
/* 最後のフレーム表示エリアのスタイル */
|
1319 |
+
.last-frame-container {
|
1320 |
+
margin-top: 15px;
|
1321 |
+
border: 1px solid #ddd;
|
1322 |
+
border-radius: 8px;
|
1323 |
+
overflow: hidden;
|
1324 |
+
}
|
1325 |
+
|
1326 |
+
/* 最後のフレームとビデオの並べ表示(大画面用) */
|
1327 |
+
@media (min-width: 1024px) {
|
1328 |
+
.media-container {
|
1329 |
+
display: flex;
|
1330 |
+
flex-direction: row;
|
1331 |
+
gap: 15px;
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
.video-container {
|
1335 |
+
flex: 2;
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
.last-frame-container {
|
1339 |
+
flex: 1;
|
1340 |
+
max-width: 320px;
|
1341 |
+
}
|
1342 |
+
}
|
1343 |
"""
|
1344 |
|
1345 |
# CSSを結合
|
|
|
1406 |
|
1407 |
seed = gr.Number(
|
1408 |
label="シード値 / Seed",
|
1409 |
+
value=1234,
|
1410 |
precision=0
|
1411 |
)
|
1412 |
|
1413 |
# タッチ操作を最適化するためにslider-containerクラスを追加
|
1414 |
with gr.Group(elem_classes="slider-container"):
|
1415 |
total_second_length = gr.Slider(
|
1416 |
+
label="動画の長さ(最大3秒) / Video Length (max 1.5 seconds)",
|
1417 |
+
minimum=1.0,
|
1418 |
+
maximum=1.5,
|
1419 |
value=1,
|
1420 |
step=0.1
|
1421 |
)
|
|
|
1430 |
elem_classes="preview-container"
|
1431 |
)
|
1432 |
|
1433 |
+
# media-containerクラスを追加して動画と最後のフレームを並べて表示(大画面用)
|
1434 |
+
with gr.Group(elem_classes="media-container"):
|
1435 |
+
# 動画結果コンテナ
|
1436 |
+
result_video = gr.Video(
|
1437 |
+
label="生成された動画 / Generated Video",
|
1438 |
+
autoplay=True,
|
1439 |
+
show_share_button=True,
|
1440 |
+
height=512,
|
1441 |
+
loop=True,
|
1442 |
+
elem_classes="video-container",
|
1443 |
+
elem_id="result-video"
|
1444 |
+
)
|
1445 |
+
|
1446 |
+
# 新規追加:最後のフレーム表示用のImage要素
|
1447 |
+
last_frame_image = gr.Image(
|
1448 |
+
label="最後のフレーム / Last Frame",
|
1449 |
+
height=320,
|
1450 |
+
show_share_button=True,
|
1451 |
+
elem_classes="last-frame-container",
|
1452 |
+
elem_id="last-frame-image"
|
1453 |
+
)
|
1454 |
|
1455 |
gr.HTML("<div ='sampling_note' class='note'>注意:逆順サンプリングのため、終了動作が開始動作より先に生成されます。開始動作が動画に表示されていない場合は、しばらくお待ちください。後で生成されます。</div>")
|
1456 |
gr.HTML("<div ='sampling_note' class='note'>Note that the ending actions will be generated before the starting actions due to the inverted sampling. If the starting action is not in the video, you just need to wait, and it will be generated later.</div>")
|
|
|
1460 |
progress_desc = gr.Markdown('', elem_classes='no-generating-animation')
|
1461 |
progress_bar = gr.HTML('', elem_classes='no-generating-animation')
|
1462 |
|
1463 |
+
# エラーメッセージエリア
|
1464 |
error_message = gr.HTML('', elem_id='error-message', visible=True)
|
1465 |
|
1466 |
+
# 処理関数の入出力を更新
|
1467 |
ips = [input_image, prompt, n_prompt, seed, total_second_length, use_teacache]
|
1468 |
+
ops = [result_video, last_frame_image, preview_image, progress_desc, progress_bar, start_button, end_button]
|
1469 |
|
1470 |
# 開始と終了ボタンのイベント
|
1471 |
+
start_button.click(fn=process, inputs=ips, outputs=ops)
|
1472 |
end_button.click(fn=end_process)
|
1473 |
|
1474 |
|
1475 |
+
block.launch()
|
|