#!/usr/bin/env python # coding: utf-8 from pathlib import Path import os import time import re import subprocess import threading import sys import socket from typing import List import config def mkdirs(path, exist_ok=True): if path and not Path(path).exists(): os.makedirs(path,exist_ok=exist_ok) # 内置参数默认值,当上下文有参数时可覆盖默认值 _useFrpc = config.useFrpc _useNgrok = config.useNgrok _reLoad = config.reLoad _before_downloading = config.before_downloading _async_downloading = config.async_downloading _before_start_sync_downloading = config.before_start_sync_downloading _server_port = config.server_port or 7860 _sd_git_repo = config.sd_git_repo\ .replace('{sdwui}','stable-diffusion-webui')\ .replace('{wui}',"webui") or 'https://github.com/viyiviyi/stable-diffusion-webui.git -b local' _sd_config_git_repu = config.sd_config_git_repu\ .replace('{sdwui}','stable-diffusion-webui')\ .replace('{wui}',"webui") or 'https://github.com/viyiviyi/sd-configs.git' _link_instead_of_copy = config.link_instead_of_copy show_shell_info = not config.hidden_console_info _skip_start = config.skip_start run_by_none_device = False def run(command, cwd=None, desc=None, errdesc=None, custom_env=None,try_error:bool=True) -> str: global show_shell_info if desc is not None: print(desc) run_kwargs = { "args": command, "shell": True, "cwd": cwd, "env": os.environ if custom_env is None else custom_env, "encoding": 'utf8', "errors": 'ignore', } if not show_shell_info: run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE result = subprocess.run(**run_kwargs) if result.returncode != 0: error_bits = [ f"{errdesc or 'Error running command'}.", f"Command: {command}", f"Error code: {result.returncode}", ] if result.stdout: error_bits.append(f"stdout: {result.stdout}") if result.stderr: error_bits.append(f"stderr: {result.stderr}") if try_error: print(RuntimeError("\n".join(error_bits))) else: raise RuntimeError("\n".join(error_bits)) if show_shell_info: print(result.stdout or "") return (result.stdout or "") install_path=f"{os.getcwd()}/sdwui" # 安装目录 output_path= os.path.join(os.getcwd(), config.output_path or 'sdwui/Output') input_path = config.input_path or f'{os.getcwd()}/sdwui/Input' mkdirs(install_path,True) mkdirs(output_path,True) os.environ['install_path'] = install_path os.environ['output_path'] = output_path os.environ['input_path'] = input_path def replace_path(input_str:str): return input_str.replace('$install_path',install_path)\ .replace('{install_path}',install_path)\ .replace('$input_path',input_path)\ .replace('{input_path}',input_path)\ .replace('$output_path',output_path)\ .replace('{output_path}',output_path)\ .replace('{sdwui}','stable-diffusion-webui')\ .replace('{wui}',"webui") space_string = ' \n\r\t\'\",' def config_reader(conf:str): args = [replace_path(item.split('#')[0].strip(space_string)) for item in conf.split('\n') if item.strip(space_string)] return [item.strip() for item in args if item.strip()] ngrokTokenFile = os.path.join(input_path,'configs/ngrok_token.txt') # 非必填 存放ngrokToken的文件的路径 frpcConfigFile = os.path.join(input_path,'configs/frpc_koishi.ini') # 非必填 frp 配置文件 # ss证书目录 下载nginx的版本,把pem格式改成crt格式 frpcSSLFFlies = [os.path.join(input_path,'configs/koishi_ssl')] + config_reader(config.frp_ssl_dir) # frpc 文件目录 如果目录不存在,会自动下载,也可以在数据集搜索 viyiviyi/utils 添加 frpcExePath = os.path.join(input_path,'utils-tools/frpc') # 其他需要加载的webui启动参数 写到【参数列表】这个配置去 otherArgs = ' '.join([item for item in config_reader(config.sd_start_args) if item != '--no-gradio-queue']) or '--xformers' venvPath = os.path.join(input_path,'sd-webui-venv/venv.tar.bak') # 安装好的python环境 sd-webui-venv是一个公开是数据集 可以搜索添加 # 用于使用kaggle api的token文件 参考 https://www.kaggle.com/docs/api # 此文件用于自动上传koishi的相关配置 也可以用于保存重要的输出文件 kaggleApiTokenFile = os.path.join(input_path,'configs/kaggle.json') requirements = [] frpcStartArg = '' _setting_file = replace_path(config.setting_file) _ui_config_file = replace_path(config.ui_config_file) mkdirs(f'{install_path}/configFiles',True) _frp_config_or_file = replace_path(config.frp_config_or_file) if Path(_frp_config_or_file.strip()).exists(): frpcConfigFile = _frp_config_or_file.strip() if not Path(frpcConfigFile).exists(): if _frp_config_or_file.strip().startswith('-f'): frpcStartArg = _frp_config_or_file.strip() else: print('没有frpcp配置') _useFrpc = False else: run(f'''cp -f {frpcConfigFile} {install_path}/configFiles/frpc_webui.ini''') frpcConfigFile = f'{install_path}/configFiles/frpc_webui.ini' run(f'''sed -i "s/local_port = .*/local_port = {_server_port}/g" {frpcConfigFile}''') frpcStartArg = f' -c {frpcConfigFile}' ngrokToken='' _ngrok_config_or_file = replace_path(config.ngrok_config_or_file) if Path(_ngrok_config_or_file.strip()).exists(): ngrokTokenFile = _ngrok_config_or_file.strip() if Path(ngrokTokenFile).exists(): with open(ngrokTokenFile,encoding = "utf-8") as nkfile: ngrokToken = nkfile.readline() elif not _ngrok_config_or_file.strip().startswith('/'): ngrokToken=_ngrok_config_or_file.strip() if not Path(venvPath).exists(): venvPath = os.path.join(input_path,'sd-webui-venv/venv.zip') import concurrent.futures import importlib import os import pprint import re from pathlib import Path from typing import List import requests show_shell_info = False def is_installed(package): try: spec = importlib.util.find_spec(package) except ModuleNotFoundError: return False return spec is not None def download_file(url:str, filename:str, dist_path:str, cache_path = '',_link_instead_of_copy:bool=True): # 获取文件的真实文件名 if not filename: with requests.get(url, stream=True) as r: if 'Content-Disposition' in r.headers: filename = r.headers['Content-Disposition'].split('filename=')[1].strip('"') r.close() if not filename and re.search(r'/[^/]+\.[^/]+$',url): filename = url.split('/')[-1] filename = re.sub(r'[\\/:*?"<>|;]', '', filename) filename = re.sub(r'[\s\t]+', '_', filename) if show_shell_info: print(f'下载 {filename} url: {url} --> {dist_path}') # 创建目录 if cache_path and not Path(cache_path).exists(): mkdirs(cache_path,exist_ok=True) if dist_path and not Path(dist_path).exists(): mkdirs(dist_path,exist_ok=True) # 拼接文件的完整路径 filepath = os.path.join(dist_path, filename) if cache_path: cache_path = os.path.join(cache_path, filename) # 判断文件是否已存在 if Path(filepath).exists(): print(f'文件 {filename} 已存在 {dist_path}') return if cache_path and Path(cache_path).exists(): run(f'cp -n -r -f {"-s" if _link_instead_of_copy else ""} {cache_path} {dist_path}') if show_shell_info: print(f'文件缓存 {cache_path} --> {dist_path}') return # 下载文件 with requests.get(url, stream=True) as r: r.raise_for_status() with open(cache_path or filepath, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): if chunk: f.write(chunk) # 如果使用了缓存目录 需要复制或链接文件到目标目录 if cache_path: run(f'cp -n -r -f {"-s" if _link_instead_of_copy else ""} {cache_path} {dist_path}') if show_shell_info: print(f'下载完成 {filename} --> {dist_path}') def download_git(url, dist_path, cache_path = '',_link_instead_of_copy:bool=True): if not Path(dist_path).exists(): mkdirs(dist_path,exist_ok=True) if show_shell_info: print(f'git 下载 {url} --> {dist_path}') if cache_path and not Path(cache_path).exists(): mkdirs(cache_path,exist_ok=True) run(f'git clone {config.git_proxy}{url}',cwd = cache_path) if cache_path: run(f'cp -n -r -f {cache_path}/* {dist_path}') else: run(f'git clone {config.git_proxy}{url}',cwd = dist_path) if show_shell_info: print(f'git 下载完成 {url} --> {dist_path}') # 加入文件到下载列表 def pause_url(url:str,dist_path:str): file_name = '' if re.match(r'^[^:]+:(https?|ftps?)://', url, flags=0): file_name = re.findall(r'^[^:]+:',url)[0][:-1] url = url[len(file_name)+1:] if not re.match(r'^(https?|ftps?)://',url): return file_name = re.sub(r'\s+','_',file_name or '') path_hash = str(hash(url)).replace('-','') return {'file_name':file_name,'path_hash':path_hash,'url':url,'dist_path':dist_path} def download_urls(download_list:List[dict],sync:bool=False,thread_num:int=5, cache_path:str=os.path.join(os.getcwd(),'.cache','download_util'), _link_instead_of_copy:bool=True,is_await:bool=False): if sync: for conf in download_list: cache_dir = os.path.join(cache_path,conf['path_hash']) if conf['url'].startswith('https://github.com'): download_git(conf['url'],conf['dist_path'],cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy) continue download_file(conf['url'],conf['file_name'],conf['dist_path'],cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy) else: executor = concurrent.futures.ThreadPoolExecutor(max_workers=thread_num) futures = [] for conf in download_list: cache_dir = os.path.join(cache_path,conf['path_hash']) if conf['url'].startswith('https://github.com'): futures.append(executor.submit(download_git, conf['url'],conf['dist_path'], cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy)) continue futures.append(executor.submit(download_file, conf['url'],conf['file_name'],conf['dist_path'], cache_path=cache_dir,_link_instead_of_copy=_link_instead_of_copy)) if is_await: concurrent.futures.wait(futures) def parse_config(config:str): space_string = ' \n\r\t\'\",' other_flie_list = [item.split('#')[0].strip(space_string) for item in config.split('\n') if item.strip(space_string)] other_flie_list = [item.strip() for item in other_flie_list if item.strip()] other_flie_list_store = {} other_flie_list_store_name='default' other_flie_list_store_list_cache=[] for item in other_flie_list: if item.startswith('[') and item.endswith(']'): if not other_flie_list_store_name == 'default': other_flie_list_store[other_flie_list_store_name]=other_flie_list_store_list_cache other_flie_list_store_list_cache = [] other_flie_list_store_name = item[1:-1] else: other_flie_list_store_list_cache.append(item) other_flie_list_store[other_flie_list_store_name]=other_flie_list_store_list_cache return other_flie_list_store def link_or_download_flie(config:str, skip_url:bool=False, _link_instead_of_copy:bool=True, base_path:str = '', sync:bool=False,thread_num:int=None, is_await:bool=False): store:dict[str,List[str]] = parse_config(config) download_list = [] for dist_dir in store.keys(): dist_path = os.path.join(base_path,dist_dir) mkdirs(dist_path,exist_ok=True) for path in store[dist_dir]: if 'https://' in path or 'http://' in path: if skip_url: continue if sync: download_urls([pause_url(path,dist_path)],_link_instead_of_copy = _link_instead_of_copy, sync=sync) continue download_list.append(pause_url(path,dist_path)) else: run(f'cp -n -r -f {"-s" if _link_instead_of_copy else ""} {path} {dist_path}') if show_shell_info: print(f'{"链接" if _link_instead_of_copy else "复制"} {path} --> {dist_path}') run(f'rm -f {dist_path}/\*.* ') if not skip_url: if show_shell_info: pprint.pprint(download_list) download_urls(download_list,_link_instead_of_copy = _link_instead_of_copy, sync=sync, thread_num=thread_num or 2,is_await=is_await) def echoToFile(content:str,path:str): if path.find('/') >= 0: _path = '/'.join(path.split('/')[:-1]) mkdirs(f'{_path}',True) with open(path,'w') as sh: sh.write(content) def zipPath(path:str,zipName:str,format='tar'): if path.startswith('$install_path'): path = path.replace('$install_path',install_path) if path.startswith('$output_path'): path = path.replace('$install_path',output_path) if not path.startswith('/'): path = f'{install_path}/sd_main_dir/{path}' if Path(path).exists(): if 'tar' == format: run(f'tar -cf {output_path}/'+ zipName +'.tar -C '+ path +' . ') elif 'gz' == format: run(f'tar -czf {output_path}/'+ zipName +'.tar.gz -C '+ path +' . ') return print('指定的目录不存在:'+path) # 检查网络 def check_service(host, port): try: socket.create_connection((host, port), timeout=5) return True except socket.error: return False # ngrok def startNgrok(ngrokToken:str,ngrokLocalPort:int): if not is_installed('pyngrok'): run(f'pip install pyngrok') from pyngrok import conf, ngrok try: conf.get_default().auth_token = ngrokToken conf.get_default().monitor_thread = False ssh_tunnels = ngrok.get_tunnels(conf.get_default()) if len(ssh_tunnels) == 0: ssh_tunnel = ngrok.connect(ngrokLocalPort) print('ngrok 访问地址:'+ssh_tunnel.public_url) else: print('ngrok 访问地址:'+ssh_tunnels[0].public_url) except: print('启动ngrok出错') def startFrpc(name,configFile): echoToFile(f''' cd {install_path}/frpc/ {install_path}/frpc/frpc {configFile} ''',f'{install_path}/frpc/start.sh') os.system(f'''bash {install_path}/frpc/start.sh''') def installProxyExe(): if _useFrpc: print('安装frpc') mkdirs(f'{install_path}/frpc',True) if Path(frpcExePath).exists(): run(f'cp -f -n {frpcExePath} {install_path}/frpc/frpc') else: run(f'wget "https://huggingface.co/datasets/ACCA225/Frp/resolve/main/frpc" -O {install_path}/frpc/frpc') for ssl in frpcSSLFFlies: if Path(ssl).exists(): run(f'cp -f -n {ssl}/* {install_path}/frpc/') run(f'chmod +x {install_path}/frpc/frpc') run(f'{install_path}/frpc/frpc -v') if _useNgrok and not is_installed('pyngrok'): run('pip install pyngrok') def startProxy(): if _useNgrok: startNgrok(ngrokToken,_server_port) if _useFrpc: startFrpc('frpc_proxy',frpcStartArg) # nginx 反向代理配置文件 def localProxy(): conf = ''' server { listen '''+str(_server_port)+'''; listen [::]:'''+str(_server_port)+'''; server_name 127.0.0.1 localhost 0.0.0.0 ""; if ($request_method = OPTIONS) { return 200; } fastcgi_send_timeout 10m; fastcgi_read_timeout 10m; fastcgi_connect_timeout 10m; location /1/ { proxy_pass http://127.0.0.1:'''+str(_server_port+2)+'''/; # add_header Set-Cookie "subpath=1; expires=0; Path=/"; # proxy_set_header Set-Cookie "subpath=1; expires=0; Path=/"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_http_version 1.1; proxy_connect_timeout 10m; proxy_read_timeout 10m; } location / { set $proxy_url http://127.0.0.1:'''+str(_server_port+1)+'''; # if ($cookie_subpath = "1") { # set $proxy_url http://127.0.0.1:'''+str(_server_port+2)+'''; # } proxy_pass $proxy_url; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_http_version 1.1; proxy_connect_timeout 10m; proxy_read_timeout 10m; } } ''' echoToFile(conf,'/etc/nginx/conf.d/proxy_nginx.conf') if not check_service('localhost',_server_port): run(f'''nginx -c /etc/nginx/nginx.conf''') os.system(f'''nginx -s reload''') import inspect import ctypes def _async_raise(tid, exctype): """raises the exception, performs cleanup if needed""" tid = ctypes.c_long(tid) if not inspect.isclass(exctype): exctype = type(exctype) res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) if res == 0: raise ValueError("invalid thread id") elif res != 1: # """if it returns a number greater than one, you're in trouble, # and you should call it again with exc=NULL to revert the effect""" ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) raise SystemError("PyThreadState_SetAsyncExc failed") def stop_thread(thread): _async_raise(thread.ident, SystemExit) def stop_solo_threads(): # 获取当前所有活动的线程 threads = threading.enumerate() # 关闭之前创建的子线程 for thread in threads: if thread.name.startswith('solo_'): print(f'结束线程:{thread.name}') try: stop_thread(thread) except socket.error: print(f'结束线程:{thread.name} 执行失败') envInstalled=False quickStart = True #安装 def install(): print('安装') os.chdir(f'''{install_path}''') run(f'''git lfs install''') run(f'''git config --global credential.helper store''') for requirement in requirements: run(f'pip install {requirement}') if _reLoad: run(f'''rm -rf sd_main_dir''') if Path("sd_main_dir").exists(): os.chdir(f'''{install_path}/sd_main_dir/''') run(f'''git checkout .''') run(f'''git pull''') else: run(f'''git clone {config.git_proxy}{_sd_git_repo} sd_main_dir''') os.chdir(f'''{install_path}/sd_main_dir''') print('安装 完成') # 链接输出目录 def link_dir(): print('链接输出目录') # 链接图片输出目录 mkdirs(f'{output_path}/outputs',True) run(f'''rm -rf {install_path}/sd_main_dir/outputs''') run(f'''ln -s -r {output_path}/outputs {install_path}/sd_main_dir/''') # 输出收藏目录 mkdirs(f'{output_path}/log',True) run(f'''rm -rf {install_path}/sd_main_dir/log''') run(f'''ln -s -r {output_path}/log {install_path}/sd_main_dir/''') # 链接训练输出目录 文件夹链接会导致功能不能用 run(f'''rm -rf {install_path}/sd_main_dir/textual_inversion''') mkdirs(f'{output_path}/textual_inversion/',True) run(f'''ln -s -r {output_path}/textual_inversion {install_path}/sd_main_dir/''') print('链接输出目录 完成') def install_optimizing(): run('sudo apt install nginx -y') run('env TF_CPP_MIN_LOG_LEVEL=1') run('sudo apt -y update -qq') run('wget http://launchpadlibrarian.net/367274644/libgoogle-perftools-dev_2.5-2.2ubuntu3_amd64.deb') run('wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/google-perftools_2.5-2.2ubuntu3_all.deb') run('wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/libtcmalloc-minimal4_2.5-2.2ubuntu3_amd64.deb') run('wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/libgoogle-perftools4_2.5-2.2ubuntu3_amd64.deb') run('sudo apt -y install -qq libunwind8-dev') run('dpkg -i *.deb') run('env LD_P_reLoad=libtcmalloc.so') run('rm *.deb') #安装依赖 def install_dependencies(): print('安装需要的python环境') global envInstalled global venvPath run(f'''rm -rf {install_path}/sd_main_dir/venv''') mkdirs(f'{install_path}/sd_main_dir/venv',True) if str(sys.version).startswith('3.10'): try: run('python3 -m venv venv' ,cwd=f'{install_path}/sd_main_dir',try_error=False) except: run('sudo apt install python3.10-venv -y') run('python3 -m venv venv' ,cwd=f'{install_path}/sd_main_dir') else: run('add-apt-repository ppa:deadsnakes/ppa -y') run('sudo apt update') run('sudo apt install python3.10 -y') run('python3.10 -m venv venv',cwd=f'{install_path}/sd_main_dir') if quickStart: if not Path(venvPath).exists(): mkdirs(f'{install_path}/venv_cache',True) if not Path(f'{install_path}/venv_cache/venv.tar.bak').exists(): download_file('https://huggingface.co/viyi/sdwui/resolve/main/venv.zip','venv.zip',f'{install_path}/venv_cache') run(f'''unzip {install_path}/venv_cache/venv.zip -d {install_path}/venv_cache''') venvPath = f'{install_path}/venv_cache/venv.tar.bak' run(f'''rm -rf {install_path}/venv_cache/venv.zip''') elif venvPath.endswith('.zip'): mkdirs(f'{install_path}/venv_cache',True) run(f'''unzip {venvPath} -d {install_path}/venv_cache''') venvPath = f'{install_path}/venv_cache/venv.tar.bak' print('解压环境') run(f'tar -xf {venvPath} -C ./venv',cwd=f'{install_path}/sd_main_dir') run(f'rm -rf {install_path}/sd_main_dir/venv.lib') if not Path(f'{install_path}/sd_main_dir/venv/bin/pip').exists(): run('curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py') run(f'{install_path}/sd_main_dir/venv/bin/python3 get-pip.py') os.system(f'''{install_path}/sd_main_dir/venv/bin/python3 -V''') os.system(f'''{install_path}/sd_main_dir/venv/bin/python3 -m pip -V''') envInstalled = True print('安装需要的python环境 完成') # 个性化配置 def use_config(): print('使用自定义配置 包括tag翻译 \n') mkdirs(f'{install_path}/temp',True) run(f'git clone {config.git_proxy}{_sd_config_git_repu} sd-configs',cwd=f'{install_path}/temp') run(f'cp -r -f -n {install_path}/temp/sd-configs/dist/* {install_path}/sd_main_dir') if not Path(_ui_config_file).exists(): # ui配置文件 mkdirs(f"{_ui_config_file[:_ui_config_file.rfind('/')]}",True) run(f'cp -f -n {install_path}/sd_main_dir/ui-config.json {_ui_config_file}') if not Path(_setting_file).exists(): # 设置配置文件 mkdirs(f"{_setting_file[:_setting_file.rfind('/')]}",True) run(f'cp -f -n {install_path}/sd_main_dir/config.json {_setting_file}') def copy_last_log_to_images(): print('复制编号最大的一张收藏图到输出目录,用于保持编号,否则会出现收藏的图片被覆盖的情况') img_list = os.listdir(f'{install_path}/sd_main_dir/log/images') last_img_path = '' last_img_num = 0 for img in img_list: if re.findall(r'^\d+-',str(img)): num = int(re.findall(r'^\d+-',str(img))[0][:-1]) if num > last_img_num: last_img_path = img last_img_num = num print(f'{install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/txt2img-images') mkdirs(f"{install_path}/sd_main_dir/outputs/txt2img-images",True) run(f'''cp -f {install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/txt2img-images/''') print(f'{install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/img2img-images') mkdirs(f"{install_path}/sd_main_dir/outputs/img2img-images",True) run(f'''cp -f {install_path}/sd_main_dir/log/images/{last_img_path} {install_path}/sd_main_dir/outputs/img2img-images/''') def start_webui(i): # 只要不爆内存,其他方式关闭后会再次重启 访问地址会发生变化 print(i,'--port',str(_server_port+1+i)) if i>0: print(f'使用第{i+1}张显卡启动第{i+1}个服务,通过frpc或nrgok地址后加/{i}/进行访问(不能使用同一个浏览器)') if _useFrpc: restart_times = 0 last_restart_time = time.time() while True: 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'}''') print('5秒后重启服务') if time.time() - last_restart_time < 30: restart_times = restart_times + 1 else: restart_times = 0 last_restart_time = time.time() if restart_times >3 : # 如果90秒内重启了3此,将不再自动重启 break time.sleep(5) else: 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'}''') # 启动 def start(): print('启动') os.chdir(f'''{install_path}/sd_main_dir''') args = '' if _ui_config_file is not None and _ui_config_file != '' and Path(_ui_config_file).exists(): # ui配置文件 args += ' --ui-config-file=' + _ui_config_file if _setting_file is not None and _setting_file != '' and Path(_setting_file).exists(): # 设置配置文件 args += ' --ui-settings-file=' + _setting_file args += ' ' + otherArgs os.environ['COMMANDLINE_ARGS']=args run(f'''echo COMMANDLINE_ARGS=$COMMANDLINE_ARGS''') os.environ['REQS_FILE']='requirements.txt' start_webui(0) # 启动非webui相关的的内容,加快启动速度 def main(): global envInstalled global huggingface_is_init startTicks = time.time() stop_solo_threads() isInstall = True if os.getenv('IsInstall','False') == 'True' else False if Path(f'{install_path}/sd_main_dir').exists(): isInstall = True if isInstall is False or _reLoad: print('启动 安装和运行环境') install() link_dir() threading.Thread(target = install_dependencies,daemon=True,name='solo_install_dependencies').start() threading.Thread(target = install_optimizing,daemon=True,name='solo_install_optimizing').start() threading.Thread(target = installProxyExe,daemon=True).start() link_or_download_flie(replace_path(_async_downloading), _link_instead_of_copy=_link_instead_of_copy, base_path=f'{install_path}/sd_main_dir') link_or_download_flie(replace_path(_before_downloading), _link_instead_of_copy=_link_instead_of_copy, base_path=f'{install_path}/sd_main_dir',is_await=True) t = 0 while not envInstalled: if t%10==0: print('等待python环境安装...') t = t+1 time.sleep(1) use_config() localProxy() os.environ['IsInstall'] = 'True' else: envInstalled = True link_or_download_flie(replace_path(_before_start_sync_downloading), _link_instead_of_copy=_link_instead_of_copy, base_path=f'{install_path}/sd_main_dir',sync=True) threading.Thread(target = startProxy, daemon=True, name='solo_startProxy').start() ticks = time.time() print("安装耗时:",(ticks - startTicks),"秒") start() if _skip_start: print('已跳过自动启动,可手动执行 main() 进行启动。') print('''推荐的启动代码: try: check_gpu() # 检查是否存在gpu main() except KeyboardInterrupt: stop_solo_threads() # 中断后自动停止后台线程 (有部分功能在后台线程中运行) ''') else: try: main() except KeyboardInterrupt: stop_solo_threads()