HOPStudio commited on
Commit
8d50ebd
·
verified ·
1 Parent(s): 5373ae2

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +140 -417
main.py CHANGED
@@ -6,7 +6,6 @@ import json
6
  import sys
7
  import time
8
  import zipfile
9
- import re
10
  import requests
11
  import gradio as gr
12
 
@@ -18,25 +17,24 @@ FRP_VERSION = "0.69.0"
18
  CURRENT_DIR = REPO_PATH
19
  LAST_OUTPUT = ""
20
 
21
- # ===== 新增:ModelScope 整库克隆/下载并启动 ComfyUI 的配置 =====
22
  MODELSCOPE_MODEL_ID = "HOPStudio/forgeui"
23
- # forgeui 整库和 offline_build 下载包都很大,不放在 REPO_PATH 里,避免 clone_repository() 删除 /root/modelscope 时把断点文件删掉。
24
- OFFLINE_BUILD_CACHE_DIR = "/root/offline_build_cache"
25
- MODELSCOPE_DOWNLOAD_DIR = os.path.join(OFFLINE_BUILD_CACHE_DIR, "modelscope_download")
26
- MODELSCOPE_CACHE_DIR = os.path.join(OFFLINE_BUILD_CACHE_DIR, "modelscope_cache")
27
- OFFLINE_BUILD_DIR = os.path.join(REPO_PATH, "offline_build")
 
 
 
 
 
 
28
  COMFYUI_PORT = 8188
29
  COMFYUI_LOG_PATH = os.path.join(REPO_PATH, "comfyui.log")
30
  PROXY_URL = "http://103.71.69.203:17890"
31
  MODELSCOPE_DOWNLOAD_TIMEOUT = 21600
32
 
33
- # 兼容可能的文件名拼写:用户当前指定 offlinbuild.zip,
34
- # 同时兼容 offlinebuild.zip / offline_build.zip,避免文件名略有差异时直接失败。
35
- OFFLINE_BUILD_ZIP_CANDIDATES = [
36
- "offlinbuild.zip",
37
- "offlinebuild.zip",
38
- "offline_build.zip"
39
- ]
40
 
41
 
42
  def clone_repository():
@@ -392,7 +390,7 @@ def start_jupyterlab_process():
392
 
393
 
394
 
395
- # ===== 新增:ModelScope 整库克隆/下载 offline_build 并启动 ComfyUI =====
396
  def get_proxy_env():
397
  env = os.environ.copy()
398
 
@@ -407,25 +405,14 @@ def get_proxy_env():
407
  return env
408
 
409
 
410
- def get_modelscope_download_env(use_proxy=False):
411
  env = os.environ.copy()
412
 
413
- # 固定 ModelScope 缓存目录,避免默认缓存散落到 ~/.cache,且方便断点续传。
414
  os.makedirs(MODELSCOPE_CACHE_DIR, exist_ok=True)
415
  env["MODELSCOPE_CACHE"] = MODELSCOPE_CACHE_DIR
416
  env["MODELSCOPE_ENDPOINT"] = "https://www.modelscope.cn"
417
  env["MODELSCOPE_SDK_ENDPOINT"] = "https://www.modelscope.cn"
418
 
419
- # ModelScope 下载大文件时会跳到阿里云 OSS。这里默认直连,避免代理把 OSS 内网地址转坏。
420
- proxy_keys = [
421
- "http_proxy",
422
- "https_proxy",
423
- "HTTP_PROXY",
424
- "HTTPS_PROXY",
425
- "ALL_PROXY",
426
- "all_proxy"
427
- ]
428
-
429
  if use_proxy:
430
  env["http_proxy"] = PROXY_URL
431
  env["https_proxy"] = PROXY_URL
@@ -434,12 +421,18 @@ def get_modelscope_download_env(use_proxy=False):
434
  env["ALL_PROXY"] = PROXY_URL
435
  env["all_proxy"] = PROXY_URL
436
  else:
437
- for key in proxy_keys:
 
 
 
 
 
 
 
438
  env.pop(key, None)
439
 
440
- no_proxy_value = "localhost,127.0.0.1,modelscope.cn,www.modelscope.cn,.modelscope.cn,aliyuncs.com,.aliyuncs.com"
441
- env["no_proxy"] = no_proxy_value
442
- env["NO_PROXY"] = no_proxy_value
443
 
444
  return env
445
 
@@ -474,35 +467,11 @@ def is_valid_zip(path):
474
  return False
475
 
476
  with zipfile.ZipFile(path, "r") as zf:
477
- # 只读取目录,不做全量 testzip,避免大压缩包校验耗时过长。
478
  return len(zf.namelist()) > 0
479
  except Exception:
480
  return False
481
 
482
 
