lisonallen commited on
Commit
2a8ac85
·
1 Parent(s): 6dcbefe

修复进度条更新和视频生成过程中的异常处理

Browse files
Files changed (1) hide show
  1. app.py +695 -43
app.py CHANGED
@@ -1,9 +1,99 @@
1
  from diffusers_helper.hf_login import login
2
 
3
  import os
 
 
 
 
 
 
4
 
5
  os.environ['HF_HOME'] = os.path.abspath(os.path.realpath(os.path.join(os.path.dirname(__file__), './hf_download')))
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import gradio as gr
8
  import torch
9
  import traceback
@@ -497,17 +587,107 @@ if IN_HF_SPACE and 'spaces' in globals():
497
  break
498
  except Exception as e:
499
  print(f"处理输出时出错: {e}")
500
- # 如果有最后的视频文件,确保返回
501
- if prev_output_filename is not None:
502
- yield prev_output_filename, gr.update(visible=False), gr.update(), f'处理过程中出现错误,但已生成部分视频', gr.update(interactive=True), gr.update(interactive=False)
503
- else:
504
- yield None, gr.update(visible=False), gr.update(), f'处理过程中出现错误: {str(e)}', gr.update(interactive=True), gr.update(interactive=False)
505
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
 
507
  except Exception as e:
508
  print(f"启动处理时出错: {e}")
509
  traceback.print_exc()
