viyi commited on
Commit
4ab8f12
1 Parent(s): bb4b3ed

Upload 2 files

Browse files
Files changed (2) hide show
  1. config.py +176 -0
  2. sdwui_install.py +764 -0
config.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ 文件路径说明:
3
+
4
+ - []内的是下载文件的目标目录,可以是相对目录也可以是觉得路径
5
+ - []的下一行就是文件列表,可以是下载地址、git仓库、文件路径、文件夹路径,且支持通配符
6
+ - 如果需要对下载的文件重命名,可以在下载链接前面写上文件名后加一个:分开文件名和下载地址
7
+ - 如果需要下载到其他目录,可以使用同样的格式写其他目录
8
+ '''
9
+
10
+ # 这个列表仅加载一次 且会等待加载完成再开始安装sd
11
+ before_downloading = '''
12
+ [extensions] # 插件
13
+ https://github.com/dtlnor/stable-diffusion-webui-localization-zh_CN.git
14
+ https://github.com/AlUlkesh/stable-diffusion-webui-images-browser.git
15
+ https://github.com/DominikDoom/a1111-sd-webui-tagcomplete.git
16
+ https://github.com/Mikubill/sd-webui-controlnet.git
17
+ https://github.com/LianZiZhou/sd-webui-pixink-console.git
18
+ https://github.com/ilian6806/stable-diffusion-webui-state.git
19
+ https://github.com/pkuliyi2015/multidiffusion-upscaler-for-automatic1111.git
20
+ https://github.com/Bing-su/adetailer.git
21
+ https://github.com/civitai/sd_civitai_extension.git
22
+ https://github.com/zanllp/sd-webui-infinite-image-browsing.git
23
+ https://github.com/viyiviyi/stable-diffusion-webui-zoomimage.git
24
+
25
+ # 如果你有模型文件需要在启动前加载,可以写在这个下面对应位置
26
+
27
+ [models/Stable-diffusion] # 大模型列表
28
+
29
+ [models/hypernetworks] # hypernetworks文件列表
30
+
31
+ [models/embeddings] # embeddings文件列表
32
+
33
+ [models/Lora] # Lora文件列表
34
+
35
+ [models/VAE] # VAE文件列表
36
+
37
+ [extensions/sd-webui-controlnet/models] # controlnet插件的模型列表
38
+
39
+ '''
40
+
41
+ # 这个列表仅加载一次 且不会等待加载完成
42
+ async_downloading='''
43
+ [extensions] # 插件 如果你没有使用ngrok或者frpc,请不要把插件放在这里加载,因为这里的文件可能在webui启动后才加载完成
44
+
45
+ [models/Stable-diffusion] # 大模型列表
46
+ https://huggingface.co/viyi/testing_models/resolve/main/mg-LBG.safetensors
47
+ 容华_国风_SDXL.safetensors:https://civitai.com/api/download/models/151978
48
+
49
+ [models/hypernetworks] # hypernetworks文件列表
50
+
51
+ [models/embeddings] # embeddings文件列表
52
+
53
+ [models/Lora] # Lora文件列表
54
+ Genshin_Impact_all-in-one.safetensors:https://civitai.com/api/download/models/116970
55
+ https://civitai.com/api/download/models/117151 # Clothing +/- Adjuster 衣物增/减 LoRA
56
+ https://civitai.com/api/download/models/62833 # Detail Tweaker LoRA (细节调整LoRA)
57
+
58
+ [models/VAE] # VAE文件列表
59
+ {input_path}/vae-ft-ema-prunedsafetensors/vae-ft-ema-560000-ema-pruned.safetensors
60
+ {input_path}/vae-ft-ema-prunedsafetensors/vae-ft-mse-840000-ema-pruned.safetensors
61
+ https://huggingface.co/stabilityai/sd-vae-ft-ema-original/resolve/main/vae-ft-ema-560000-ema-pruned.safetensors
62
+ https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt
63
+ sdxl_vae.safetensors:https://civitai.com/api/download/models/130720?type=VAE # sdxl模型需要sdxl的vae
64
+
65
+ [extensions/sd-webui-controlnet/models] # controlnet插件的模型列表
66
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_ip2p_fp16.safetensors
67
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_shuffle_fp16.safetensors
68
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11f1e_sd15_tile_fp16.safetensors
69
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11f1p_sd15_depth_fp16.safetensors
70
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_canny_fp16.safetensors
71
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_inpaint_fp16.safetensors
72
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_lineart_fp16.safetensors
73
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_mlsd_fp16.safetensors
74
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_normalbae_fp16.safetensors
75
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_openpose_fp16.safetensors
76
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_scribble_fp16.safetensors
77
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_seg_fp16.safetensors
78
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_softedge_fp16.safetensors
79
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15s2_lineart_anime_fp16.safetensors
80
+ https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11u_sd15_tile_fp16.safetensors
81
+ '''
82
+
83
+ # 这个列表每次 启动都会加载一次,且一定按照顺序加载
84
+ before_start_sync_downloading = '''
85
+
86
+ # 如果你需要每次启动都加载一下文件,可以写在这。(比如测试路径是否正确的时候)
87
+
88
+ [models/Stable-diffusion] # 大模型列表
89
+
90
+ [models/hypernetworks] # hypernetworks文件列表
91
+
92
+ [models/embeddings] # embeddings文件列表
93
+
94
+ [models/Lora] # Lora文件列表
95
+
96
+ [models/VAE] # VAE文件列表
97
+
98
+ [extensions/sd-webui-controlnet/models] # controlnet插件的模型列表
99
+
100
+ '''
101
+
102
+ # sd启动参数
103
+ sd_start_args='''
104
+ # --ckpt=mg-Tender.safetensors # 默认模型名称,路径不能包含空格
105
+ --disable-safe-unpickle
106
+ --deepdanbooru
107
+ --no-hashing
108
+ --no-download-sd-model
109
+ --administrator
110
+ --skip-torch-cuda-test
111
+ --skip-version-check
112
+ --disable-nan-check
113
+ # --opt-sdp-attention
114
+ --opt-sdp-no-mem-attention
115
+ --xformers-flash-attention
116
+ --xformers
117
+ --api
118
+ --listen
119
+ --lowram
120
+ --no-gradio-queue
121
+ --share
122
+ --disable-console-progressbars
123
+ --no-half-vae
124
+ # --no-half #关闭半精度
125
+ # --enable-console-prompts
126
+ # --nowebui
127
+ # --api-auth=2333:6666 # api密码
128
+ # --gradio-auth=2333:6666 # webui密码
129
+ '''
130
+
131
+ useGooglrDrive = True # 连接到谷歌云盘 在google colab环境才会生效
132
+ # 启用Ngrok 如果没有配置文件即使开启也无效
133
+ useNgrok=True
134
+ # 启用frp 如果没有配置文件即使开启也无效
135
+ useFrpc=True
136
+
137
+ #文件或直接填配置
138
+ ngrok_config_or_file = '''
139
+ {input_path}/configs/ngrok_token.txt
140
+ '''
141
+ frp_config_or_file = '''
142
+ {input_path}/configs/frpc_litechat.ini
143
+ '''
144
+ frp_ssl_dir = '''
145
+ {input_path}/configs/litechat_nginx
146
+ '''
147
+
148
+ # 配置启动参数
149
+ server_port=7860 # webui 默认端口
150
+
151
+ # 仓库地址 这是修改过界面布局顺序的webui,不定期同步到官方版本
152
+ # 如果要使用官方版本,改成这个: https://github.com/AUTOMATIC1111/stable-diffusion-webui
153
+ sd_git_repo='https://github.com/viyiviyi/stable-diffusion-webui.git -b local'
154
+ # 配置文件,包括webui的设置和UI默认值,如果要自定义,fork这个仓库后修改并把地址替换这个地址
155
+ sd_config_git_repu = 'https://github.com/viyiviyi/sd-configs.git'
156
+ # 设置文件保存路径 当使用谷歌云盘时非常有用
157
+ setting_file = '{output_path}/configs/config.json'
158
+ ui_config_file = '{output_path}/configs/ui-config.json'
159
+
160
+ git_proxy="https://ghproxy.com/"
161
+
162
+ link_instead_of_copy = True # 下载或加载Input的文件时是使用链接还是复制的方式加载到目标目录
163
+ hidden_console_info = False # 是否隐藏大部分的控制台内容
164
+
165
+ reLoad = True
166
+ # 如果需要重新安装,请注释下面这一行
167
+ reLoad = False
168
+
169
+ skip_start = False
170
+
171
+ # 输出目录 如果要指定在执行目录外的目录,请写绝对路径
172
+ # 如果需要将输出的图片指定到其他目录,可以设置这里的值
173
+ output_path = '/root/autodl-fs/sd_output'
174
+
175
+ input_path = '/root/autodl-fs/sd_input'
176
+ # 安装路径说明,会安装在运行 sdwui_install 时的目录
sdwui_install.py ADDED
@@ -0,0 +1,764 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # coding: utf-8
3
+
4
+ from pathlib import Path
5
+ import os
6
+ import time
7
+ import re
8
+ import subprocess
9
+ import threading
10
+ import sys
11
+ import socket
12
+ from typing import List
13
+ import config
14
+
15
+ def mkdirs(path, exist_ok=True):
16
+ if path and not Path(path).exists():
17
+ os.makedirs(path,exist_ok=exist_ok)
18
+
19
+ # 内置参数默认值,当上下文有参数时可覆盖默认值
20
+
21
+ _useFrpc = False
22
+ if 'useFrpc' in locals() or 'useFrpc' in globals():
23
+ _useFrpc = config.useFrpc
24
+
25
+ _useNgrok = False
26
+ if 'useNgrok' in locals() or 'useNgrok' in globals():
27
+ _useNgrok = config.useNgrok
28
+
29
+ _reLoad = False
30
+ if 'reLoad' in locals() or 'reLoad' in globals():
31
+ _reLoad = config.reLoad
32
+
33
+ _before_downloading = ''
34
+ if 'before_downloading' in locals() or 'before_downloading' in globals():
35
+ _before_downloading = config.before_downloading
36
+
37
+ _async_downloading = ''
38
+ if 'async_downloading' in locals() or 'async_downloading' in globals():
39
+ _async_downloading = config.async_downloading
40
+
41
+ _before_start_sync_downloading = ''
42
+ if 'before_start_sync_downloading' in locals() or 'before_start_sync_downloading' in globals():
43
+ _before_start_sync_downloading = config.before_start_sync_downloading
44
+
45
+ _server_port = 7860
46
+ if 'server_port' in locals() or 'server_port' in globals():
47
+ _server_port = config.server_port
48
+
49
+ _sd_git_repo ='https://github.com/viyiviyi/stable-diffusion-webui.git -b local'
50
+ if 'sd_git_repo' in locals() or 'sd_git_repo' in globals():
51
+ _sd_git_repo = config.sd_git_repo\
52
+ .replace('{sdwui}','stable-diffusion-webui')\
53
+ .replace('{wui}',"webui")
54
+
55
+ _sd_config_git_repu = 'https://github.com/viyiviyi/sd-configs.git'
56
+ if 'sd_config_git_repu' in locals() or 'sd_config_git_repu' in globals():
57
+ _sd_config_git_repu = config.sd_config_git_repu\
58
+ .replace('{sdwui}','stable-diffusion-webui')\
59
+ .replace('{wui}',"webui")
60
+
61
+ _link_instead_of_copy = True
62
+ if 'link_instead_of_copy' in locals() or 'link_instead_of_copy' in globals():
63
+ _link_instead_of_copy = config.link_instead_of_copy
64
+
65
+ show_shell_info = False
66
+ if 'hidden_console_info' in locals() or 'hidden_console_info' in globals():
67
+ show_shell_info = not config.hidden_console_info
68
+
69
+ _skip_start = False
70
+ if 'skip_start' in locals() or 'skip_start' in globals():
71
+ _skip_start = config.skip_start
72
+
73
+ run_by_none_device = False
74
+
75
+
76
+ def run(command, cwd=None, desc=None, errdesc=None, custom_env=None,try_error:bool=True) -> str:
77
+ global show_shell_info
78
+ if desc is not None:
79
+ print(desc)
80
+
81
+ run_kwargs = {
82
+ "args": command,
83
+ "shell": True,
84
+ "cwd": cwd,
85
+ "env": os.environ if custom_env is None else custom_env,
86
+ "encoding": 'utf8',
87
+ "errors": 'ignore',
88
+ }
89
+
90
+ if not show_shell_info:
91
+ run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE
92
+
93
+ result = subprocess.run(**run_kwargs)
94
+
95
+ if result.returncode != 0:
96
+ error_bits = [
97
+ f"{errdesc or 'Error running command'}.",
98
+ f"Command: {command}",
99
+ f"Error code: {result.returncode}",
100
+ ]
101
+ if result.stdout:
102
+ error_bits.append(f"stdout: {result.stdout}")
103
+ if result.stderr:
104
+ error_bits.append(f"stderr: {result.stderr}")
105
+ if try_error:
106
+ print(RuntimeError("\n".join(error_bits)))
107
+ else:
108
+ raise RuntimeError("\n".join(error_bits))
109
+
110
+ if show_shell_info:
111
+ print(result.stdout or "")
112
+ return (result.stdout or "")
113
+
114
+
115
+ install_path=f"{os.getcwd()}/sdwui" # 安装目录
116
+ output_path= os.path.join(os.getcwd(), config.output_path or 'sdwui/Output')
117
+ input_path = config.input_path or f'{os.getcwd()}/sdwui/Input'
118
+
119
+ mkdirs(install_path,True)
120
+ mkdirs(output_path,True)
121
+
122
+ os.environ['install_path'] = install_path
123
+ os.environ['output_path'] = output_path
124
+ os.environ['input_path'] = input_path
125
+
126
+ def replace_path(input_str:str):
127
+ return input_str.replace('$install_path',install_path)\
128
+ .replace('{install_path}',install_path)\
129
+ .replace('$input_path',input_path)\
130
+ .replace('{input_path}',input_path)\
131
+ .replace('$output_path',output_path)\
132
+ .replace('{output_path}',output_path)\
133
+ .replace('{sdwui}','stable-diffusion-webui')\
134
+ .replace('{wui}',"webui")
135
+
136
+ space_string = ' \n\r\t\'\",'
137
+
138
+ def config_reader(conf:str):
139
+ args = [replace_path(item.split('#')[0].strip(space_string)) for item in conf.split('\n') if item.strip(space_string)]
140
+ return [item.strip() for item in args if item.strip()]
141
+
142
+
143
+ ngrokTokenFile = os.path.join(input_path,'configs/ngrok_token.txt') # 非必填 存放ngrokToken的文件的路径
144
+ frpcConfigFile = os.path.join(input_path,'configs/frpc_koishi.ini') # 非必填 frp 配置文件
145
+ # ss证书目录 下载nginx的版本,把pem格式改成crt格式
146
+ frpcSSLFFlies =[os.path.join(input_path,'configs/koishi_ssl')]
147
+ if 'frp_ssl_dir' in locals() or 'frp_ssl_dir' in globals():
148
+ frpcSSLFFlies = frpcSSLFFlies + config_reader(config.frp_ssl_dir)
149
+ # frpc 文件目录 如果目录不存在,会自动下载,也可以在数据集搜索 viyiviyi/utils 添加
150
+ frpcExePath = os.path.join(input_path,'utils-tools/frpc')
151
+ # 其他需要加载的webui启动参数 写到【参数列表】这个配置去
152
+ otherArgs = '--xformers'
153
+ if 'sd_start_args' in locals() or 'sd_start_args' in globals():
154
+ otherArgs = ' '.join([item for item in config_reader(config.sd_start_args) if item != '--no-gradio-queue'])
155
+ venvPath = os.path.join(input_path,'sd-webui-venv/venv.tar.bak') # 安装好的python环境 sd-webui-venv是一个公开是数据集 可以搜索添加
156
+
157
+ # 用于使用kaggle api的token文件 参考 https://www.kaggle.com/docs/api
158
+ # 此文件用于自动上传koishi的相关配置 也可以用于保存重要的输出文件
159
+ kaggleApiTokenFile = os.path.join(input_path,'configs/kaggle.json')
160
+
161
+ requirements = []
162
+
163
+ frpcStartArg = ''
164
+
165
+ _setting_file = ''
166
+ if 'setting_file' in locals() or 'setting_file' in globals():
167
+ _setting_file = replace_path(config.setting_file)
168
+ _ui_config_file = ''
169
+ if 'ui_config_file' in locals() or 'ui_config_file' in globals():
170
+ _ui_config_file = replace_path(config.ui_config_file)
171
+
172
+ mkdirs(f'{install_path}/configFiles',True)
173
+ if 'frp_config_or_file' in locals() or 'frp_config_or_file' in globals():
174
+ _frp_config_or_file = replace_path(config.frp_config_or_file)
175
+ if Path(_frp_config_or_file.strip()).exists():
176
+ frpcConfigFile = _frp_config_or_file.strip()
177
+ if not Path(frpcConfigFile).exists():
178
+ if _frp_config_or_file.strip().startswith('-f'):
179
+ frpcStartArg = _frp_config_or_file.strip()
180
+ else:
181
+ print('没有frpcp配置')
182
+ _useFrpc = False
183
+ else:
184
+ run(f'''cp -f {frpcConfigFile} {install_path}/configFiles/frpc_webui.ini''')
185
+ frpcConfigFile = f'{install_path}/configFiles/frpc_webui.ini'
186
+ run(f'''sed -i "s/local_port = .*/local_port = {_server_port}/g" {frpcConfigFile}''')
187
+ frpcStartArg = f' -c {frpcConfigFile}'
188
+
189
+ ngrokToken=''
190
+ if 'ngrok_config_or_file' in locals() or 'ngrok_config_or_file' in globals():
191
+ _ngrok_config_or_file = replace_path(config.ngrok_config_or_file)
192
+ if Path(_ngrok_config_or_file.strip()).exists():
193
+ ngrokTokenFile = _ngrok_config_or_file.strip()
194
+ if Path(ngrokTokenFile).exists():
195
+ with open(ngrokTokenFile,encoding = "utf-8") as nkfile:
196
+ ngrokToken = nkfile.readline()
197
+ elif not _ngrok_config_or_file.strip().startswith('/'):
198
+ ngrokToken=_ngrok_config_or_file.strip()
199
+
200
+ if not Path(venvPath).exists():
201
+ venvPath = os.path.join(input_path,'sd-webui-venv/venv.zip')
202
+
203
+
204
+ import concurrent.futures
205
+ import importlib
206
+ import os
207
+ import pprint
208
+ import re
209
+ from pathlib import Path
210
+ from typing import List
211
+
212
+ import requests
213
+
214
+ show_shell_info = False
215
+
216
+
217
+ def is_installed(package):
218
+ try:
219
+ spec = importlib.util.find_spec(package)
220
+ except ModuleNotFoundError:
221
+ return False
222
+
223
+ return spec is not None
224
+
225
+ def download_file(url:str, filename:str, dist_path:str, cache_path = '',_link_instead_of_copy:bool=True):
226
+ # 获取文件的真实文件名
227
+ if not filename:
228
+ with requests.get(url, stream=True) as r:
229
+ if 'Content-Disposition' in r.headers:
230
+ filename = r.headers['Content-Disposition'].split('filename=')[1].strip('"')
231
+ r.close()
232
+ if not filename and re.search(r'/[^/]+\.[^/]+$',url):
233
+ filename = url.split('/')[-1]
234
+
235
+ filename = re.sub(r'[\\/:*?"<>|;]', '', filename)
236
+ filename = re.sub(r'[\s\t]+', '_', filename)
237
+
238
+ if show_shell_info:
239
+ print(f'下载 {filename} url: {url} --> {dist_path}')
240
+
241
+ # 创建目录
242
+ if cache_path and not Path(cache_path).exists():
243
+ mkdirs(cache_path,exist_ok=True)
244
+ if dist_path and not Path(dist_path).exists():
245
+ mkdirs(dist_path,exist_ok=True)
246
+
247
+ # 拼接文件的完整路径
248
+ filepath = os.path.join(dist_path, filename)
249
+
250
+ if cache_path:
251
+ cache_path = os.path.join(cache_path, filename)
252
+
253
+ # 判断文件是否已存在
254
+ if Path(filepath).exists():
255
+ print(f'文件 {filename} 已存在 {dist_path}')
256
+ return
257
+
258
+ if cache_path and Path(cache_path).exists():
259
+ run(f'cp -n -r -f {"-s" if _link_instead_of_copy else ""} {cache_path} {dist_path}')
260
+ if show_shell_info:
261
+ print(f'文件缓存 {cache_path} --> {dist_path}')
262
+ return
263
+ # 下载文件
264
+ with requests.get(url, stream=True) as r:
265
+ r.raise_for_status()
266
+ with open(cache_path or filepath, 'wb') as f:
267
+ for chunk in r.iter_content(chunk_size=8192):
268
+ if chunk:
269
+ f.write(chunk)
270
+ # 如果使用了缓存目录 需要复制或链接文件到目标目录
271
+ if cache_path:
272
+ run(f'cp -n -r -f {"-s" if _link_instead_of_copy else ""} {cache_path} {dist_path}')
273
+ if show_shell_info:
274
+ print(f'下载完成 {filename} --> {dist_path}')
275
+
276
+ def download_git(url, dist_path, cache_path = '',_link_instead_of_copy:bool=True):
277
+ if not Path(dist_path).exists():
278
+ mkdirs(dist_path,exist_ok=True)
279
+ if show_shell_info:
280
+ print(f'git 下载 {url} --> {dist_path}')
281
+ if cache_path and not Path(cache_path).exists():
282
+ mkdirs(cache_path,exist_ok=True)
283
+ run(f'git clone {config.git_proxy}{url}',cwd = cache_path)
284
+ if cache_path:
285
+ run(f'cp -n -r -f {cache_path}/* {dist_path}')
286
+ else:
287
+ run(f'git clone {config.git_proxy}{url}',cwd = dist_path)
288
+ if show_shell_info:
289
+ print(f'git 下载完成 {url} --> {dist_path}')
290
+
291
+
292
+
293
+ # 加入文件到下载列表
294
+ def pause_url(url:str,dist_path:str):
295
+ file_name = ''
296
+ if re.match(r'^[^:]+:(https?|ftps?)://', url, flags=0):
297
+ file_name = re.findall(r'^[^:]+:',url)[0][:-1]
298
+ url = url[len(file_name)+1:]
299
+ if not re.match(r'^(https?|ftps?)://',url):
300
+ return
301
+ file_name = re.sub(r'\s+','_',file_name or '')
302
+ path_hash = str(hash(url)).replace('-','')
303
+
304
+ return {'file_name':file_name,'path_hash':path_hash,'url':url,'dist_path':dist_path}
305
+
306
+ def download_urls(download_list:List[dict],sync:bool=False,thread_num:int=5,
307
+ cache_path:str=os.path.join(os.getcwd(),'.cache','download_util'),
308
+ _link_instead_of_copy:bool=True,is_await:bool=False):
309
+ if sync:
310
+ for conf in download_list:
311
+ cache_dir = os.path.join(cache_path,conf['path_hash'])
312
+ if conf['url'].startswith('https://github.com'):
313
+ download_git(conf['url'],conf['dist_path'],cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy)
314
+ continue
315
+ download_file(conf['url'],conf['file_name'],conf['dist_path'],cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy)
316
+ else:
317
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=thread_num)
318
+ futures = []
319
+ for conf in download_list:
320
+ cache_dir = os.path.join(cache_path,conf['path_hash'])
321
+ if conf['url'].startswith('https://github.com'):
322
+ futures.append(executor.submit(download_git, conf['url'],conf['dist_path'],
323
+ cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy))
324
+ continue
325
+ futures.append(executor.submit(download_file, conf['url'],conf['file_name'],conf['dist_path'],
326
+ cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy))
327
+ if is_await:
328
+ concurrent.futures.wait(futures)
329
+
330
+
331
+ def parse_config(config:str):
332
+ space_string = ' \n\r\t\'\",'
333
+ other_flie_list = [item.split('#')[0].strip(space_string) for item in config.split('\n') if item.strip(space_string)]
334
+ other_flie_list = [item.strip() for item in other_flie_list if item.strip()]
335
+ other_flie_list_store = {}
336
+ other_flie_list_store_name='default'
337
+ other_flie_list_store_list_cache=[]
338
+
339
+ for item in other_flie_list:
340
+ if item.startswith('[') and item.endswith(']'):
341
+ if not other_flie_list_store_name == 'default':
342
+ other_flie_list_store[other_flie_list_store_name]=other_flie_list_store_list_cache
343
+ other_flie_list_store_list_cache = []
344
+ other_flie_list_store_name = item[1:-1]
345
+ else:
346
+ other_flie_list_store_list_cache.append(item)
347
+ other_flie_list_store[other_flie_list_store_name]=other_flie_list_store_list_cache
348
+
349
+ return other_flie_list_store
350
+
351
+
352
+ def link_or_download_flie(config:str, skip_url:bool=False, _link_instead_of_copy:bool=True, base_path:str = '',
353
+ sync:bool=False,thread_num:int=None, is_await:bool=False):
354
+ store:dict[str,List[str]] = parse_config(config)
355
+ download_list = []
356
+ for dist_dir in store.keys():
357
+ dist_path = os.path.join(base_path,dist_dir)
358
+ mkdirs(dist_path,exist_ok=True)
359
+ for path in store[dist_dir]:
360
+ if 'https://' in path or 'http://' in path:
361
+ if skip_url:
362
+ continue
363
+ if sync:
364
+ download_urls([pause_url(path,dist_path)],_link_instead_of_copy = _link_instead_of_copy, sync=sync)
365
+ continue
366
+ download_list.append(pause_url(path,dist_path))
367
+ else:
368
+ run(f'cp -n -r -f {"-s" if _link_instead_of_copy else ""} {path} {dist_path}')
369
+ if show_shell_info:
370
+ print(f'{"链接" if _link_instead_of_copy else "复制"} {path} --> {dist_path}')
371
+ run(f'rm -f {dist_path}/\*.* ')
372
+ if not skip_url:
373
+ if show_shell_info:
374
+ pprint.pprint(download_list)
375
+ download_urls(download_list,_link_instead_of_copy = _link_instead_of_copy, sync=sync, thread_num=thread_num or 2,is_await=is_await)
376
+
377
+
378
+ def echoToFile(content:str,path:str):
379
+ if path.find('/') >= 0:
380
+ _path = '/'.join(path.split('/')[:-1])
381
+ mkdirs(f'{_path}',True)
382
+ with open(path,'w') as sh:
383
+ sh.write(content)
384
+
385
+ def zipPath(path:str,zipName:str,format='tar'):
386
+ if path.startswith('$install_path'):
387
+ path = path.replace('$install_path',install_path)
388
+ if path.startswith('$output_path'):
389
+ path = path.replace('$install_path',output_path)
390
+ if not path.startswith('/'):
391
+ path = f'{install_path}/sd_main_dir/{path}'
392
+ if Path(path).exists():
393
+ if 'tar' == format:
394
+ run(f'tar -cf {output_path}/'+ zipName +'.tar -C '+ path +' . ')
395
+ elif 'gz' == format:
396
+ run(f'tar -czf {output_path}/'+ zipName +'.tar.gz -C '+ path +' . ')
397
+ return
398
+ print('指定的目录不存在:'+path)
399
+
400
+ # 检查网络
401
+ def check_service(host, port):
402
+ try:
403
+ socket.create_connection((host, port), timeout=5)
404
+ return True
405
+ except socket.error:
406
+ return False
407
+
408
+ # ngrok
409
+ def startNgrok(ngrokToken:str,ngrokLocalPort:int):
410
+ if not is_installed('pyngrok'):
411
+ run(f'pip install pyngrok')
412
+ from pyngrok import conf, ngrok
413
+ try:
414
+ conf.get_default().auth_token = ngrokToken
415
+ conf.get_default().monitor_thread = False
416
+ ssh_tunnels = ngrok.get_tunnels(conf.get_default())
417
+ if len(ssh_tunnels) == 0:
418
+ ssh_tunnel = ngrok.connect(ngrokLocalPort)
419
+ print('ngrok 访问地址:'+ssh_tunnel.public_url)
420
+ else:
421
+ print('ngrok 访问地址:'+ssh_tunnels[0].public_url)
422
+ except:
423
+ print('启动ngrok出错')
424
+
425
+ def startFrpc(name,configFile):
426
+ echoToFile(f'''
427
+ cd {install_path}/frpc/
428
+ {install_path}/frpc/frpc {configFile}
429
+ ''',f'{install_path}/frpc/start.sh')
430
+ os.system(f'''bash {install_path}/frpc/start.sh''')
431
+
432
+ def installProxyExe():
433
+ if _useFrpc:
434
+ print('安装frpc')
435
+ mkdirs(f'{install_path}/frpc',True)
436
+ if Path(frpcExePath).exists():
437
+ run(f'cp -f -n {frpcExePath} {install_path}/frpc/frpc')
438
+ else:
439
+ run(f'wget "https://huggingface.co/datasets/ACCA225/Frp/resolve/main/frpc" -O {install_path}/frpc/frpc')
440
+
441
+ for ssl in frpcSSLFFlies:
442
+ if Path(ssl).exists():
443
+ run(f'cp -f -n {ssl}/* {install_path}/frpc/')
444
+ run(f'chmod +x {install_path}/frpc/frpc')
445
+ run(f'{install_path}/frpc/frpc -v')
446
+ if _useNgrok and not is_installed('pyngrok'):
447
+ run('pip install pyngrok')
448
+
449
+ def startProxy():
450
+ if _useNgrok:
451
+ startNgrok(ngrokToken,_server_port)
452
+ if _useFrpc:
453
+ startFrpc('frpc_proxy',frpcStartArg)
454
+
455
+
456
+ # nginx 反向代理配置文件
457
+ def localProxy():
458
+ conf = '''
459
+ server
460
+ {
461
+ listen '''+str(_server_port)+''';
462
+ listen [::]:'''+str(_server_port)+''';
463
+ server_name 127.0.0.1 localhost 0.0.0.0 "";
464
+
465
+ if ($request_method = OPTIONS) {
466
+ return 200;
467
+ }
468
+ fastcgi_send_timeout 10m;
469
+ fastcgi_read_timeout 10m;
470
+ fastcgi_connect_timeout 10m;
471
+ location /1/
472
+ {
473
+ proxy_pass http://127.0.0.1:'''+str(_server_port+2)+'''/;
474
+ # add_header Set-Cookie "subpath=1; expires=0; Path=/";
475
+ # proxy_set_header Set-Cookie "subpath=1; expires=0; Path=/";
476
+ proxy_set_header Host $host;
477
+ proxy_set_header X-Real-IP $remote_addr;
478
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
479
+ proxy_set_header REMOTE-HOST $remote_addr;
480
+ proxy_set_header Upgrade $http_upgrade;
481
+ proxy_set_header Connection upgrade;
482
+ proxy_http_version 1.1;
483
+ proxy_connect_timeout 10m;
484
+ proxy_read_timeout 10m;
485
+ }
486
+ location /
487
+ {
488
+ set $proxy_url http://127.0.0.1:'''+str(_server_port+1)+''';
489
+ # if ($cookie_subpath = "1") {
490
+ # set $proxy_url http://127.0.0.1:'''+str(_server_port+2)+''';
491
+ # }
492
+ proxy_pass $proxy_url;
493
+ proxy_set_header Host $host;
494
+ proxy_set_header X-Real-IP $remote_addr;
495
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
496
+ proxy_set_header REMOTE-HOST $remote_addr;
497
+ proxy_set_header Upgrade $http_upgrade;
498
+ proxy_set_header Connection upgrade;
499
+ proxy_http_version 1.1;
500
+ proxy_connect_timeout 10m;
501
+ proxy_read_timeout 10m;
502
+ }
503
+ }
504
+ '''
505
+ echoToFile(conf,'/etc/nginx/conf.d/proxy_nginx.conf')
506
+ if not check_service('localhost',_server_port):
507
+ run(f'''nginx -c /etc/nginx/nginx.conf''')
508
+ os.system(f'''nginx -s reload''')
509
+
510
+
511
+ import inspect
512
+ import ctypes
513
+
514
+ def _async_raise(tid, exctype):
515
+ """raises the exception, performs cleanup if needed"""
516
+ tid = ctypes.c_long(tid)
517
+ if not inspect.isclass(exctype):
518
+ exctype = type(exctype)
519
+ res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
520
+ if res == 0:
521
+ raise ValueError("invalid thread id")
522
+ elif res != 1:
523
+ # """if it returns a number greater than one, you're in trouble,
524
+ # and you should call it again with exc=NULL to revert the effect"""
525
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
526
+ raise SystemError("PyThreadState_SetAsyncExc failed")
527
+
528
+ def stop_thread(thread):
529
+ _async_raise(thread.ident, SystemExit)
530
+
531
+ def stop_solo_threads():
532
+ # 获取当前所有活动的线程
533
+ threads = threading.enumerate()
534
+ # 关闭之前创建的子线程
535
+ for thread in threads:
536
+ if thread.name.startswith('solo_'):
537
+ print(f'结束线程:{thread.name}')
538
+ try:
539
+ stop_thread(thread)
540
+ except socket.error:
541
+ print(f'结束线程:{thread.name} 执行失败')
542
+
543
+
544
+ envInstalled=False
545
+ quickStart = True
546
+ #安装
547
+ def install():
548
+ print('安装')
549
+ os.chdir(f'''{install_path}''')
550
+ run(f'''git lfs install''')
551
+ run(f'''git config --global credential.helper store''')
552
+ for requirement in requirements:
553
+ run(f'pip install {requirement}')
554
+ if _reLoad:
555
+ run(f'''rm -rf sd_main_dir''')
556
+ if Path("sd_main_dir").exists():
557
+ os.chdir(f'''{install_path}/sd_main_dir/''')
558
+ run(f'''git checkout .''')
559
+ run(f'''git pull''')
560
+ else:
561
+ run(f'''git clone {config.git_proxy}{_sd_git_repo} sd_main_dir''')
562
+ os.chdir(f'''{install_path}/sd_main_dir''')
563
+ print('安装 完成')
564
+
565
+ # 链接输出目录
566
+ def link_dir():
567
+ print('链接输出目录')
568
+ # 链接图片输出目录
569
+ mkdirs(f'{output_path}/outputs',True)
570
+ run(f'''rm -rf {install_path}/sd_main_dir/outputs''')
571
+ run(f'''ln -s -r {output_path}/outputs {install_path}/sd_main_dir/''')
572
+ # 输出收藏目录
573
+ mkdirs(f'{output_path}/log',True)
574
+ run(f'''rm -rf {install_path}/sd_main_dir/log''')
575
+ run(f'''ln -s -r {output_path}/log {install_path}/sd_main_dir/''')
576
+ # 链接训练输出目录 文件夹链接会导致功能不能用
577
+ run(f'''rm -rf {install_path}/sd_main_dir/textual_inversion''')
578
+ mkdirs(f'{output_path}/textual_inversion/',True)
579
+ run(f'''ln -s -r {output_path}/textual_inversion {install_path}/sd_main_dir/''')
580
+ print('链接输出目录 完成')
581
+
582
+ def install_optimizing():
583
+ run('sudo apt install nginx -y')
584
+ run('env TF_CPP_MIN_LOG_LEVEL=1')
585
+ run('sudo apt -y update -qq')
586
+ run('wget http://launchpadlibrarian.net/367274644/libgoogle-perftools-dev_2.5-2.2ubuntu3_amd64.deb')
587
+ run('wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/google-perftools_2.5-2.2ubuntu3_all.deb')
588
+ run('wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/libtcmalloc-minimal4_2.5-2.2ubuntu3_amd64.deb')
589
+ run('wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/libgoogle-perftools4_2.5-2.2ubuntu3_amd64.deb')
590
+ run('sudo apt -y install -qq libunwind8-dev')
591
+ run('dpkg -i *.deb')
592
+ run('env LD_P_reLoad=libtcmalloc.so')
593
+ run('rm *.deb')
594
+
595
+ #安装依赖
596
+ def install_dependencies():
597
+ print('安装需要的python环境')
598
+ global envInstalled
599
+ global venvPath
600
+ run(f'''rm -rf {install_path}/sd_main_dir/venv''')
601
+ mkdirs(f'{install_path}/sd_main_dir/venv',True)
602
+ if str(sys.version).startswith('3.10'):
603
+ try:
604
+ run('python3 -m venv venv' ,cwd=f'{install_path}/sd_main_dir',try_error=False)
605
+ except:
606
+ run('sudo apt install python3.10-venv -y')
607
+ run('python3 -m venv venv' ,cwd=f'{install_path}/sd_main_dir')
608
+ else:
609
+ run('add-apt-repository ppa:deadsnakes/ppa -y')
610
+ run('sudo apt update')
611
+ run('sudo apt install python3.10 -y')
612
+ run('python3.10 -m venv venv',cwd=f'{install_path}/sd_main_dir')
613
+
614
+ if quickStart:
615
+ if not Path(venvPath).exists():
616
+ mkdirs(f'{install_path}/venv_cache',True)
617
+ if not Path(f'{install_path}/venv_cache/venv.tar.bak').exists():
618
+ download_file('https://huggingface.co/viyi/sdwui/resolve/main/venv.zip','venv.zip',f'{install_path}/venv_cache')
619
+ run(f'''unzip {install_path}/venv_cache/venv.zip -d {install_path}/venv_cache''')
620
+ venvPath = f'{install_path}/venv_cache/venv.tar.bak'
621
+ run(f'''rm -rf {install_path}/venv_cache/venv.zip''')
622
+ elif venvPath.endswith('.zip'):
623
+ mkdirs(f'{install_path}/venv_cache',True)
624
+ run(f'''unzip {venvPath} -d {install_path}/venv_cache''')
625
+ venvPath = f'{install_path}/venv_cache/venv.tar.bak'
626
+ print('解压环境')
627
+ run(f'tar -xf {venvPath} -C ./venv',cwd=f'{install_path}/sd_main_dir')
628
+ run(f'rm -rf {install_path}/sd_main_dir/venv.lib')
629
+ if not Path(f'{install_path}/sd_main_dir/venv/bin/pip').exists():
630
+ run('curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py')
631
+ run(f'{install_path}/sd_main_dir/venv/bin/python3 get-pip.py')
632
+
633
+ os.system(f'''{install_path}/sd_main_dir/venv/bin/python3 -V''')
634
+ os.system(f'''{install_path}/sd_main_dir/venv/bin/python3 -m pip -V''')
635
+
636
+ envInstalled = True
637
+ print('安装需要的python环境 完成')
638
+
639
+ # 个性化配置
640
+ def use_config():
641
+ print('使用自定义配置 包括tag翻译 \n')
642
+ mkdirs(f'{install_path}/temp',True)
643
+ run(f'git clone {config.git_proxy}{_sd_config_git_repu} sd-configs',cwd=f'{install_path}/temp')
644
+ run(f'cp -r -f -n {install_path}/temp/sd-configs/dist/* {install_path}/sd_main_dir')
645
+ if not Path(_ui_config_file).exists(): # ui配置文件
646
+ mkdirs(f"{_ui_config_file[:_ui_config_file.rfind('/')]}",True)
647
+ run(f'cp -f -n {install_path}/sd_main_dir/ui-config.json {_ui_config_file}')
648
+ if not Path(_setting_file).exists(): # 设置配置文件
649
+ mkdirs(f"{_setting_file[:_setting_file.rfind('/')]}",True)
650
+ run(f'cp -f -n {install_path}/sd_main_dir/config.json {_setting_file}')
651
+
652
+ def copy_last_log_to_images():
653
+ print('复制编号最大的一张收藏图到输出目录,用于保持编号,否则会出现收藏的图片被覆盖的情况')
654
+ img_list = os.listdir(f'{install_path}/sd_main_dir/log/images')
655
+ last_img_path = ''
656
+ last_img_num = 0
657
+ for img in img_list:
658
+ if re.findall(r'^\d+-',str(img)):
659
+ num = int(re.findall(r'^\d+-',str(img))[0][:-1])
660
+ if num > last_img_num:
661
+ last_img_path = img
662
+ last_img_num = num
663
+ print(f'{install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/txt2img-images')
664
+ mkdirs(f"{install_path}/sd_main_dir/outputs/txt2img-images",True)
665
+ run(f'''cp -f {install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/txt2img-images/''')
666
+
667
+ print(f'{install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/img2img-images')
668
+ mkdirs(f"{install_path}/sd_main_dir/outputs/img2img-images",True)
669
+ run(f'''cp -f {install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/img2img-images/''')
670
+
671
+ def start_webui(i):
672
+ # 只要不爆内存,其他方式关闭后会再次重启 访问地址会发生变化
673
+ print(i,'--port',str(_server_port+1+i))
674
+ if i>0:
675
+ print(f'使用第{i+1}张显卡启动第{i+1}个服务,通过frpc或nrgok地址后加/{i}/进行访问(不能使用同一个浏览器)')
676
+ if _useFrpc:
677
+ restart_times = 0
678
+ last_restart_time = time.time()
679
+ while True:
680
+ os.system(f'''{install_path}/sd_main_dir/venv/bin/python3 launch.py --device-id={i} --port {str(_server_port+1+i)} {'' if i ==0 else '--nowebui'}''')
681
+ print('5秒后重启服务')
682
+ if time.time() - last_restart_time < 30:
683
+ restart_times = restart_times + 1
684
+ else:
685
+ restart_times = 0
686
+ last_restart_time = time.time()
687
+ if restart_times >3 :
688
+ # 如果90秒内重启了3此,将不再自动重启
689
+ break
690
+ time.sleep(5)
691
+ else:
692
+ os.system(f'''{install_path}/sd_main_dir/venv/bin/python3 launch.py --device-id={i} --port {str(_server_port+1+i)} {'' if i ==0 else '--nowebui'}''')
693
+
694
+ # 启动
695
+ def start():
696
+ print('启动')
697
+ os.chdir(f'''{install_path}/sd_main_dir''')
698
+ args = ''
699
+ if _ui_config_file is not None and _ui_config_file != '' and Path(_ui_config_file).exists(): # ui配置文件
700
+ args += ' --ui-config-file=' + _ui_config_file
701
+ if _setting_file is not None and _setting_file != '' and Path(_setting_file).exists(): # 设置配置文件
702
+ args += ' --ui-settings-file=' + _setting_file
703
+ args += ' ' + otherArgs
704
+ os.environ['COMMANDLINE_ARGS']=args
705
+ run(f'''echo COMMANDLINE_ARGS=$COMMANDLINE_ARGS''')
706
+ os.environ['REQS_FILE']='requirements.txt'
707
+ start_webui()
708
+
709
+
710
+ # 启动非webui相关的的内容,加快启动速度
711
+ def main():
712
+ global envInstalled
713
+ global huggingface_is_init
714
+ startTicks = time.time()
715
+ stop_solo_threads()
716
+ isInstall = True if os.getenv('IsInstall','False') == 'True' else False
717
+ if Path(f'{install_path}/sd_main_dir').exists():
718
+ isInstall = True
719
+ if isInstall is False or _reLoad:
720
+ print('启动 安装和运行环境')
721
+ install()
722
+ link_dir()
723
+ threading.Thread(target = install_dependencies,daemon=True,name='solo_install_dependencies').start()
724
+ threading.Thread(target = install_optimizing,daemon=True,name='solo_install_optimizing').start()
725
+ threading.Thread(target = installProxyExe,daemon=True).start()
726
+ link_or_download_flie(replace_path(_async_downloading), _link_instead_of_copy=_link_instead_of_copy,
727
+ base_path=f'{install_path}/sd_main_dir')
728
+
729
+ link_or_download_flie(replace_path(_before_downloading), _link_instead_of_copy=_link_instead_of_copy,
730
+ base_path=f'{install_path}/sd_main_dir',is_await=True)
731
+ t = 0
732
+ while not envInstalled:
733
+ if t%10==0:
734
+ print('等待python环境安装...')
735
+ t = t+1
736
+ time.sleep(1)
737
+ use_config()
738
+ localProxy()
739
+ os.environ['IsInstall'] = 'True'
740
+ else:
741
+ envInstalled = True
742
+ link_or_download_flie(replace_path(_before_start_sync_downloading), _link_instead_of_copy=_link_instead_of_copy,
743
+ base_path=f'{install_path}/sd_main_dir',sync=True)
744
+ threading.Thread(target = startProxy, daemon=True, name='solo_startProxy').start()
745
+ ticks = time.time()
746
+ print("安装耗时:",(ticks - startTicks),"秒")
747
+ start()
748
+
749
+
750
+ if _skip_start:
751
+ print('已跳过自动启动,可手动执行 main() 进行启动。')
752
+ print('''推荐的启动代码:
753
+ try:
754
+ check_gpu() # 检查是否存在gpu
755
+ main()
756
+ except KeyboardInterrupt:
757
+ stop_solo_threads() # 中断后自动停止后台线程 (有部分功能在后台线程中运行)
758
+ ''')
759
+ else:
760
+ try:
761
+ main()
762
+ except KeyboardInterrupt:
763
+ stop_solo_threads()
764
+