483
- def find_existing_offline_zip():
484
- search_dirs = [MODELSCOPE_DOWNLOAD_DIR, OFFLINE_BUILD_CACHE_DIR, REPO_PATH]
485
-
486
- for directory in search_dirs:
487
- if not os.path.isdir(directory):
488
- continue
489
-
490
- for zip_name in OFFLINE_BUILD_ZIP_CANDIDATES:
491
- zip_path = os.path.join(directory, zip_name)
492
- if is_valid_zip(zip_path):
493
- return zip_path
494
-
495
- for root, _, files in os.walk(directory):
496
- for filename in files:
497
- lower_name = filename.lower()
498
- if lower_name.endswith(".zip") and "offlin" in lower_name:
499
- zip_path = os.path.join(root, filename)
500
- if is_valid_zip(zip_path):
501
- return zip_path
502
-
503
- return None
504
-
505
-
506
  def install_modelscope_cli():
507
  if shutil.which("modelscope"):
508
  print("检测到 modelscope 命令。")
@@ -535,6 +504,7 @@ def install_modelscope_cli():
535
  try:
536
  result = subprocess.run(
537
  command,
 
538
  stdout=subprocess.PIPE,
539
  stderr=subprocess.PIPE,
540
  text=True,
@@ -556,297 +526,43 @@ def install_modelscope_cli():
556
  except Exception as e:
557
  print(f"modelscope 安装异常: {e}")
558
 
559
- print("modelscope 不可用,无法继续下载 offline_build。")
560
  return False
561
 
562
 
563
- def extract_urls_from_text(text):
564
- if not text:
565
- return []
566
-
567
- urls = re.findall(r"https?://[^\s'\")<>]+", text)
568
- cleaned_urls = []
569
-
570
- for url in urls:
571
- cleaned_urls.append(url.rstrip(".,;"))
572
-
573
- return cleaned_urls
574
-
575
-
576
- def convert_internal_oss_url_to_public(url):
577
- if not url:
578
- return None
579
-
580
- public_url = url.replace("-internal.aliyuncs.com", ".aliyuncs.com")
581
- public_url = public_url.replace("http://", "https://", 1)
582
-
583
- if public_url == url:
584
- return None
585
-
586
- return public_url
587
-
588
-
589
- def download_url_to_file(url, output_path, use_proxy=False):
590
- os.makedirs(os.path.dirname(output_path), exist_ok=True)
591
-
592
- env = get_modelscope_download_env(use_proxy=use_proxy)
593
-
594
- if shutil.which("aria2c"):
595
- command = [
596
- "aria2c",
597
- "--continue=true",
598
- "--max-connection-per-server=8",
599
- "--split=8",
600
- "--min-split-size=16M",
601
- "--check-certificate=false",
602
- "--summary-interval=30",
603
- "--timeout=30",
604
- "--retry-wait=10",
605
- "--max-tries=20",
606
- "--dir",
607
- os.path.dirname(output_path),
608
- "--out",
609
- os.path.basename(output_path),
610
- url
611
- ]
612
- else:
613
- command = [
614
- "wget",
615
- "--continue",
616
- "--timeout=30",
617
- "--tries=20",
618
- "--no-check-certificate",
619
- "--output-document",
620
- output_path,
621
- url
622
- ]
623
 
 
 
624
  try:
625
- print("执行直链下载命令: " + " ".join(command[:1]) + " ...")
 
 
 
626
 
627
- result = subprocess.run(
628
- command,
629
- env=env,
630
- stdout=subprocess.PIPE,
631
- stderr=subprocess.PIPE,
632
- text=True,
633
- timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
634
- )
635
-
636
- if result.stdout.strip():
637
- print(result.stdout[-2000:])
638
- if result.stderr.strip():
639
- print(result.stderr[-2000:])
640
 
641
- if result.returncode == 0 and is_valid_zip(output_path):
642
- print(f"直链下载成功: {output_path}")
643
- return True
644
-
645
- print(f"直链下载失败或压缩包无效: {output_path}")
646
- return False
647
-
648
- except subprocess.TimeoutExpired:
649
- print("直链下载超时。")
650
- return False
651
  except Exception as e:
652
- print(f"直链下载异常: {e}")
653
- return False
654
-
655
-
656
- def try_public_oss_fallback(download_output, zip_name):
657
- urls = extract_urls_from_text(download_output)
658
-
659
- for url in urls:
660
- public_url = convert_internal_oss_url_to_public(url)
661
- if not public_url:
662
- continue
663
-
664
- print("检测到 ModelScope 返回 OSS 内网地址,尝试改用公网 OSS 地址继续下载。")
665
- target_path = os.path.join(MODELSCOPE_DOWNLOAD_DIR, zip_name)
666
-
667
- # 先直连公网 OSS;失败后才走代理。直连更适合国内服务器访问阿里云 OSS。
668
- if download_url_to_file(public_url, target_path, use_proxy=False):
669
- return target_path
670
-
671
- if download_url_to_file(public_url, target_path, use_proxy=True):
672
- return target_path
673
 
674
  return None
675
 
676
 
677
- def ensure_git_lfs_available():
678
- try:
679
- result = subprocess.run(
680
- ["git", "lfs", "version"],
681
- stdout=subprocess.PIPE,
682
- stderr=subprocess.PIPE,
683
- text=True,
684
- timeout=20
685
- )
686
- if result.returncode == 0:
687
- print("检测到 git-lfs。")
688
- return True
689
- except Exception:
690
- pass
691
-
692
- print("未检测到 git-lfs,尝试安装 git-lfs。")
693
-
694
- install_commands = []
695
- if shutil.which("apt-get"):
696
- install_commands.append(["bash", "-lc", "apt-get update && apt-get install -y git-lfs"])
697
- if shutil.which("yum"):
698
- install_commands.append(["bash", "-lc", "yum install -y git-lfs"])
699
-
700
- for command in install_commands:
701
- try:
702
- result = subprocess.run(
703
- command,
704
- stdout=subprocess.PIPE,
705
- stderr=subprocess.PIPE,
706
- text=True,
707
- timeout=600
708
- )
709
- if result.stdout.strip():
710
- print(result.stdout[-2000:])
711
- if result.stderr.strip():
712
- print(result.stderr[-2000:])
713
-
714
- check = subprocess.run(
715
- ["git", "lfs", "version"],
716
- stdout=subprocess.PIPE,
717
- stderr=subprocess.PIPE,
718
- text=True,
719
- timeout=20
720
- )
721
- if check.returncode == 0:
722
- print("git-lfs 安装成功。")
723
- return True
724
-
725
- except subprocess.TimeoutExpired:
726
- print("git-lfs 安装超时。")
727
- except Exception as e:
728
- print(f"git-lfs 安装异常: {e}")
729
-
730
- print("git-lfs 不可用,后续 git clone 可能只能拿到 LFS 指针文件。")
731
- return False
732
-
733
-
734
- def clone_whole_forgeui_repo_with_git():
735
- os.makedirs(OFFLINE_BUILD_CACHE_DIR, exist_ok=True)
736
-
737
- existing_zip = find_existing_offline_zip()
738
- if existing_zip:
739
- print(f"检测到已有 offline_build 压缩包: {existing_zip}")
740
- return existing_zip
741
-
742
- if not shutil.which("git"):
743
- print("未检测到 git,跳过 git 整库克隆。")
744
- return None
745
-
746
- ensure_git_lfs_available()
747
-
748
- repo_urls = [
749
- "https://www.modelscope.cn/HOPStudio/forgeui.git",
750
- "https://modelscope.cn/HOPStudio/forgeui.git"
751
- ]
752
-
753
- if os.path.isdir(os.path.join(MODELSCOPE_DOWNLOAD_DIR, ".git")):
754
- print(f"检测到已存在 ModelScope 整库仓库: {MODELSCOPE_DOWNLOAD_DIR}")
755
- try:
756
- subprocess.run(
757
- ["git", "-C", MODELSCOPE_DOWNLOAD_DIR, "lfs", "pull"],
758
- env=get_modelscope_download_env(use_proxy=False),
759
- stdout=subprocess.PIPE,
760
- stderr=subprocess.PIPE,
761
- text=True,
762
- timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
763
- )
764
- except Exception as e:
765
- print(f"git lfs pull 执行异常: {e}")
766
-
767
- existing_zip = find_existing_offline_zip()
768
- if existing_zip:
769
- print(f"在已克隆仓库中找到 offline_build 压缩包: {existing_zip}")
770
- return existing_zip
771
-
772
- elif os.path.exists(MODELSCOPE_DOWNLOAD_DIR):
773
- # 这里是旧版 modelscope download 留下的目录。整库 git clone 需要空目录,
774
- # 因此先改名备份,不直接删除,避免误删已有断点文件。
775
- backup_dir = MODELSCOPE_DOWNLOAD_DIR + f".bak_{int(time.time())}"
776
- try:
777
- shutil.move(MODELSCOPE_DOWNLOAD_DIR, backup_dir)
778
- print(f"检测到非 git 下载目录,已备份为: {backup_dir}")
779
- except Exception as e:
780
- print(f"备份旧下载目录失败: {e}")
781
- return None
782
-
783
- for repo_url in repo_urls:
784
- for use_proxy, mode_name in [(False, "直连"), (True, "代理")]:
785
- if os.path.exists(MODELSCOPE_DOWNLOAD_DIR):
786
- shutil.rmtree(MODELSCOPE_DOWNLOAD_DIR, ignore_errors=True)
787
-
788
- env = get_modelscope_download_env(use_proxy=use_proxy)
789
- command = [
790
- "git",
791
- "clone",
792
- "--depth",
793
- "1",
794
- repo_url,
795
- MODELSCOPE_DOWNLOAD_DIR
796
- ]
797
-
798
- try:
799
- print(f"执行 ModelScope 整库 git clone [{mode_name}]: " + " ".join(command))
800
-
801
- result = subprocess.run(
802
- command,
803
- env=env,
804
- stdout=subprocess.PIPE,
805
- stderr=subprocess.PIPE,
806
- text=True,
807
- timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
808
- )
809
-
810
- if result.stdout.strip():
811
- print(result.stdout[-2000:])
812
- if result.stderr.strip():
813
- print(result.stderr[-4000:])
814
-
815
- if result.returncode != 0:
816
- print("本次 git clone 失败,继续尝试下一个方式。")
817
- continue
818
-
819
- try:
820
- subprocess.run(
821
- ["git", "-C", MODELSCOPE_DOWNLOAD_DIR, "lfs", "pull"],
822
- env=env,
823
- stdout=subprocess.PIPE,
824
- stderr=subprocess.PIPE,
825
- text=True,
826
- timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
827
- )
828
- except Exception as e:
829
- print(f"git lfs pull 执行异常: {e}")
830
-
831
- zip_path = find_existing_offline_zip()
832
- if zip_path:
833
- print(f"ModelScope 整库克隆完成,找到 offline_build 压缩包: {zip_path}")
834
- return zip_path
835
-
836
- print("整库 git clone 完成,但未找到有效 offline_build 压缩包。")
837
-
838
- except subprocess.TimeoutExpired:
839
- print("ModelScope 整库 git clone 超时,继续尝试下一个方式。")
840
- except Exception as e:
841
- print(f"ModelScope 整库 git clone 异常: {e}")
842
-
843
- return None
844
-
845
 
846
- def download_whole_forgeui_repo_with_modelscope_cli():
847
  if not install_modelscope_cli():
848
  return None
849
 
 
 
850
  commands = [
851
  (
852
  [
@@ -854,11 +570,12 @@ def download_whole_forgeui_repo_with_modelscope_cli():
854
  "download",
855
  "--model",
856
  MODELSCOPE_MODEL_ID,
 
857
  "--local_dir",
858
- MODELSCOPE_DOWNLOAD_DIR
859
  ],
860
- False,
861
- "整库直连"
862
  ),
863
  (
864
  [
@@ -866,13 +583,12 @@ def download_whole_forgeui_repo_with_modelscope_cli():
866
  "download",
867
  "--model",
868
  MODELSCOPE_MODEL_ID,
 
869
  "--local_dir",
870
- MODELSCOPE_DOWNLOAD_DIR,
871
- "--endpoint",
872
- "https://www.modelscope.cn"
873
  ],
874
  False,
875
- "整库直连+显式 endpoint"
876
  ),
877
  (
878
  [
@@ -881,16 +597,28 @@ def download_whole_forgeui_repo_with_modelscope_cli():
881
  "--model",
882
  MODELSCOPE_MODEL_ID,
883
  "--local_dir",
884
- MODELSCOPE_DOWNLOAD_DIR
885
  ],
886
  True,
887
- "整库代理"
 
 
 
 
 
 
 
 
 
 
 
 
888
  )
889
  ]
890
 
891
  for command, use_proxy, mode_name in commands:
892
  try:
893
- print(f"执行 ModelScope CLI 整库下载 [{mode_name}]: " + " ".join(command))
894
 
895
  result = subprocess.run(
896
  command,
@@ -901,129 +629,122 @@ def download_whole_forgeui_repo_with_modelscope_cli():
901
  timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
902
  )
903
 
904
- output_text = ""
905
  if result.stdout.strip():
906
- output_text += result.stdout + "\n"
907
  print(result.stdout[-2000:])
908
  if result.stderr.strip():
909
- output_text += result.stderr + "\n"
910
  print(result.stderr[-4000:])
911
 
912
- zip_path = find_existing_offline_zip()
913
- if result.returncode == 0 and zip_path:
914
- print(f"ModelScope CLI 整库下载完成,找到 offline_build 压缩包: {zip_path}")
915
- return zip_path
916
 
917
- # 这里不再按文件名调用 modelscope download,只在 CLI 日志暴露 OSS 直链时做公网地址回退。
918
- fallback_zip = try_public_oss_fallback(output_text, "offlinbuild.zip")
919
- if fallback_zip and is_valid_zip(fallback_zip):
920
- print(f"offline_build 压缩包通过公网 OSS 回退下载成功: {fallback_zip}")
921
  return fallback_zip
922
 
923
- print("本次整库下载未找到有效 offline_build 压缩包,继续尝试下一个命令。")
 
 
 
924
 
925
  except subprocess.TimeoutExpired:
926
- print("ModelScope CLI 整库下载超时,继续尝试下一个命令。")
927
  except Exception as e:
928
- print(f"ModelScope CLI 整库下载异常: {e}")
929
 
 
930
  return None
931
 
932
 
933
- def download_offline_build_from_modelscope():
934
- os.makedirs(MODELSCOPE_DOWNLOAD_DIR, exist_ok=True)
935
- os.makedirs(MODELSCOPE_CACHE_DIR, exist_ok=True)
936
-
937
- existing_zip = find_existing_offline_zip()
938
- if existing_zip:
939
- print(f"检测到已有 offline_build 压缩包: {existing_zip}")
940
- return existing_zip
941
-
942
- print(f"开始从 ModelScope 整库克隆/下载 {MODELSCOPE_MODEL_ID}。")
943
-
944
- zip_path = clone_whole_forgeui_repo_with_git()
945
- if zip_path:
946
- return zip_path
947
-
948
- print("git 整库克隆未成功,改用 ModelScope CLI 整库下载。")
949
-
950
- zip_path = download_whole_forgeui_repo_with_modelscope_cli()
951
- if zip_path:
952
- return zip_path
953
 
954
- print("ModelScope 整库克隆/下载失败,未找到有效 offline_build 压缩包。")
955
- return None
956
 
 
 
 
 
 
957
 
958
- def locate_offline_build_dir():
959
- expected_main = os.path.join(OFFLINE_BUILD_DIR, "ComfyUI", "main.py")
960
- if os.path.exists(expected_main):
961
- return OFFLINE_BUILD_DIR
962
 
963
- for root, dirs, files in os.walk(REPO_PATH):
964
- if "main.py" in files and os.path.basename(root) == "ComfyUI":
965
- parent_dir = os.path.dirname(root)
966
- venv_activate = os.path.join(parent_dir, "venv", "bin", "activate")
967
- if os.path.exists(venv_activate):
968
- return parent_dir
969
 
970
- return None
 
 
971
 
972
 
973
- def extract_offline_build(zip_path):
974
- existing_dir = locate_offline_build_dir()
975
  if existing_dir:
976
- print(f"检测到已解压的 offline_build: {existing_dir}")
977
  return existing_dir
978
 
979
  if not zip_path or not is_valid_zip(zip_path):
980
- print(f"offline_build 压缩包无效: {zip_path}")
981
  return None
982
 
983
  try:
984
- print(f"正在解压 offline_build: {zip_path}")
 
 
 
985
 
986
- with zipfile.ZipFile(zip_path, "r") as zf:
987
- zf.extractall(REPO_PATH)
 
988
 
989
- extracted_dir = locate_offline_build_dir()
990
- if extracted_dir:
991
- print(f"offline_build 解压完成: {extracted_dir}")
992
- return extracted_dir
993
 
994
- print("解压完成,但未找到 offline_build/ComfyUI/main.py。")
995
- return None
 
 
 
 
 
 
996
 
997
  except Exception as e:
998
- print(f"解压 offline_build 失败: {e}")
999
  return None
1000
 
1001
 
1002
- def prepare_offline_build():
1003
- existing_dir = locate_offline_build_dir()
1004
  if existing_dir:
1005
- print(f"offline_build 已存在,跳过下载解压: {existing_dir}")
1006
  return existing_dir
1007
 
1008
- zip_path = download_offline_build_from_modelscope()
1009
  if not zip_path:
1010
  return None
1011
 
1012
- return extract_offline_build(zip_path)
1013
 
1014
 
1015
  def start_comfyui_process():
1016
- offline_build_dir = prepare_offline_build()
1017
 
1018
- if not offline_build_dir:
1019
- print("offline_build 准备失败,跳过 ComfyUI 启动。")
1020
  return
1021
 
1022
- venv_activate = os.path.join(offline_build_dir, "venv", "bin", "activate")
1023
- comfyui_main = os.path.join(offline_build_dir, "ComfyUI", "main.py")
 
1024
 
1025
- if not os.path.exists(venv_activate):
1026
- print(f"未找到虚拟环境激活脚本: {venv_activate}")
1027
  return
1028
 
1029
  if not os.path.exists(comfyui_main):
@@ -1032,9 +753,11 @@ def start_comfyui_process():
1032
 
1033
  configure_git_proxy()
1034
 
 
 
1035
  command = f'''
1036
  set -e
1037
- source "{venv_activate}"
1038
  export http_proxy={PROXY_URL}
1039
  export https_proxy={PROXY_URL}
1040
  export HTTP_PROXY={PROXY_URL}
@@ -1044,8 +767,8 @@ export no_proxy=localhost,127.0.0.1
1044
  export NO_PROXY=localhost,127.0.0.1
1045
  git config --global http.proxy {PROXY_URL}
1046
  git config --global https.proxy {PROXY_URL}
1047
- cd "{os.path.join(offline_build_dir, "ComfyUI")}"
1048
- python main.py --listen 0.0.0.0 --port {COMFYUI_PORT}
1049
  '''
1050
 
1051
  try:
@@ -1054,7 +777,7 @@ python main.py --listen 0.0.0.0 --port {COMFYUI_PORT}
1054
 
1055
  process = subprocess.Popen(
1056
  ["bash", "-lc", command],
1057
- cwd=offline_build_dir,
1058
  env=get_proxy_env(),
1059
  stdout=log_file,
1060
  stderr=subprocess.STDOUT
 
6
  import sys
7
  import time
8
  import zipfile
 
9
  import requests
10
  import gradio as gr
11
 
 
17
  CURRENT_DIR = REPO_PATH
18
  LAST_OUTPUT = ""
19
 
20
+ # ===== ModelScope 下载 comfy_portable.zip 并启动 ComfyUI 的配置 =====
21
  MODELSCOPE_MODEL_ID = "HOPStudio/forgeui"
22
+ COMFY_PORTABLE_ZIP_NAME = "comfy_portable.zip"
23
+
24
+ # 便携整合包固定路径:下载到 /root/comfy_portable.zip,解压后得到 /root/comfy_portable。
25
+ # 运行时不使用 venv,只调用整合包内置 Python。
26
+ COMFY_PORTABLE_ZIP_PATH = "/root/comfy_portable.zip"
27
+ COMFY_PORTABLE_DIR = "/root/comfy_portable"
28
+ COMFY_PORTABLE_PYTHON = os.path.join(COMFY_PORTABLE_DIR, "python", "bin", "python")
29
+ COMFYUI_DIR = os.path.join(COMFY_PORTABLE_DIR, "ComfyUI")
30
+ COMFYUI_MAIN = os.path.join(COMFYUI_DIR, "main.py")
31
+
32
+ MODELSCOPE_CACHE_DIR = "/root/modelscope_cache"
33
  COMFYUI_PORT = 8188
34
  COMFYUI_LOG_PATH = os.path.join(REPO_PATH, "comfyui.log")
35
  PROXY_URL = "http://103.71.69.203:17890"
36
  MODELSCOPE_DOWNLOAD_TIMEOUT = 21600
37
 
 
 
 
 
 
 
 
38
 
39
 
40
  def clone_repository():
 
390
 
391
 
392
 
393
+ # ===== ModelScope 下载 comfy_portable.zip,解压用内置 Python 启动 ComfyUI =====
394
  def get_proxy_env():
395
  env = os.environ.copy()
396
 
 
405
  return env
406
 
407
 
408
+ def get_modelscope_download_env(use_proxy=True):
409
  env = os.environ.copy()
410
 
 
411
  os.makedirs(MODELSCOPE_CACHE_DIR, exist_ok=True)
412
  env["MODELSCOPE_CACHE"] = MODELSCOPE_CACHE_DIR
413
  env["MODELSCOPE_ENDPOINT"] = "https://www.modelscope.cn"
414
  env["MODELSCOPE_SDK_ENDPOINT"] = "https://www.modelscope.cn"
415
 
 
 
 
 
 
 
 
 
 
 
416
  if use_proxy:
417
  env["http_proxy"] = PROXY_URL
418
  env["https_proxy"] = PROXY_URL
 
421
  env["ALL_PROXY"] = PROXY_URL
422
  env["all_proxy"] = PROXY_URL
423
  else:
424
+ for key in [
425
+ "http_proxy",
426
+ "https_proxy",
427
+ "HTTP_PROXY",
428
+ "HTTPS_PROXY",
429
+ "ALL_PROXY",
430
+ "all_proxy"
431
+ ]:
432
  env.pop(key, None)
433
 
434
+ env["no_proxy"] = "localhost,127.0.0.1"
435
+ env["NO_PROXY"] = "localhost,127.0.0.1"
 
436
 
437
  return env
438
 
 
467
  return False
468
 
469
  with zipfile.ZipFile(path, "r") as zf:
 
470
  return len(zf.namelist()) > 0
471
  except Exception:
472
  return False
473
 
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
  def install_modelscope_cli():
476
  if shutil.which("modelscope"):
477
  print("检测到 modelscope 命令。")
 
504
  try:
505
  result = subprocess.run(
506
  command,
507
+ env=get_proxy_env(),
508
  stdout=subprocess.PIPE,
509
  stderr=subprocess.PIPE,
510
  text=True,
 
526
  except Exception as e:
527
  print(f"modelscope 安装异常: {e}")
528
 
529
+ print("modelscope 不可用,无法继续下载 comfy_portable.zip。")
530
  return False
531
 
532
 
533
+ def locate_comfy_portable_dir():
534
+ if os.path.exists(COMFY_PORTABLE_PYTHON) and os.path.exists(COMFYUI_MAIN):
535
+ return COMFY_PORTABLE_DIR
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
537
+ # 兼容 zip 解压后多套了一层目录的情况。
538
+ root_dir = "/root"
539
  try:
540
+ for root, dirs, files in os.walk(root_dir):
541
+ if root.count(os.sep) - root_dir.count(os.sep) > 4:
542
+ dirs[:] = []
543
+ continue
544
 
545
+ candidate_python = os.path.join(root, "python", "bin", "python")
546
+ candidate_main = os.path.join(root, "ComfyUI", "main.py")
 
 
 
 
 
 
 
 
 
 
 
547
 
548
+ if os.path.exists(candidate_python) and os.path.exists(candidate_main):
549
+ return root
 
 
 
 
 
 
 
 
550
  except Exception as e:
551
+ print(f"定位 comfy_portable 目录异常: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
 
553
  return None
554
 
555
 
556
+ def download_comfy_portable_from_modelscope():
557
+ if is_valid_zip(COMFY_PORTABLE_ZIP_PATH):
558
+ print(f"检测到已有 comfy_portable.zip: {COMFY_PORTABLE_ZIP_PATH}")
559
+ return COMFY_PORTABLE_ZIP_PATH
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
 
 
561
  if not install_modelscope_cli():
562
  return None
563
 
564
+ os.makedirs(MODELSCOPE_CACHE_DIR, exist_ok=True)
565
+
566
  commands = [
567
  (
568
  [
 
570
  "download",
571
  "--model",
572
  MODELSCOPE_MODEL_ID,
573
+ COMFY_PORTABLE_ZIP_NAME,
574
  "--local_dir",
575
+ "/root"
576
  ],
577
+ True,
578
+ "代理下载"
579
  ),
580
  (
581
  [
 
583
  "download",
584
  "--model",
585
  MODELSCOPE_MODEL_ID,
586
+ COMFY_PORTABLE_ZIP_NAME,
587
  "--local_dir",
588
+ "/root"
 
 
589
  ],
590
  False,
591
+ "直连下载"
592
  ),
593
  (
594
  [
 
597
  "--model",
598
  MODELSCOPE_MODEL_ID,
599
  "--local_dir",
600
+ "/root/modelscope_download"
601
  ],
602
  True,
603
+ "整库代理下载"
604
+ ),
605
+ (
606
+ [
607
+ "modelscope",
608
+ "download",
609
+ "--model",
610
+ MODELSCOPE_MODEL_ID,
611
+ "--local_dir",
612
+ "/root/modelscope_download"
613
+ ],
614
+ False,
615
+ "整库直连下载"
616
  )
617
  ]
618
 
619
  for command, use_proxy, mode_name in commands:
620
  try:
621
+ print(f"执行 ModelScope 下载 [{mode_name}]: " + " ".join(command))
622
 
623
  result = subprocess.run(
624
  command,
 
629
  timeout=MODELSCOPE_DOWNLOAD_TIMEOUT
630
  )
631
 
 
632
  if result.stdout.strip():
 
633
  print(result.stdout[-2000:])
634
  if result.stderr.strip():
 
635
  print(result.stderr[-4000:])
636
 
637
+ if is_valid_zip(COMFY_PORTABLE_ZIP_PATH):
638
+ print(f"comfy_portable.zip 下载完成: {COMFY_PORTABLE_ZIP_PATH}")
639
+ return COMFY_PORTABLE_ZIP_PATH
 
640
 
641
+ fallback_zip = os.path.join("/root/modelscope_download", COMFY_PORTABLE_ZIP_NAME)
642
+ if is_valid_zip(fallback_zip):
643
+ print(f"在整库下载目录中找到 comfy_portable.zip: {fallback_zip}")
 
644
  return fallback_zip
645
 
646
+ if result.returncode != 0:
647
+ print("本次 ModelScope 下载失败,继续尝试下一个方式。")
648
+ else:
649
+ print("本次 ModelScope 下载完成,但未找到有效 comfy_portable.zip。")
650
 
651
  except subprocess.TimeoutExpired:
652
+ print("ModelScope 下载超时,继续尝试下一个方式。")
653
  except Exception as e:
654
+ print(f"ModelScope 下载异常: {e}")
655
 
656
+ print("ModelScope 下载失败,未找到有效 comfy_portable.zip。")
657
  return None
658
 
659
 
660
+ def safe_extract_zip(zip_path, extract_root):
661
+ if not is_valid_zip(zip_path):
662
+ print(f"zip 文件无效: {zip_path}")
663
+ return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
 
665
+ try:
666
+ extract_root_abs = os.path.abspath(extract_root)
667
 
668
+ with zipfile.ZipFile(zip_path, "r") as zf:
669
+ for member in zf.infolist():
670
+ target_path = os.path.abspath(os.path.join(extract_root_abs, member.filename))
671
+ if not target_path.startswith(extract_root_abs + os.sep) and target_path != extract_root_abs:
672
+ raise RuntimeError(f"zip 中存在不安全路径: {member.filename}")
673
 
674
+ zf.extractall(extract_root_abs)
 
 
 
675
 
676
+ return True
 
 
 
 
 
677
 
678
+ except Exception as e:
679
+ print(f"解压 zip 失败: {e}")
680
+ return False
681
 
682
 
683
+ def extract_comfy_portable(zip_path):
684
+ existing_dir = locate_comfy_portable_dir()
685
  if existing_dir:
686
+ print(f"检测到已解压的 comfy_portable: {existing_dir}")
687
  return existing_dir
688
 
689
  if not zip_path or not is_valid_zip(zip_path):
690
+ print(f"comfy_portable.zip 无效: {zip_path}")
691
  return None
692
 
693
  try:
694
+ if os.path.exists(COMFY_PORTABLE_DIR):
695
+ backup_dir = COMFY_PORTABLE_DIR + f".bak_{int(time.time())}"
696
+ shutil.move(COMFY_PORTABLE_DIR, backup_dir)
697
+ print(f"检测到不完整 comfy_portable 目录,已备份为: {backup_dir}")
698
 
699
+ print(f"正在解压 comfy_portable.zip: {zip_path}")
700
+ if not safe_extract_zip(zip_path, "/root"):
701
+ return None
702
 
703
+ portable_dir = locate_comfy_portable_dir()
704
+ if not portable_dir:
705
+ print("解压完成,但未找到 python/bin/python 和 ComfyUI/main.py。")
706
+ return None
707
 
708
+ python_path = os.path.join(portable_dir, "python", "bin", "python")
709
+ try:
710
+ subprocess.run(["chmod", "+x", python_path], check=False)
711
+ except Exception:
712
+ pass
713
+
714
+ print(f"comfy_portable 解压完成: {portable_dir}")
715
+ return portable_dir
716
 
717
  except Exception as e:
718
+ print(f"准备 comfy_portable 失败: {e}")
719
  return None
720
 
721
 
722
+ def prepare_comfy_portable():
723
+ existing_dir = locate_comfy_portable_dir()
724
  if existing_dir:
725
+ print(f"comfy_portable 已存在,跳过下载解压: {existing_dir}")
726
  return existing_dir
727
 
728
+ zip_path = download_comfy_portable_from_modelscope()
729
  if not zip_path:
730
  return None
731
 
732
+ return extract_comfy_portable(zip_path)
733
 
734
 
735
  def start_comfyui_process():
736
+ portable_dir = prepare_comfy_portable()
737
 
738
+ if not portable_dir:
739
+ print("comfy_portable 准备失败,跳过 ComfyUI 启动。")
740
  return
741
 
742
+ python_path = os.path.join(portable_dir, "python", "bin", "python")
743
+ comfyui_dir = os.path.join(portable_dir, "ComfyUI")
744
+ comfyui_main = os.path.join(comfyui_dir, "main.py")
745
 
746
+ if not os.path.exists(python_path):
747
+ print(f"未找到便携 Python: {python_path}")
748
  return
749
 
750
  if not os.path.exists(comfyui_main):
 
753
 
754
  configure_git_proxy()
755
 
756
+ python_lib_dir = os.path.join(portable_dir, "python", "lib")
757
+
758
  command = f'''
759
  set -e
760
+ export LD_LIBRARY_PATH="{python_lib_dir}:${{LD_LIBRARY_PATH:-}}"
761
  export http_proxy={PROXY_URL}
762
  export https_proxy={PROXY_URL}
763
  export HTTP_PROXY={PROXY_URL}
 
767
  export NO_PROXY=localhost,127.0.0.1
768
  git config --global http.proxy {PROXY_URL}
769
  git config --global https.proxy {PROXY_URL}
770
+ cd "{comfyui_dir}"
771
+ "{python_path}" main.py --listen 0.0.0.0 --port {COMFYUI_PORT}
772
  '''
773
 
774
  try:
 
777
 
778
  process = subprocess.Popen(
779
  ["bash", "-lc", command],
780
+ cwd=portable_dir,
781
  env=get_proxy_env(),
782
  stdout=log_file,
783
  stderr=subprocess.STDOUT