510
- yield None, gr.update(), gr.update(), f'启动处理时出错: {str(e)}', gr.update(interactive=True), gr.update(interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
 
512
  process = process_with_gpu
513
  else:
@@ -550,17 +730,81 @@ else:
550
  break
551
  except Exception as e:
552
  print(f"处理输出时出错: {e}")
553
- # 如果有最后的视频文件,确保返回
554
- if prev_output_filename is not None:
555
- yield prev_output_filename, gr.update(visible=False), gr.update(), f'处理过程中出现错误,但已生成部分视频', gr.update(interactive=True), gr.update(interactive=False)
556
- else:
557
- yield None, gr.update(visible=False), gr.update(), f'处理过程中出现错误: {str(e)}', gr.update(interactive=True), gr.update(interactive=False)
558
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
 
560
  except Exception as e:
561
  print(f"启动处理时出错: {e}")
562
  traceback.print_exc()
563
- yield None, gr.update(), gr.update(), f'启动处理时出错: {str(e)}', gr.update(interactive=True), gr.update(interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
 
565
 
566
  def end_process():
@@ -574,46 +818,454 @@ quick_prompts = [
574
  quick_prompts = [[x] for x in quick_prompts]
575
 
576
 
577
- css = make_progress_bar_css()
578
  block = gr.Blocks(css=css).queue()
579
  with block:
580
- gr.Markdown('# FramePack - 图像到视频生成')
581
- with gr.Row():
582
- with gr.Column():
583
- input_image = gr.Image(sources='upload', type="numpy", label="上传图像", height=320)
584
- prompt = gr.Textbox(label="提示词", value='')
585
- example_quick_prompts = gr.Dataset(samples=quick_prompts, label='快速提示词列表', samples_per_page=1000, components=[prompt])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586
  example_quick_prompts.click(lambda x: x[0], inputs=[example_quick_prompts], outputs=prompt, show_progress=False, queue=False)
587
 
588
- with gr.Row():
589
- start_button = gr.Button(value="开始生成")
590
- end_button = gr.Button(value="结束生成", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
591
 
 
592
  with gr.Group():
593
- use_teacache = gr.Checkbox(label='使用TeaCache', value=True, info='速度更快,但可能会使手指和手的生成效果稍差。')
594
-
595
- n_prompt = gr.Textbox(label="负面提示词", value="", visible=False) # Not used
596
- seed = gr.Number(label="随机种子", value=31337, precision=0)
597
-
598
- total_second_length = gr.Slider(label="视频长度(秒)", minimum=1, maximum=120, value=5, step=0.1)
599
- latent_window_size = gr.Slider(label="潜在窗口大小", minimum=1, maximum=33, value=9, step=1, visible=False) # Should not change
600
- steps = gr.Slider(label="推理步数", minimum=1, maximum=100, value=25, step=1, info='不建议修改此值。')
601
-
602
- cfg = gr.Slider(label="CFG Scale", minimum=1.0, maximum=32.0, value=1.0, step=0.01, visible=False) # Should not change
603
- gs = gr.Slider(label="蒸馏CFG比例", minimum=1.0, maximum=32.0, value=10.0, step=0.01, info='不建议修改此值。')
604
- rs = gr.Slider(label="CFG重缩放", minimum=0.0, maximum=1.0, value=0.0, step=0.01, visible=False) # Should not change
605
 
606
- gpu_memory_preservation = gr.Slider(label="GPU推理保留内存(GB)(值越大速度越慢)", minimum=6, maximum=128, value=6, step=0.1, info="如果出现OOM错误,请将此值设置得更大。值越大,速度越慢。")
 
 
 
 
 
 
607
 
608
- with gr.Column():
609
- preview_image = gr.Image(label="下一批潜变量", height=200, visible=False)
610
- result_video = gr.Video(label="生成的视频", autoplay=True, show_share_button=False, height=512, loop=True)
611
- gr.Markdown('注意:由于采样是倒序的,结束动作将在开始动作之前生成。如果视频中没有出现起始动作,请继续等待,它将在稍后生成。')
612
- progress_desc = gr.Markdown('', elem_classes='no-generating-animation')
613
- progress_bar = gr.HTML('', elem_classes='no-generating-animation')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  ips = [input_image, prompt, n_prompt, seed, total_second_length, latent_window_size, steps, cfg, gs, rs, gpu_memory_preservation, use_teacache]
 
 
615
  start_button.click(fn=process, inputs=ips, outputs=[result_video, preview_image, progress_desc, progress_bar, start_button, end_button])
616
  end_button.click(fn=end_process)
617
 
618
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619
  block.launch()
 
1
  from diffusers_helper.hf_login import login
2
 
3
  import os
4
+ import threading
5
+ import time
6
+ 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
 
13
+ # 添加中英双语翻译字典
14
+ translations = {
15
+ "en": {
16
+ "title": "FramePack - Image to Video Generation",
17
+ "upload_image": "Upload Image",
18
+ "prompt": "Prompt",
19
+ "quick_prompts": "Quick Prompts",
20
+ "start_generation": "Generate",
21
+ "stop_generation": "Stop",
22
+ "use_teacache": "Use TeaCache",
23
+ "teacache_info": "Faster speed, but may result in slightly worse finger and hand generation.",
24
+ "negative_prompt": "Negative Prompt",
25
+ "seed": "Seed",
26
+ "video_length": "Video Length (seconds)",
27
+ "latent_window": "Latent Window Size",
28
+ "steps": "Inference Steps",
29
+ "steps_info": "Changing this value is not recommended.",
30
+ "cfg_scale": "CFG Scale",
31
+ "distilled_cfg": "Distilled CFG Scale",
32
+ "distilled_cfg_info": "Changing this value is not recommended.",
33
+ "cfg_rescale": "CFG Rescale",
34
+ "gpu_memory": "GPU Memory Preservation (GB) (larger means slower)",
35
+ "gpu_memory_info": "Set this to a larger value if you encounter OOM errors. Larger values cause slower speed.",
36
+ "next_latents": "Next Latents",
37
+ "generated_video": "Generated Video",
38
+ "sampling_note": "Note: Due to reversed sampling, ending actions will be generated before starting actions. If the starting action is not in the video, please wait, it will be generated later.",
39
+ "error_message": "Error",
40
+ "processing_error": "Processing error",
41
+ "network_error": "Network connection is unstable, model download timed out. Please try again later.",
42
+ "memory_error": "GPU memory insufficient, please try increasing GPU memory preservation value or reduce video length.",
43
+ "model_error": "Failed to load model, possibly due to network issues or high server load. Please try again later.",
44
+ "partial_video": "Processing error, but partial video has been generated",
45
+ "processing_interrupt": "Processing was interrupted, but partial video has been generated"
46
+ },
47
+ "zh": {
48
+ "title": "FramePack - 图像到视频生成",
49
+ "upload_image": "上传图像",
50
+ "prompt": "提示词",
51
+ "quick_prompts": "快速提示词列表",
52
+ "start_generation": "开始生成",
53
+ "stop_generation": "结束生成",
54
+ "use_teacache": "使用TeaCache",
55
+ "teacache_info": "速度更快,但可能会使手指和手的生成效果稍差。",
56
+ "negative_prompt": "负面提示词",
57
+ "seed": "随机种子",
58
+ "video_length": "视频长度(秒)",
59
+ "latent_window": "潜在窗口大小",
60
+ "steps": "推理步数",
61
+ "steps_info": "不建议修改此值。",
62
+ "cfg_scale": "CFG Scale",
63
+ "distilled_cfg": "蒸馏CFG比例",
64
+ "distilled_cfg_info": "不建议修改此值。",
65
+ "cfg_rescale": "CFG重缩放",
66
+ "gpu_memory": "GPU推理保留内存(GB)(值越大速度越慢)",
67
+ "gpu_memory_info": "如果出现OOM错误,请将此值设置得更大。值越大,速度越慢。",
68
+ "next_latents": "下一批潜变量",
69
+ "generated_video": "生成的视频",
70
+ "sampling_note": "注意:由于采样是倒序的,结束动作将在开始动作之前生成。如果视频中没有出现起始动作,请继续等待,它将在稍后生成。",
71
+ "error_message": "错误信息",
72
+ "processing_error": "处理过程出错",
73
+ "network_error": "网络连接不稳定,模型下载超时。请稍后再试。",
74
+ "memory_error": "GPU内存不足,请尝试增加GPU推理保留内存值或降低视频长度。",
75
+ "model_error": "模型加载失败,可能是网络问题或服务器负载过高。请稍后再试。",
76
+ "partial_video": "处理过程中出现错误,但已生成部分视频",
77
+ "processing_interrupt": "处理过程中断,但已生成部分视频"
78
+ }
79
+ }
80
+
81
+ # 语言切换功能
82
+ def get_translation(key, lang="en"):
83
+ if lang in translations and key in translations[lang]:
84
+ return translations[lang][key]
85
+ # 默认返回英文
86
+ return translations["en"].get(key, key)
87
+
88
+ # 默认语言设置
89
+ current_language = "en"
90
+
91
+ # 切换语言函数
92
+ def switch_language():
93
+ global current_language
94
+ current_language = "zh" if current_language == "en" else "en"
95
+ return current_language
96
+
97
  import gradio as gr
98
  import torch
99
  import traceback
 
587
  break
588
  except Exception as e:
589
  print(f"处理输出时出错: {e}")
590
+ # 检查是否长时间没有更新
591
+ current_time = time.time()
592
+ if current_time - last_update_time > 60: # 60秒没有更新,可能卡住了
593
+ print(f"处理似乎卡住了,已经 {current_time - last_update_time:.1f} 秒没有更新")
594
+
595
+ # 如果有部分生成的视频,返回
596
+ if prev_output_filename:
597
+ # 创建双语部分视频生成消息
598
+ partial_video_msg = f"""
599
+ <div id="partial-video-container">
600
+ <div class="msg-en" data-lang="en">Processing error, but partial video has been generated</div>
601
+ <div class="msg-zh" data-lang="zh">处理过程中出现错误,但已生成部分视频</div>
602
+ </div>
603
+ <script>
604
+ // 根据当前语言显示相应的消息
605
+ (function() {{
606
+ const container = document.getElementById('partial-video-container');
607
+ if (container) {{
608
+ const currentLang = window.currentLang || 'en'; // 默认英语
609
+ const msgs = container.querySelectorAll('[data-lang]');
610
+ msgs.forEach(msg => {{
611
+ msg.style.display = msg.getAttribute('data-lang') === currentLang ? 'block' : 'none';
612
+ }});
613
+ }}
614
+ }})();
615
+ </script>
616
+ """
617
+ yield prev_output_filename, gr.update(visible=False), gr.update(), partial_video_msg, gr.update(interactive=True), gr.update(interactive=False)
618
+ else:
619
+ # 创建双语错误消息
620
+ error_msg = str(e)
621
+ en_msg = f"Processing error: {error_msg}"
622
+ zh_msg = f"处理过程中出现错误: {error_msg}"
623
+
624
+ error_html = f"""
625
+ <div id="error-msg-container">
626
+ <div class="error-msg-en" data-lang="en">{en_msg}</div>
627
+ <div class="error-msg-zh" data-lang="zh">{zh_msg}</div>
628
+ </div>
629
+ <script>
630
+ // 根据当前语言显示相应的错误消息
631
+ (function() {{
632
+ const errorContainer = document.getElementById('error-msg-container');
633
+ if (errorContainer) {{
634
+ const currentLang = window.currentLang || 'en'; // 默认英语
635
+ const errMsgs = errorContainer.querySelectorAll('[data-lang]');
636
+ errMsgs.forEach(msg => {{
637
+ msg.style.display = msg.getAttribute('data-lang') === currentLang ? 'block' : 'none';
638
+ }});
639
+ }}
640
+ }})();
641
+ </script>
642
+ """
643
+ yield None, gr.update(visible=False), gr.update(), error_html, gr.update(interactive=True), gr.update(interactive=False)
644
+ break
645
 
646
  except Exception as e:
647
  print(f"启动处理时出错: {e}")
648
  traceback.print_exc()
649
+ error_msg = str(e)
650
+ user_friendly_msg = f'处理过程出错: {error_msg}'
651
+
652
+ # 提供更友好的中英文双语错误信息
653
+ en_msg = ""
654
+ zh_msg = ""
655
+
656
+ if "模型下载超时" in error_msg or "网络连接不稳定" in error_msg or "ReadTimeoutError" in error_msg or "ConnectionError" in error_msg:
657
+ en_msg = "Network connection is unstable, model download timed out. Please try again later."
658
+ zh_msg = "网络连接不稳定,模型下载超时。请稍后再试。"
659
+ elif "GPU内存不足" in error_msg or "CUDA out of memory" in error_msg or "OutOfMemoryError" in error_msg:
660
+ en_msg = "GPU memory insufficient, please try increasing GPU memory preservation value or reduce video length."
661
+ zh_msg = "GPU内存不足,请尝试增加GPU推理保留内存值或降低视频长度。"
662
+ elif "无法加载模型" in error_msg:
663
+ en_msg = "Failed to load model, possibly due to network issues or high server load. Please try again later."
664
+ zh_msg = "模型加载失败,可能是网络问题或服务器负载过高。请稍后再试。"
665
+ else:
666
+ en_msg = f"Processing error: {error_msg}"
667
+ zh_msg = f"处理过程出错: {error_msg}"
668
+
669
+ # 创建双语错误消息HTML
670
+ bilingual_error = f"""
671
+ <div id="error-container">
672
+ <div class="error-msg-en" data-lang="en">{en_msg}</div>
673
+ <div class="error-msg-zh" data-lang="zh">{zh_msg}</div>
674
+ </div>
675
+ <script>
676
+ // 根据当前语言显示相应的错误消息
677
+ (function() {{
678
+ const errorContainer = document.getElementById('error-container');
679
+ if (errorContainer) {{
680
+ const currentLang = window.currentLang || 'en'; // 默认英语
681
+ const errMsgs = errorContainer.querySelectorAll('[data-lang]');
682
+ errMsgs.forEach(msg => {{
683
+ msg.style.display = msg.getAttribute('data-lang') === currentLang ? 'block' : 'none';
684
+ }});
685
+ }}
686
+ }})();
687
+ </script>
688
+ """
689
+
690
+ yield None, gr.update(visible=False), gr.update(), bilingual_error, gr.update(interactive=True), gr.update(interactive=False)
691
 
692
  process = process_with_gpu
693
  else:
 
730
  break
731
  except Exception as e:
732
  print(f"处理输出时出错: {e}")
733
+ # 检查是否长时间没有更新
734
+ current_time = time.time()
735
+ if current_time - last_update_time > 60: # 60秒没有更新,可能卡住了
736
+ print(f"处理似乎卡住了,已经 {current_time - last_update_time:.1f} 秒没有更新")
737
+
738
+ # 如果有部分生成的视频,返回
739
+ if prev_output_filename:
740
+ # 创建中断消息的双语支持
741
+ interrupt_msg = f"""
742
+ <div id="interrupt-container">
743
+ <div class="msg-en" data-lang="en">Processing was interrupted, but partial video has been generated</div>
744
+ <div class="msg-zh" data-lang="zh">处理过程中断,但已生成部分视频</div>
745
+ </div>
746
+ <script>
747
+ // 根据当前语言显示相应的消息
748
+ (function() {{
749
+ const container = document.getElementById('interrupt-container');
750
+ if (container) {{
751
+ const currentLang = window.currentLang || 'en'; // 默认英语
752
+ const msgs = container.querySelectorAll('[data-lang]');
753
+ msgs.forEach(msg => {{
754
+ msg.style.display = msg.getAttribute('data-lang') === currentLang ? 'block' : 'none';
755
+ }});
756
+ }}
757
+ }})();
758
+ </script>
759
+ """
760
+ yield prev_output_filename, gr.update(visible=False), gr.update(), interrupt_msg, gr.update(interactive=True), gr.update(interactive=False)
761
+ break
762
 
763
  except Exception as e:
764
  print(f"启动处理时出错: {e}")
765
  traceback.print_exc()
766
+ error_msg = str(e)
767
+ user_friendly_msg = f'处理过程出错: {error_msg}'
768
+
769
+ # 提供更友好的中英文双语错误信息
770
+ en_msg = ""
771
+ zh_msg = ""
772
+
773
+ if "模型下载超时" in error_msg or "网络连接不稳定" in error_msg or "ReadTimeoutError" in error_msg or "ConnectionError" in error_msg:
774
+ en_msg = "Network connection is unstable, model download timed out. Please try again later."
775
+ zh_msg = "网络连接不稳定,模型下载超时。请稍后再试。"
776
+ elif "GPU内存不足" in error_msg or "CUDA out of memory" in error_msg or "OutOfMemoryError" in error_msg:
777
+ en_msg = "GPU memory insufficient, please try increasing GPU memory preservation value or reduce video length."
778
+ zh_msg = "GPU内存不足,请尝试增加GPU推理保留内存值或降低视频长度。"
779
+ elif "无法加载模型" in error_msg:
780
+ en_msg = "Failed to load model, possibly due to network issues or high server load. Please try again later."
781
+ zh_msg = "模型加载失败,可能是网络问题或服务器负载过高。请稍后再试。"
782
+ else:
783
+ en_msg = f"Processing error: {error_msg}"
784
+ zh_msg = f"处理过程出错: {error_msg}"
785
+
786
+ # 创建双语错误消息HTML
787
+ bilingual_error = f"""
788
+ <div id="error-container">
789
+ <div class="error-msg-en" data-lang="en">{en_msg}</div>
790
+ <div class="error-msg-zh" data-lang="zh">{zh_msg}</div>
791
+ </div>
792
+ <script>
793
+ // 根据当前语言显示相应的错误消息
794
+ (function() {{
795
+ const errorContainer = document.getElementById('error-container');
796
+ if (errorContainer) {{
797
+ const currentLang = window.currentLang || 'en'; // 默认英语
798
+ const errMsgs = errorContainer.querySelectorAll('[data-lang]');
799
+ errMsgs.forEach(msg => {{
800
+ msg.style.display = msg.getAttribute('data-lang') === currentLang ? 'block' : 'none';
801
+ }});
802
+ }}
803
+ }})();
804
+ </script>
805
+ """
806
+
807
+ yield None, gr.update(visible=False), gr.update(), bilingual_error, gr.update(interactive=True), gr.update(interactive=False)
808
 
809
 
810
  def end_process():
 
818
  quick_prompts = [[x] for x in quick_prompts]
819
 
820
 
821
+ css = make_custom_css()
822
  block = gr.Blocks(css=css).queue()
823
  with block:
824
+ # 添加语言切换功能
825
+ gr.HTML("""
826
+ <div id="app-container">
827
+ <button id="language-toggle" onclick="toggleLanguage()">中文/English</button>
828
+ </div>
829
+ <script>
830
+ // 全局变量,存储当前语言
831
+ window.currentLang = "en";
832
+
833
+ // 语言切换函数
834
+ function toggleLanguage() {
835
+ window.currentLang = window.currentLang === "en" ? "zh" : "en";
836
+
837
+ // 获取所有带有data-i18n属性的元素
838
+ const elements = document.querySelectorAll('[data-i18n]');
839
+
840
+ // 遍历并切换语言
841
+ elements.forEach(el => {
842
+ const key = el.getAttribute('data-i18n');
843
+ const translations = {
844
+ "en": {
845
+ "title": "FramePack - Image to Video Generation",
846
+ "upload_image": "Upload Image",
847
+ "prompt": "Prompt",
848
+ "quick_prompts": "Quick Prompts",
849
+ "start_generation": "Generate",
850
+ "stop_generation": "Stop",
851
+ "use_teacache": "Use TeaCache",
852
+ "teacache_info": "Faster speed, but may result in slightly worse finger and hand generation.",
853
+ "negative_prompt": "Negative Prompt",
854
+ "seed": "Seed",
855
+ "video_length": "Video Length (seconds)",
856
+ "latent_window": "Latent Window Size",
857
+ "steps": "Inference Steps",
858
+ "steps_info": "Changing this value is not recommended.",
859
+ "cfg_scale": "CFG Scale",
860
+ "distilled_cfg": "Distilled CFG Scale",
861
+ "distilled_cfg_info": "Changing this value is not recommended.",
862
+ "cfg_rescale": "CFG Rescale",
863
+ "gpu_memory": "GPU Memory Preservation (GB) (larger means slower)",
864
+ "gpu_memory_info": "Set this to a larger value if you encounter OOM errors. Larger values cause slower speed.",
865
+ "next_latents": "Next Latents",
866
+ "generated_video": "Generated Video",
867
+ "sampling_note": "Note: Due to reversed sampling, ending actions will be generated before starting actions. If the starting action is not in the video, please wait, it will be generated later.",
868
+ "error_message": "Error",
869
+ "processing_error": "Processing error",
870
+ "network_error": "Network connection is unstable, model download timed out. Please try again later.",
871
+ "memory_error": "GPU memory insufficient, please try increasing GPU memory preservation value or reduce video length.",
872
+ "model_error": "Failed to load model, possibly due to network issues or high server load. Please try again later.",
873
+ "partial_video": "Processing error, but partial video has been generated",
874
+ "processing_interrupt": "Processing was interrupted, but partial video has been generated"
875
+ },
876
+ "zh": {
877
+ "title": "FramePack - 图像到视频生成",
878
+ "upload_image": "上传图像",
879
+ "prompt": "提示词",
880
+ "quick_prompts": "快速提示词列表",
881
+ "start_generation": "开始生成",
882
+ "stop_generation": "结束生成",
883
+ "use_teacache": "使用TeaCache",
884
+ "teacache_info": "速度更快,但可能会使手指和手的生成效果稍差。",
885
+ "negative_prompt": "负面提示词",
886
+ "seed": "随机种子",
887
+ "video_length": "视频长度(秒)",
888
+ "latent_window": "潜在窗口大小",
889
+ "steps": "推理步数",
890
+ "steps_info": "不建议修改此值。",
891
+ "cfg_scale": "CFG Scale",
892
+ "distilled_cfg": "蒸馏CFG比例",
893
+ "distilled_cfg_info": "不建议修改此值。",
894
+ "cfg_rescale": "CFG重缩放",
895
+ "gpu_memory": "GPU推理保留内存(GB)(值越大速度越慢)",
896
+ "gpu_memory_info": "如果出现OOM错误,请将此值设置得更大。值越大,速度越慢。",
897
+ "next_latents": "下一批潜变量",
898
+ "generated_video": "生成的视频",
899
+ "sampling_note": "注意:由于采样是倒序的,结束动作将在开始动作之前生成。如果视频中没有出现起始动作,请继续等待,它将在稍后生成。",
900
+ "error_message": "错误信息",
901
+ "processing_error": "处理过程出错",
902
+ "network_error": "网络连接不稳定,模型下载超时。请稍后再试。",
903
+ "memory_error": "GPU内存不足,请尝试增加GPU推理保留内存值或降低视频长度。",
904
+ "model_error": "模型加载失败,可能是网络问题或服务器负载过高。请稍后再试。",
905
+ "partial_video": "处理过程中出现错误,但已生成部分视频",
906
+ "processing_interrupt": "处理过程中断,但已生成部分视频"
907
+ }
908
+ };
909
+
910
+ if (translations[window.currentLang] && translations[window.currentLang][key]) {
911
+ // 根据元素类型设置文本
912
+ if (el.tagName === 'BUTTON') {
913
+ el.textContent = translations[window.currentLang][key];
914
+ } else if (el.tagName === 'LABEL') {
915
+ el.textContent = translations[window.currentLang][key];
916
+ } else {
917
+ el.innerHTML = translations[window.currentLang][key];
918
+ }
919
+ }
920
+ });
921
+
922
+ // 更新页面上其他元素
923
+ document.querySelectorAll('.bilingual-label').forEach(el => {
924
+ const enText = el.getAttribute('data-en');
925
+ const zhText = el.getAttribute('data-zh');
926
+ el.textContent = window.currentLang === 'en' ? enText : zhText;
927
+ });
928
+
929
+ // 处理错误消息容器
930
+ document.querySelectorAll('[data-lang]').forEach(el => {
931
+ el.style.display = el.getAttribute('data-lang') === window.currentLang ? 'block' : 'none';
932
+ });
933
+ }
934
+
935
+ // 页面加载后初始化
936
+ document.addEventListener('DOMContentLoaded', function() {
937
+ // 添加data-i18n属性到需要国际化的元素
938
+ setTimeout(() => {
939
+ // 给所有标签添加i18n属性
940
+ const labelMap = {
941
+ "Upload Image": "upload_image",
942
+ "上传图像": "upload_image",
943
+ "Prompt": "prompt",
944
+ "提示词": "prompt",
945
+ "Quick Prompts": "quick_prompts",
946
+ "快速提示词列表": "quick_prompts",
947
+ "Generate": "start_generation",
948
+ "开始生成": "start_generation",
949
+ "Stop": "stop_generation",
950
+ "结束生成": "stop_generation",
951
+ // 添加其他标签映射...
952
+ };
953
+
954
+ // 处理标签
955
+ document.querySelectorAll('label, span, button').forEach(el => {
956
+ const text = el.textContent.trim();
957
+ if (labelMap[text]) {
958
+ el.setAttribute('data-i18n', labelMap[text]);
959
+ }
960
+ });
961
+
962
+ // 添加特定元素的i18n属性
963
+ const titleEl = document.querySelector('h1');
964
+ if (titleEl) titleEl.setAttribute('data-i18n', 'title');
965
+
966
+ // 初始化标签语言
967
+ toggleLanguage();
968
+ }, 1000);
969
+ });
970
+ </script>
971
+ """)
972
+
973
+ # 标题使用data-i18n属性以便JavaScript切换
974
+ gr.HTML("<h1 data-i18n='title'>FramePack - Image to Video Generation / 图像到视频生成</h1>")
975
+
976
+ # 使用带有mobile-full-width类的响应式行
977
+ with gr.Row(elem_classes="mobile-full-width"):
978
+ with gr.Column(scale=1, elem_classes="mobile-full-width"):
979
+ # 添加双语标签 - 上传图像
980
+ input_image = gr.Image(
981
+ sources='upload',
982
+ type="numpy",
983
+ label="Upload Image / 上传图像",
984
+ elem_id="input-image",
985
+ height=320
986
+ )
987
+
988
+ # 添加双语标签 - 提示词
989
+ prompt = gr.Textbox(
990
+ label="Prompt / 提示词",
991
+ value='',
992
+ elem_id="prompt-input"
993
+ )
994
+
995
+ # 添加双语标签 - 快速提示词
996
+ example_quick_prompts = gr.Dataset(
997
+ samples=quick_prompts,
998
+ label='Quick Prompts / 快速提示词列表',
999
+ samples_per_page=1000,
1000
+ components=[prompt]
1001
+ )
1002
  example_quick_prompts.click(lambda x: x[0], inputs=[example_quick_prompts], outputs=prompt, show_progress=False, queue=False)
1003
 
1004
+ # 按钮添加样式和双语标签
1005
+ with gr.Row(elem_classes="button-container"):
1006
+ start_button = gr.Button(
1007
+ value="Generate / 开始生成",
1008
+ elem_classes="start-btn",
1009
+ elem_id="start-button",
1010
+ variant="primary"
1011
+ )
1012
+
1013
+ end_button = gr.Button(
1014
+ value="Stop / 结束生成",
1015
+ elem_classes="stop-btn",
1016
+ elem_id="stop-button",
1017
+ interactive=False
1018
+ )
1019
 
1020
+ # 参数设置区域
1021
  with gr.Group():
1022
+ use_teacache = gr.Checkbox(
1023
+ label='Use TeaCache / 使用TeaCache',
1024
+ value=True,
1025
+ info='Faster speed, but may result in slightly worse finger and hand generation. / 速度更快,但可能会使手指和手的生成效果稍差。'
1026
+ )
 
 
 
 
 
 
 
1027
 
1028
+ n_prompt = gr.Textbox(label="Negative Prompt / 负面提示词", value="", visible=False) # Not used
1029
+
1030
+ seed = gr.Number(
1031
+ label="Seed / 随机种子",
1032
+ value=31337,
1033
+ precision=0
1034
+ )
1035
 
1036
+ # 添加slider-container类以便CSS触摸优化
1037
+ with gr.Group(elem_classes="slider-container"):
1038
+ total_second_length = gr.Slider(
1039
+ label="Video Length (seconds) / 视频长度(秒)",
1040
+ minimum=1,
1041
+ maximum=120,
1042
+ value=5,
1043
+ step=0.1
1044
+ )
1045
+
1046
+ latent_window_size = gr.Slider(
1047
+ label="Latent Window Size / 潜在窗口大小",
1048
+ minimum=1,
1049
+ maximum=33,
1050
+ value=9,
1051
+ step=1,
1052
+ visible=False
1053
+ )
1054
+
1055
+ steps = gr.Slider(
1056
+ label="Inference Steps / 推理步数",
1057
+ minimum=1,
1058
+ maximum=100,
1059
+ value=25,
1060
+ step=1,
1061
+ info='Changing this value is not recommended. / 不建议修改此值。'
1062
+ )
1063
+
1064
+ cfg = gr.Slider(
1065
+ label="CFG Scale",
1066
+ minimum=1.0,
1067
+ maximum=32.0,
1068
+ value=1.0,
1069
+ step=0.01,
1070
+ visible=False
1071
+ )
1072
+
1073
+ gs = gr.Slider(
1074
+ label="Distilled CFG Scale / 蒸馏CFG比例",
1075
+ minimum=1.0,
1076
+ maximum=32.0,
1077
+ value=10.0,
1078
+ step=0.01,
1079
+ info='Changing this value is not recommended. / 不建议修改此值。'
1080
+ )
1081
+
1082
+ rs = gr.Slider(
1083
+ label="CFG Rescale / CFG重缩放",
1084
+ minimum=0.0,
1085
+ maximum=1.0,
1086
+ value=0.0,
1087
+ step=0.01,
1088
+ visible=False
1089
+ )
1090
+
1091
+ gpu_memory_preservation = gr.Slider(
1092
+ label="GPU Memory (GB) / GPU推理保留内存(GB)",
1093
+ minimum=6,
1094
+ maximum=128,
1095
+ value=6,
1096
+ step=0.1,
1097
+ info="Set this to a larger value if you encounter OOM errors. Larger values cause slower speed. / 如果出现OOM错误,请将此值设置得更大。值越大,速度越慢。"
1098
+ )
1099
+
1100
+ # 右侧预览和结果列
1101
+ with gr.Column(scale=1, elem_classes="mobile-full-width"):
1102
+ # 预览图像
1103
+ preview_image = gr.Image(
1104
+ label="Preview / 预览",
1105
+ height=200,
1106
+ visible=False,
1107
+ elem_classes="preview-container"
1108
+ )
1109
+
1110
+ # 视频结果容器
1111
+ result_video = gr.Video(
1112
+ label="Generated Video / 生成的视频",
1113
+ autoplay=True,
1114
+ show_share_button=True, # 添加分享按钮
1115
+ height=512,
1116
+ loop=True,
1117
+ elem_classes="video-container",
1118
+ elem_id="result-video"
1119
+ )
1120
+
1121
+ # 双语说明
1122
+ gr.HTML("<div data-i18n='sampling_note' class='note'>Note: Due to reversed sampling, ending actions will be generated before starting actions. If the starting action is not in the video, please wait, it will be generated later.</div>")
1123
+
1124
+ # 进度指示器
1125
+ with gr.Group(elem_classes="progress-container"):
1126
+ progress_desc = gr.Markdown('', elem_classes='no-generating-animation')
1127
+ progress_bar = gr.HTML('', elem_classes='no-generating-animation')
1128
+
1129
+ # 错误信息区域
1130
+ error_message = gr.Markdown('', elem_id='error-message')
1131
+
1132
+ # 处理函数
1133
  ips = [input_image, prompt, n_prompt, seed, total_second_length, latent_window_size, steps, cfg, gs, rs, gpu_memory_preservation, use_teacache]
1134
+
1135
+ # 开始和结束按钮事件
1136
  start_button.click(fn=process, inputs=ips, outputs=[result_video, preview_image, progress_desc, progress_bar, start_button, end_button])
1137
  end_button.click(fn=end_process)
1138
 
1139
 
1140
+ # 创建一个自定义CSS,增加响应式布局支持
1141
+ def make_custom_css():
1142
+ progress_bar_css = make_progress_bar_css()
1143
+
1144
+ responsive_css = """
1145
+ /* 基础响应式设置 */
1146
+ #app-container {
1147
+ max-width: 100%;
1148
+ margin: 0 auto;
1149
+ }
1150
+
1151
+ /* 语言切换按钮样式 */
1152
+ #language-toggle {
1153
+ position: fixed;
1154
+ top: 10px;
1155
+ right: 10px;
1156
+ z-index: 1000;
1157
+ background-color: rgba(0, 0, 0, 0.7);
1158
+ color: white;
1159
+ border: none;
1160
+ border-radius: 4px;
1161
+ padding: 5px 10px;
1162
+ cursor: pointer;
1163
+ font-size: 14px;
1164
+ }
1165
+
1166
+ /* 页面标题样式 */
1167
+ h1 {
1168
+ font-size: 2rem;
1169
+ text-align: center;
1170
+ margin-bottom: 1rem;
1171
+ }
1172
+
1173
+ /* 按钮样式 */
1174
+ .start-btn, .stop-btn {
1175
+ min-height: 45px;
1176
+ font-size: 1rem;
1177
+ }
1178
+
1179
+ /* 移动设备样式 - 小屏幕 */
1180
+ @media (max-width: 768px) {
1181
+ h1 {
1182
+ font-size: 1.5rem;
1183
+ margin-bottom: 0.5rem;
1184
+ }
1185
+
1186
+ /* 单列布局 */
1187
+ .mobile-full-width {
1188
+ flex-direction: column !important;
1189
+ }
1190
+
1191
+ .mobile-full-width > .gr-block {
1192
+ min-width: 100% !important;
1193
+ flex-grow: 1;
1194
+ }
1195
+
1196
+ /* 调整视频大小 */
1197
+ .video-container {
1198
+ height: auto !important;
1199
+ }
1200
+
1201
+ /* 调整按钮大小 */
1202
+ .button-container button {
1203
+ min-height: 50px;
1204
+ font-size: 1rem;
1205
+ touch-action: manipulation;
1206
+ }
1207
+
1208
+ /* 调整滑块 */
1209
+ .slider-container input[type="range"] {
1210
+ height: 30px;
1211
+ }
1212
+ }
1213
+
1214
+ /* 平板设备样式 */
1215
+ @media (min-width: 769px) and (max-width: 1024px) {
1216
+ .tablet-adjust {
1217
+ width: 48% !important;
1218
+ }
1219
+ }
1220
+
1221
+ /* 黑暗模式支持 */
1222
+ @media (prefers-color-scheme: dark) {
1223
+ .dark-mode-text {
1224
+ color: #f0f0f0;
1225
+ }
1226
+
1227
+ .dark-mode-bg {
1228
+ background-color: #2a2a2a;
1229
+ }
1230
+ }
1231
+
1232
+ /* 增强可访问性 */
1233
+ button, input, select, textarea {
1234
+ font-size: 16px; /* 防止iOS缩放 */
1235
+ }
1236
+
1237
+ /* 触摸优化 */
1238
+ button, .interactive-element {
1239
+ min-height: 44px;
1240
+ min-width: 44px;
1241
+ }
1242
+
1243
+ /* 提高对比度 */
1244
+ .high-contrast {
1245
+ color: #fff;
1246
+ background-color: #000;
1247
+ }
1248
+
1249
+ /* 进度条样式增强 */
1250
+ .progress-container {
1251
+ margin-top: 10px;
1252
+ margin-bottom: 10px;
1253
+ }
1254
+
1255
+ /* 错误消息样式 */
1256
+ #error-message {
1257
+ color: #ff4444;
1258
+ font-weight: bold;
1259
+ padding: 10px;
1260
+ border-radius: 4px;
1261
+ margin-top: 10px;
1262
+ background-color: rgba(255, 0, 0, 0.1);
1263
+ }
1264
+ """
1265
+
1266
+ # 合并CSS
1267
+ combined_css = progress_bar_css + responsive_css
1268
+ return combined_css
1269
+
1270
+
1271
  block.launch()