File size: 45,561 Bytes
617a49e
1
{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 这只是一个完整项目的一部分 不能运行的 \n- 可以从这个地址运行 [点击打开](https://www.kaggle.com/viyiviyi/sdwui-before)\n---","metadata":{"id":"6zE2QeUKLCtC"}},{"cell_type":"code","source":"print('''\n路径说明\n*  可以使用 $install_path 或 {install_path} 来访问安装目录,写在字符串内也会生效\n*  $output_path 或 {output_path} 可以访问输出目录\n*  $input_path 或 {input_path} 可以访问输入目录 在kaggle是数据集根目录\n*  如果链接了谷歌云盘,将会在云盘根目录创建 sdwebui 的文件夹,文件夹内的 Input 目录将会作为 输入目录 Output 将会作为输出目录\n\n发布地址 https://www.kaggle.com/code/yiyiooo/stable-diffusion-webui-novelai\n\n更新日志\n*\n*  23-07-26\n*  增加可隐藏启动时的不重要信息 通过增加配置 [hidden_console_info = True] 开启\n*  增加可自定义下载或链接文件到指定目录\n*  配置方式:查看 https://www.kaggle.com/code/yiyiooo/stable-diffusion-webui-novelai 的 [其他文件列表]\n*\n*  23-07-22\n*  更新了可自动同步收藏目录到 https://huggingface.co/ 的功能\n*  可通过增加配置项 huggingface_token = \"token\" 和 huggingface_repo = “仓库id” 后使用此功能\n*\n*  23-07-18  \n*  如果有两个GPU 第二个GPU将会启动api服务,可用api的方式调用,和webui互相独立,绘图时不会导致另外一边卡顿\n*  通过/1/作为base url来访问这个api\n*  没有启动两个webui是因为,很大概率爆内存\n''')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pathlib import Path\nimport os\nimport time\nimport re\nimport subprocess\nimport threading\nimport sys\nimport socket\nimport torch\nfrom typing import List\n\n# 检查gpu是否存在\nif torch.cuda.device_count() == 0:\n    raise Exception('当前环境没有GPU')\n\ninstall_path=f\"{os.environ['HOME']}/sdwebui\" # 安装目录\noutput_path=f\"{os.environ['HOME']}/sdwebui/Output\" # 输出目录 如果使用google云盘 会在google云盘增加sdwebui/Output\ninput_path = '/kaggle/input' # 输入目录\n\ngoogle_drive = '' \n# 连接谷歌云\ntry:\n    if useGooglrDrive:\n        from google.colab import drive\n        drive.mount(f'~/google_drive')\n        google_drive = f\"{os.environ['HOME']}/google_drive/MyDrive\"\n        output_path = f'{google_drive}/sdwebui/Output'\n        input_path = f'{google_drive}/sdwebui/Input'\n        !mkdir -p {input_path}\n        # 设置文件路径\n        if setting_file== '/kaggle/working/configs/config.json':\n            setting_file = os.path.join(output_path,'configs/config.json')\n        if ui_config_file == '/kaggle/working/configs/ui-config.json':\n            ui_config_file = os.path.join(output_path,'configs/ui-config.json')\n        print('''\n已经链接到谷歌云盘\n云盘根目录/sdwebui/Output 被链接为输出目录,图片和设置文件将会保存在此文件夹\n云盘根目录/sdwebui/Input 被链接为输入目录,环境变量名是 $input_path\n        ''')\nexcept:\n    useGooglrDrive = False\n\n!mkdir -p {install_path}\n!mkdir -p {output_path}\n\nos.environ['install_path'] = install_path\nos.environ['output_path'] = output_path\nos.environ['google_drive'] = google_drive\nos.environ['input_path'] = input_path\n\ndef replace_path(input_str:str):\n    return input_str.replace('$install_path',install_path)\\\n    .replace('{install_path}',install_path)\\\n    .replace('$input_path',input_path)\\\n    .replace('{input_path}',input_path)\\\n    .replace('$output_path',output_path)\\\n    .replace('{output_path}',output_path)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 模型目录 文件名称:链接的方式指定文件名\nmodelDirs = []+[replace_path(item.strip()) for item in 模型列表.split('\\n') if item.strip() ]\n# vae列表\nmodelVaeDirs = []+[replace_path(item.strip()) for item in VAE列表.split('\\n') if item.strip()]\n# 此配置内的目录下所有文件将加载到hypernetworks文件夹\nhypernetworksModelDirs = []+[replace_path(item.strip()) for item in hypernetworks列表.split('\\n') if item.strip()] \n# 此配置内的目录下所有文件将加载到embeddings文件夹\nembeddingsModelDirs = [] +[replace_path(item.strip()) for item in embeddings列表.split('\\n') if item.strip()]\n# 此配置内的目录下所有文件将加载到Lora文件夹\nloraModelDirs = []+[replace_path(item.strip()) for item in Lora列表.split('\\n') if item.strip()] \n\nlyCORISModelDirs = []+[replace_path(item.strip()) for item in LyCORIS列表.split('\\n') if item.strip()] \n# controlNet插件模型列表\ncontrolNetModels=[] +[replace_path(item.strip()) for item in controlNet模型列表.split('\\n') if item.strip()]\n# 插件列表\nextensions=[]+[replace_path(item.strip()) for item in 插件列表.split('\\n') if item.strip()]\n# 配置文件链接\nlinkHypernetworksDir=False # 链接 hypernetworks 目录到输出目录\nlinkEmbeddingsDir=False # 链接 embeddings 目录到输出目录\nlinkTextual_inversionDir=False # 链接 textual_inversion 目录到输出目录 如果需要保存训练过程的产出文件时建议开启\n\nngrokTokenFile = os.path.join(input_path,'configs/ngrok_token.txt') # 非必填 存放ngrokToken的文件的路径\nfrpcConfigFile = os.path.join(input_path,'configs/frpc_koishi.ini')  # 非必填 frp 配置文件\n# ss证书目录 下载nginx的版本,把pem格式改成crt格式\nfrpcSSLFFlies =[os.path.join(input_path,'configs/koishi_ssl')]+[replace_path(item.strip()) for item in frpSSL文件.split('\\n') if item.strip() ]\n# frpc 文件目录 如果目录不存在,会自动下载,也可以在数据集搜索 viyiviyi/utils 添加\nfrpcExePath = os.path.join(input_path,'utils-tools/frpc')\n# 其他需要加载的webui启动参数 写到【参数列表】这个配置去\notherArgs = ' '.join([replace_path(item.strip()) for item in 参数列表.split('\\n') if item.strip()])\n\nvenvPath = os.path.join(input_path,'sd-webui-venv/venv.tar.bak') # 安装好的python环境 sd-webui-venv是一个公开是数据集 可以搜索添加\n\n# 用于使用kaggle api的token文件 参考 https://www.kaggle.com/docs/api\n# 此文件用于自动上传koishi的相关配置 也可以用于保存重要的输出文件\nkaggleApiTokenFile = os.path.join(input_path,'configs/kaggle.json')\n\nrequirements = []","metadata":{"_kg_hide-input":true,"id":"i3LhnwYHLCtC","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 这下面的是用于初始化一些值或者环境变量的,轻易别改\nfrpcStartArg = ''\n\nfrp配置文件或配置 = replace_path(frp配置文件或配置)\nngrok配置或文件地址 = replace_path(ngrok配置或文件地址)\n\n!mkdir -p $install_path/configFiles\nif Path(frp配置文件或配置.strip()).exists():\n    frpcConfigFile = frp配置文件或配置.strip()\nif not Path(frpcConfigFile).exists(): \n    if frp配置文件或配置.strip().startswith('-f'):\n        frpcStartArg = frp配置文件或配置.strip()\n    else:\n        print('没有frpcp配置')\n        useFrpc = False\nelse:\n    os.system(f'cp -f {frpcConfigFile} {install_path}/configFiles/frpc_webui.ini')\n    frpcConfigFile = f'{install_path}/configFiles/frpc_webui.ini'\n    os.system(f'sed -i \"s/local_port = .*/local_port = {webuiPort}/g\" {frpcConfigFile}')\n    frpcStartArg = f' -c {frpcConfigFile}'\n\nngrokToken=''\nif Path(ngrok配置或文件地址.strip()).exists():\n    ngrokTokenFile = ngrok配置或文件地址.strip()\nif Path(ngrokTokenFile).exists():\n    with open(ngrokTokenFile,encoding = \"utf-8\") as nkfile:\n        ngrokToken = nkfile.readline()\nelif not ngrok配置或文件地址.strip().startswith('/'):\n    ngrokToken=ngrok配置或文件地址.strip()\n    \nif not Path(venvPath).exists():\n    venvPath = os.path.join(input_path,'sd-webui-venv/venv.zip')\n","metadata":{"_kg_hide-input":true,"id":"a_GtG2ayLCtD","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_other_files():\n    if '其他文件列表' not in globals():\n        return {}\n    other_flie_list = [replace_path(item.split('#')[0].strip()) for item in 其他文件列表.split('\\n') if item.strip()]\n    other_flie_list_store = {}\n    other_flie_list_store_name='default'\n    other_flie_list_store_list_cache=[]\n    for item in other_flie_list:\n        if item.startswith('[') and item.endswith(']'):\n            if not other_flie_list_store_name == 'default':\n                other_flie_list_store[other_flie_list_store_name]=other_flie_list_store_list_cache\n                other_flie_list_store_list_cache = []\n            other_flie_list_store_name = item[1:-1]\n        else:\n            other_flie_list_store_list_cache.append(item)\n    other_flie_list_store[other_flie_list_store_name]=other_flie_list_store_list_cache\n    return other_flie_list_store","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os,sys,io\nsys_sys_stdout = sys.stdout\nclass HiddenPrints:\n    def __enter__(self):\n        if 'hidden_console_info' not in globals(): return\n        if hidden_console_info:\n            self._stdout = sys.stdout\n            sys.stdout = self._stringio = io.StringIO()\n\n    def __exit__(self, *args):\n        if 'hidden_console_info' not in globals(): return\n        if hidden_console_info:\n            sys.stdout = self._stdout","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# kaggle public API\n\n**不能使用%cd这种会改变当前工作目录的命令,会导致和其他线程冲突**\n\n---","metadata":{"id":"p0uS-BLULCtD"}},{"cell_type":"code","source":"# 安装kaggle的api token文件\ndef initKaggleConfig():\n    if Path('~/.kaggle/kaggle.json').exists():\n        return True\n    if Path(kaggleApiTokenFile).exists():\n        !mkdir -p ~/.kaggle/\n        os.system('cp '+kaggleApiTokenFile+' ~/.kaggle/kaggle.json')\n        !chmod 600 ~/.kaggle/kaggle.json\n        return True\n    print('缺少kaggle的apiToken文件,访问:https://www.kaggle.com/你的kaggle用户名/account 获取')\n    return False\n\ndef getUserName():\n    if not initKaggleConfig(): return\n    import kaggle\n    return kaggle.KaggleApi().read_config_file()['username']\n\ndef createOrUpdateDataSet(path:str,datasetName:str):\n    if not initKaggleConfig(): return\n    print('创建或更新数据集 '+datasetName)\n    import kaggle\n    os.system('mkdir -p $install_path/kaggle_cache')\n    os.system('rm -rf $install_path/kaggle_cache/*')\n    datasetDirPath = install_path+'/kaggle_cache/'+datasetName\n    os.system('mkdir -p '+datasetDirPath)\n    os.system('cp -f '+path+' '+datasetDirPath+'/')\n    username = getUserName()\n    print(\"kaggle username:\"+username)\n    datasetPath = username+'/'+datasetName\n    datasetList = kaggle.api.dataset_list(mine=True,search=datasetPath)\n    print(datasetList)\n    if len(datasetList) == 0 or datasetPath not in [str(d) for d in datasetList]: # 创建 create\n        os.system('kaggle datasets init -p' + datasetDirPath)\n        metadataFile = datasetDirPath+'/dataset-metadata.json'\n        os.system('sed -i s/INSERT_TITLE_HERE/'+ datasetName + '/g ' + metadataFile)\n        os.system('sed -i s/INSERT_SLUG_HERE/'+ datasetName + '/g ' + metadataFile)\n        os.system('cat '+metadataFile)\n        os.system('kaggle datasets create -p '+datasetDirPath)\n        print('create database done')\n    else:\n        kaggle.api.dataset_metadata(datasetPath,datasetDirPath)\n        kaggle.api.dataset_create_version(datasetDirPath, 'auto update',dir_mode='zip')\n        print('upload database done')\n\ndef downloadDatasetFiles(datasetName:str,outputPath:str):\n    if not initKaggleConfig(): return\n    print('下载数据集文件 '+datasetName)\n    import kaggle\n    username = getUserName()\n    datasetPath = username+'/'+datasetName\n    datasetList = kaggle.api.dataset_list(mine=True,search=datasetPath)\n    if datasetPath not in [str(d) for d in datasetList]:\n        return False\n    os.system('mkdir -p '+outputPath)\n    kaggle.api.dataset_download_files(datasetPath,path=outputPath,unzip=True)\n    return True\n\n","metadata":{"_kg_hide-input":true,"id":"m8FJi4j0LCtD","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 同步文件夹到 huggingface\n","metadata":{}},{"cell_type":"code","source":"# 文件夹与 huggingface 同步\nif 'huggingface_token' in locals() and 'huggingface_repo' in locals():\n    requirements.append('watchdog')\n    requirements.append('huggingface_hub')\n\n\nhuggingface_is_init = False\n\ndef init_huggingface():\n    if 'huggingface_token' not in globals() or 'huggingface_repo' not in globals():\n        print('请增加配置项 huggingface_token = \"token\" 和 huggingface_repo = “仓库id” 后使用 huggingface 功能')\n        return False\n    global huggingface_is_init\n    from huggingface_hub  import HfApi,login,snapshot_download\n    token = replace_path(huggingface_token)\n    if Path(token).exists():\n        with open(token,encoding = \"utf-8\") as nkfile:\n            token = nkfile.readline()\n    if not token.startswith('hf_'):\n        print('huggingface token 不正确,请将 token 或 仅存放token 的txt文件路径填入 huggingface_token 配置')\n        return False\n    login(token)\n    huggingface_is_init = True\n    return True\n\n\ndef download_huggingface_repo(repo_id:str,dist_directory:str=None,repo_type='dataset',callback=None):\n    if not huggingface_is_init:\n        print('huggingface 相关功能未初始化 请调用 init_huggingface() 初始化')\n        \n    if not dist_directory:\n        dist_directory = f'{install_path}/stable-diffusion-webui/log'\n    \n    from huggingface_hub  import HfApi,login,snapshot_download\n    \n    api = HfApi()\n    \n    print('下载收藏的图片')\n    snapshot_download(repo_id = huggingface_repo,\n                      local_dir = dist_directory,\n                      local_dir_use_symlinks = \"auto\",\n                      token=True,\n                      repo_type=repo_type\n                     )\n    if callback:\n        callback()\n\ndef start_sync_log_to_huggingface(repo_id:str,directory_to_watch:str=None,repo_type='dataset'):\n    if not huggingface_is_init:\n        print('huggingface 相关功能未初始化 请调用 init_huggingface() 初始化')\n    \n    from watchdog.observers import Observer\n    from watchdog.events import FileSystemEventHandler\n    from huggingface_hub  import HfApi,login,snapshot_download\n    \n    # 配置监视的目录和 Hugging Face 仓库信息\n    class FileChangeHandler(FileSystemEventHandler):\n        def __init__(self, api, repo_id, repo_type):\n            self.api = api\n            self.repo_id = repo_id\n            self.repo_type = repo_type\n        def on_created(self, event):\n            if not event.is_directory:\n                # 上传新文件到 Hugging Face 仓库\n                file_path = event.src_path\n                file_name = os.path.basename(file_path)\n                if file_name[-4:] not in ['.png','.jpg','.txt']: return\n                try:\n                    self.api.upload_file(\n                        path_or_fileobj=file_path,\n                        path_in_repo=file_path.replace(directory_to_watch,''),\n                        repo_id=self.repo_id,\n                        repo_type=self.repo_type,\n                    )\n                except Error as error:\n                    print(error)\n\n        def on_deleted(self, event):\n            if not event.is_directory:\n                # 从 Hugging Face 仓库删除文件\n                file_path = event.src_path\n                file_name = os.path.basename(file_path)\n                if file_name[-4:] not in ['.png','.jpg','.txt']: return\n                try:\n                    self.api.delete_file(\n                        path_in_repo=file_path.replace(directory_to_watch,''),\n                        repo_id=self.repo_id,\n                        repo_type=self.repo_type,\n                        )\n                except Error as error:\n                    print(error)\n\n        def on_modified(self, event):\n            if not event.is_directory:\n                # 更新 Hugging Face 仓库中的文件\n                file_path = event.src_path\n                file_name = os.path.basename(file_path)\n                if file_name[-4:] not in ['.png','.jpg','.txt']: return\n                try:\n                    self.api.upload_file(\n                        path_or_fileobj=file_path,\n                        path_in_repo=file_path.replace(directory_to_watch,''),\n                        repo_id=self.repo_id,\n                        repo_type=self.repo_type,\n                    )\n                except Error as error:\n                    print(error)\n\n        def on_moved(self, event):\n            if not event.is_directory:\n                file_path = event.dest_path\n                file_name = os.path.basename(file_path)\n                if file_name[-4:] not in ['.png','.jpg','.txt']: return\n                if event.dest_path.startswith(directory_to_watch):\n                    try:\n                        self.api.upload_file(\n                            path_or_fileobj=file_path,\n                            path_in_repo=file_path.replace(directory_to_watch,''),\n                            repo_id=self.repo_id,\n                            repo_type=self.repo_type,\n                        )\n                    except Error as error:\n                        print(error)\n\n    api = HfApi()\n    \n    if not directory_to_watch:\n        directory_to_watch = f'{install_path}/stable-diffusion-webui/log'\n    # 创建观察者对象并注册文件变化处理程序\n    event_handler = FileChangeHandler(api,repo_id,repo_type)\n    observer = Observer()\n    observer.schedule(event_handler, directory_to_watch, recursive=True)\n\n    # 启动观察者\n    observer.name = \"solo_directory_to_watch\"\n    print(f'启动收藏图片文件夹监听,并自动同步到 huggingface {repo_type} : {repo_id}')\n    observer.start()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 工具函数\n**不能使用%cd这种会改变当前工作目录的命令,会导致和其他线程冲突**\n\n---","metadata":{"id":"sswa04veLCtE"}},{"cell_type":"markdown","source":"一般工具函数","metadata":{}},{"cell_type":"code","source":"# 绕过 os.systen 的限制执行命令\ndef run(shell:str,shellName=''):\n    if shellName == '': shellName = str(time.time())\n    !mkdir -p $install_path/run_cache\n    with open(install_path+'/run_cache/run_cache.'+shellName+'.sh','w') as sh:\n        sh.write(shell)\n    !bash {install_path}/run_cache/run_cache.{shellName}.sh\n\n# 连接多个路径字符串 让路径在shell命令中能正常的执行\ndef pathJoin(*paths:str):\n    pathStr = ''\n    for p in paths:\n        pathStr += '\"'+p+'\"'\n    pathStr = '\"*\"'.join(pathStr.split('*'))\n    pathStr = '\"$\"'.join(pathStr.split('$'))\n    pathStr = '\"(\"'.join(pathStr.split('('))\n    pathStr = '\")\"'.join(pathStr.split(')'))\n    pathStr = '\"{\"'.join(pathStr.split('{'))\n    pathStr = '\"}\"'.join(pathStr.split('}'))\n    pathStr = re.sub(r'\"\"','',pathStr)\n    pathStr = re.sub(r'\\*{2,}','\"',pathStr)\n    pathStr = re.sub(r'/{2,}','/',pathStr)\n    pathStr = re.sub(r'/\\./','/',pathStr)\n    return pathStr\n\n# 判断路径是不是一个文件或者可能指向一些文件\ndef pathIsFile(path):\n    if Path(path).is_file():\n        return True\n    if re.search(r'\\.(ckpt|safetensors|png|jpg|txt|pt|pth|json|yaml|\\*)$',path):\n        return True\n    return False\n\ndef echoToFile(content:str,path:str):\n    with open(path,'w') as sh:\n        sh.write(content)\n\n# ngrok\ndef startNgrok(ngrokToken:str,ngrokLocalPort:int):\n    from pyngrok import conf, ngrok\n    try:\n        conf.get_default().auth_token = ngrokToken\n        conf.get_default().monitor_thread = False\n        ssh_tunnels = ngrok.get_tunnels(conf.get_default())\n        if len(ssh_tunnels) == 0:\n            ssh_tunnel = ngrok.connect(ngrokLocalPort)\n            print('ngrok 访问地址:'+ssh_tunnel.public_url)\n        else:\n            print('ngrok 访问地址:'+ssh_tunnels[0].public_url)\n    except:\n        print('启动ngrok出错')\n        \ndef startFrpc(name,configFile):\n    run(f'''\ncd $install_path/frpc/\n$install_path/frpc/frpc {configFile}\n        ''',name)\n        \ndef installProxyExe():\n    if useFrpc:\n        print('安装frpc')\n        !mkdir -p $install_path/frpc\n        if Path(frpcExePath).exists():\n            os.system(f'cp -f -n {frpcExePath} $install_path/frpc/frpc')\n        else:\n            !wget \"https://huggingface.co/datasets/ACCA225/Frp/resolve/main/frpc\" -O $install_path/frpc/frpc\n        \n        for ssl in frpcSSLFFlies:\n            if Path(ssl).exists():\n                os.system('cp -f -n '+pathJoin(ssl,'/*')+' $install_path/frpc/')\n        !chmod +x $install_path/frpc/frpc\n        !$install_path/frpc/frpc -v\n    if useNgrok:\n        %pip install pyngrok\n\ndef startProxy():\n    if useNgrok:\n        startNgrok(ngrokToken,webuiPort)\n    if useFrpc:\n        startFrpc('frpc_proxy',frpcStartArg)\n\n    \ndef zipPath(path:str,zipName:str,format='tar'):\n    if path.startswith('$install_path'):\n        path = path.replace('$install_path',install_path)\n    if path.startswith('$output_path'):\n        path = path.replace('$install_path',output_path)\n    if not path.startswith('/'):\n        path = pathJoin(install_path,'/stable-diffusion-webui','/',path)\n    if Path(path).exists():\n        if 'tar' == format:\n            os.system('tar -cf $output_path/'+ zipName +'.tar -C '+ path +' . ')\n        elif 'gz' == format:\n            os.system('tar -czf $output_path/'+ zipName +'.tar.gz -C '+ path +' . ')\n        return\n    print('指定的目录不存在:'+path)\n\n        \ndef check_service(host, port):\n    try:\n        socket.create_connection((host, port), timeout=5)\n        return True\n    except socket.error:\n        return False","metadata":{"_kg_hide-input":true,"id":"coqQvTSLLCtE","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"文件下载工具","metadata":{}},{"cell_type":"code","source":"import os\nimport re\nimport requests\n\ndef download_file(url, filename, path,cache_path = ''):\n    # 获取文件的真实文件名\n    if not filename:\n        with requests.get(url, stream=True) as r:\n            if 'Content-Disposition' in r.headers:\n                filename = r.headers['Content-Disposition'].split('filename=')[1].strip('\"')\n            r.close()\n    filename = re.sub(r'[\\\\/:*?\"<>|;]', '', filename)\n    # 拼接文件的完整路径\n    filepath = os.path.join(path, filename)\n    if cache_path:\n        cache_path = os.path.join(cache_path, filename)\n    # 判断文件是否已存在\n    if os.path.exists(filepath):\n        print(f'{filename} already exists in {path}')\n        return\n    if cache_path and os.path.exists(cache_path):\n        !ln -s -f {cache_path} {filepath}\n    # 下载文件\n    with requests.get(url, stream=True) as r:\n        r.raise_for_status()\n        with open(filepath, 'wb') as f:\n            for chunk in r.iter_content(chunk_size=8192):\n                if chunk:\n                    f.write(chunk)\n    print(f'{filename} has been downloaded to {path}')\n    \n# 加入文件到下载列表\ndef putDownloadFile(url:str,distDir:str,file_name:str=None):\n    if re.match(r'^[^:]+:(https?|ftps?)://', url, flags=0):\n        file_name = re.findall(r'^[^:]+:',url)[0][:-1]\n        url = url[len(file_name)+1:]\n    if not re.match(r'^(https?|ftps?)://',url):\n        return\n    file_name = re.sub(r'\\s+','_',file_name or '')\n    dir = str(hash(url)).replace('-','')\n    down_dir = f'{install_path}/down_cache/{dir}'\n    !mkdir -p {down_dir}\n    return [url,file_name,distDir,down_dir]\n\ndef get_file_size_in_gb(file_path):\n    size_in_bytes = Path(file_path).stat().st_size\n    size_in_gb = size_in_bytes / (1024 ** 3)\n    return '%.2f' % size_in_gb\n    \n# 下载文件\ndef startDownloadFiles(download_list):\n    print('下载列表:\\n','\\n'.join([f'{item[0]} -> {item[2]}/{item[1]}' for item in download_list]))\n    dist_list = []\n    for dow_f in download_list:\n        !mkdir -p {dow_f[3]}\n        print('下载 名称:',dow_f[1],'url:',dow_f[0])\n        download_file(dow_f[0],dow_f[1],dow_f[2],dow_f[3])\n        \n\n","metadata":{"_kg_hide-input":true,"id":"THNUsEm_LCtE","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"NGINX 反向代理","metadata":{}},{"cell_type":"code","source":"\n# nginx 反向代理配置文件\ndef localProxy():\n    conf = '''\nserver\n{\n    listen '''+str(webuiPort)+''';\n    listen [::]:'''+str(webuiPort)+''';\n    server_name 127.0.0.1 localhost 0.0.0.0 \"\";\n    \n    if ($request_method = OPTIONS) {\n        return 200;\n    }\n    fastcgi_send_timeout                 10m;\n    fastcgi_read_timeout                 10m;\n    fastcgi_connect_timeout              10m;\n    location /1/\n    {\n        proxy_pass http://127.0.0.1:'''+str(webuiPort+2)+'''/;\n        # add_header Set-Cookie \"subpath=1; expires=0; Path=/\";\n        # proxy_set_header Set-Cookie \"subpath=1; expires=0; Path=/\";\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header REMOTE-HOST $remote_addr;\n        proxy_set_header   Upgrade $http_upgrade;\n        proxy_set_header   Connection upgrade;\n        proxy_http_version 1.1;\n        proxy_connect_timeout 10m;\n        proxy_read_timeout 10m;\n    }\n    location /\n    {\n        set $proxy_url http://127.0.0.1:'''+str(webuiPort+1)+''';\n        # if ($cookie_subpath = \"1\") {\n        #    set $proxy_url  http://127.0.0.1:'''+str(webuiPort+2)+''';\n        # }\n        proxy_pass $proxy_url;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header REMOTE-HOST $remote_addr;\n        proxy_set_header   Upgrade $http_upgrade;\n        proxy_set_header   Connection upgrade;\n        proxy_http_version 1.1;\n        proxy_connect_timeout 10m;\n        proxy_read_timeout 10m;\n    }\n}\n'''\n    echoToFile(conf,'/etc/nginx/conf.d/proxy_nginx.conf')\n    if not check_service('localhost',webuiPort):\n        !nginx -c /etc/nginx/nginx.conf\n    !nginx -s reload\n    ","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"线程清理工具","metadata":{}},{"cell_type":"code","source":"import inspect\nimport ctypes\n\ndef _async_raise(tid, exctype):\n    \"\"\"raises the exception, performs cleanup if needed\"\"\"\n    tid = ctypes.c_long(tid)\n    if not inspect.isclass(exctype):\n        exctype = type(exctype)\n    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))\n    if res == 0:\n        raise ValueError(\"invalid thread id\")\n    elif res != 1:\n        # \"\"\"if it returns a number greater than one, you're in trouble,\n        # and you should call it again with exc=NULL to revert the effect\"\"\"\n        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)\n        raise SystemError(\"PyThreadState_SetAsyncExc failed\")\n\ndef stop_thread(thread):\n    _async_raise(thread.ident, SystemExit)\n\ndef stop_solo_threads():\n    # 获取当前所有活动的线程\n    threads = threading.enumerate()\n    # 关闭之前创建的子线程\n    for thread in threads:\n        if thread.name.startswith('solo_'):\n            print(thread.name)\n            try:\n                stop_thread(thread)\n            except socket.error:\n                print(f'结束进程:{thread.name} 执行失败')","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# webui 安装和配置函数\n---","metadata":{"id":"Ve3p8oOkLCtE"}},{"cell_type":"code","source":"envInstalled=False\nextensionsDone=False\n\n#安装webui\ndef install():\n    print('安装webui')\n    %cd $install_path\n    with HiddenPrints():\n        for requirement in requirements:\n            !pip install {requirement}\n    if reLoad:\n        !rm -rf stable-diffusion-webui\n    if Path(\"stable-diffusion-webui\").exists():\n        %cd $install_path/stable-diffusion-webui/\n        !git checkout .\n        !git pull\n    else:\n        if mobileOptimize:\n            !git clone https://github.com/viyiviyi/stable-diffusion-webui.git stable-diffusion-webui -b local  # 修改了前端界面,手机使用更方便\n        else:\n            !git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui stable-diffusion-webui\n    %cd {install_path}/stable-diffusion-webui\n    print('安装webui 完成')\n\n# 链接输出目录\ndef link_dir():\n    print('链接输出目录')\n    # 链接输出目录 因为sd被部署在了stable-diffusion-webui目录,运行结束后为了方便下载就只有outputs目录放在output_path/目录下\n    !mkdir -p $output_path/outputs\n    !rm -rf $install_path/stable-diffusion-webui/outputs\n    !ln -s -r $output_path/outputs $install_path/stable-diffusion-webui/ # 输出目录\n    !mkdir -p $output_path/log\n    !rm -rf $install_path/stable-diffusion-webui/log\n    !ln -s -r $output_path/log $install_path/stable-diffusion-webui/\n    # 链接 hypernetworks 目录\n    if linkHypernetworksDir:\n        !rm -rf $install_path/stable-diffusion-webui/models/hypernetworks\n        !mkdir -p $output_path/hypernetworks\n        !ln -s -r $output_path/hypernetworks $install_path/stable-diffusion-webui/models/\n    # 链接 embeddings 目录\n    if linkEmbeddingsDir:\n        !rm -rf $install_path/stable-diffusion-webui/embeddings\n        !mkdir -p $output_path/embeddings\n        !ln -s -r $output_path/embeddings $install_path/stable-diffusion-webui/\n    # 链接训练输出目录 文件夹链接会导致功能不能用\n    if linkTextual_inversionDir:\n        !rm -rf $install_path/stable-diffusion-webui/textual_inversion\n        !mkdir -p $output_path/textual_inversion/\n        !ln -s -r $output_path/textual_inversion $install_path/stable-diffusion-webui/\n    print('链接输出目录 完成')\n    \ndef link_or_download_flie(skip_down:bool,store:dict[str,List[str]]):\n    download_list = []\n    for dist_dir in store.keys():\n        dist_path = os.path.join(f'{install_path}/stable-diffusion-webui',dist_dir)\n        !mkdir -p {dist_path}\n        for path in store[dist_dir]:\n            if 'https://' in path or 'http://' in path:\n                if skip_down:\n                    continue\n                download_list.append(putDownloadFile(path,dist_path))\n            elif pathIsFile(path):\n                os.system(('cp -n' if enableLoadByCopy else 'ln -s')+' -f '+ pathJoin(path) +f' {dist_path}')\n            else:\n                os.system(('cp -n' if enableLoadByCopy else 'ln -s')+' -f '+ pathJoin(path,'/*.*') +f' {dist_path}')\n        !rm -f {dist_path}/\\*.* \n    startDownloadFiles(download_list)\n        \n# 链接模型文件\ndef load_models(skip_url=False):\n    print(('复制' if enableLoadByCopy else '链接') + '模型文件')\n    if enableLoadByCopy:\n        print('如果出现 No such file or directory 错误,可以忽略')\n    store = get_other_files()\n    store['models/Stable-diffusion'] = modelDirs + (store['models/Stable-diffusion'] if 'models/Stable-diffusion' in store else [])\n    store['models/hypernetworks'] = hypernetworksModelDirs + (store['models/hypernetworks'] if 'models/hypernetworks' in store else [])\n    store['models/embeddings'] = embeddingsModelDirs + (store['models/embeddings'] if 'models/embeddings' in store else [])\n    store['models/Lora'] = loraModelDirs + (store['models/Lora'] if 'models/Lora' in store else [])\n    store['models/LyCORIS'] = lyCORISModelDirs + (store['models/LyCORIS'] if 'models/LyCORIS' in store else [])\n    store['models/VAE'] = modelVaeDirs + (store['models/VAE'] if 'models/VAE' in store else [])\n    store['extensions/sd-webui-controlnet/models'] = controlNetModels + (store['extensions/sd-webui-controlnet/models'] if 'extensions/sd-webui-controlnet/models' in store else [])\n    link_or_download_flie(skip_url,store)\n    print(('复制' if enableLoadByCopy else '链接') + '模型文件 完成')\n\n#安装依赖\ndef install_dependencies():\n    print('安装webui需要的python环境')\n    global envInstalled\n    global venvPath\n    print(venvPath)\n    \n    with HiddenPrints():\n        %env TF_CPP_MIN_LOG_LEVEL=1\n        !apt -y update -qq\n        !wget http://launchpadlibrarian.net/367274644/libgoogle-perftools-dev_2.5-2.2ubuntu3_amd64.deb\n        !wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/google-perftools_2.5-2.2ubuntu3_all.deb\n        !wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/libtcmalloc-minimal4_2.5-2.2ubuntu3_amd64.deb\n        !wget https://launchpad.net/ubuntu/+source/google-perftools/2.5-2.2ubuntu3/+build/14795286/+files/libgoogle-perftools4_2.5-2.2ubuntu3_amd64.deb\n        !apt -y install -qq libunwind8-dev\n        !dpkg -i *.deb\n        %env LD_PRELOAD=libtcmalloc.so\n        !rm *.deb\n        !sudo apt install nginx -y\n\n    sh = f'cd {install_path}/stable-diffusion-webui\\n'\n    if str(sys.version).startswith('3.8') or str(sys.version).startswith('3.9') or str(sys.version).startswith('3.10'):\n        sh += 'python -m venv venv\\n'\n    else:\n        sh += '''\nadd-apt-repository ppa:deadsnakes/ppa -y\napt update\napt install python3.10 -y\npython3.10 -m venv venv\n'''\n    if quickStart:\n        if not Path(venvPath).exists():\n            !mkdir -p {install_path}/venv_cache\n            if not Path(f'{install_path}/venv_cache/venv.tar.bak').exists():\n                download_file('https://huggingface.co/viyi/sdwui/resolve/main/venv.zip','venv.zip',f'{install_path}/venv_cache')\n            !unzip {install_path}/venv_cache/venv.zip -d {install_path}/venv_cache\n            venvPath = f'{install_path}/venv_cache/venv.tar.bak'\n            !rm -rf {install_path}/venv_cache/venv.zip\n        elif venvPath.endswith('.zip'):\n            !mkdir -p {install_path}/venv_cache\n            !unzip {venvPath} -d {install_path}/venv_cache\n            venvPath = f'{install_path}/venv_cache/venv.tar.bak'\n\n        sh += 'echo \"unzip venv\"\\n'\n        sh += f'tar -xf {venvPath} -C ./venv\\n'\n    sh += '\\n'\n    with HiddenPrints():\n        run(sh,'dependencies')\n    if not Path(f'{install_path}/stable-diffusion-webui/venv/bin/pip').exists():\n        !curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py\n        with HiddenPrints():\n            !{install_path}/stable-diffusion-webui/venv/bin/python3 get-pip.py\n\n    !{install_path}/stable-diffusion-webui/venv/bin/python3 -V\n    !{install_path}/stable-diffusion-webui/venv/bin/python3 -m pip -V\n\n    if not quickStart or not Path(venvPath).exists():\n        with HiddenPrints():\n            !{install_path}/stable-diffusion-webui/venv/bin/python3 -m pip install -U torch==2.0.0+cu117 torchvision==0.15.1+cu117 torchaudio==2.0.1 --index-url https://download.pytorch.org/whl/cu117\n            !{install_path}/stable-diffusion-webui/venv/bin/python3 -m pip install -U open-clip-torch xformers==0.0.19\n\n    envInstalled = True\n    print('安装webui需要的python环境 完成')\n\n# 安装插件\ndef install_extensions():\n    global extensionsDone\n    print('安装插件')\n    sh = 'mkdir -p $install_path/stable-diffusion-webui/extensions \\n'\n    sh = 'mkdir -p $install_path/cache/extensions \\n'\n    sh += 'cd $install_path/cache/extensions \\n'\n    for ex in extensions:\n        sh += 'git clone ' + ex + ' \\n'\n    sh += 'rsync -a $install_path/cache/extensions/* $install_path/stable-diffusion-webui/extensions \\n'\n    sh += 'cd ../ && rm -rf $install_path/cache/extensions \\n'\n    with HiddenPrints():\n        run(sh,'extensions')\n        extensionsDone = True\n    print('安装插件 完成')\n    \n# 个性化配置 \ndef use_config():\n    print('使用自定义配置 包括tag翻译 \\n')\n    %cd $install_path/stable-diffusion-webui\n    !mkdir -p tmp\n    %cd tmp\n    !git clone {webui_settings} sd-configs\n    !cp -rf sd-configs/dist/* $install_path/stable-diffusion-webui\n    if not Path(ui_config_file).exists(): # ui配置文件\n        !mkdir -p {ui_config_file[0:ui_config_file.rfind('/')]}\n        os.system('cp -f -n $install_path/stable-diffusion-webui/ui-config.json '+ui_config_file)\n    if not Path(setting_file).exists(): # 设置配置文件\n        !mkdir -p {setting_file[0:setting_file.rfind('/')]}\n        os.system('cp -f -n $install_path/stable-diffusion-webui/config.json '+setting_file)\n\ndef checkDefaultModel():\n    print('检查默认模型文件是否存在 \\n')\n    global usedCkpt\n    if usedCkpt is not None and usedCkpt != '': # 设置启动时默认加载的模型\n        if '.' in usedCkpt:\n            if '/' in usedCkpt:\n                usedCkpt = usedCkpt\n            else:\n                usedCkpt = install_path + '/stable-diffusion-webui/models/Stable-diffusion/' + usedCkpt\n        else:\n            for x in ['.ckpt','.safetensors']:\n                if Path(install_path+'/stable-diffusion-webui/models/Stable-diffusion/' + usedCkpt+x).exists():\n                    usedCkpt = install_path+'/stable-diffusion-webui/models/Stable-diffusion/' + usedCkpt+x\n                    break\n    if Path(usedCkpt).exists():\n        return True\n    else:\n        if Path(usedCkpt).is_symlink():\n            print('模型文件真实地址:'+os.readlink(usedCkpt))\n        return False\n\ndef copy_last_log_to_images():\n    print('复制编号最大的一张收藏图到输出目录,用于保持编号,否则会出现收藏的图片被覆盖的情况')\n    img_list = os.listdir(f'{install_path}/stable-diffusion-webui/log/images')\n    last_img_path = ''\n    last_img_num = 0\n    for img in img_list:\n        if re.findall(r'^\\d+-',str(img)):\n            num = int(re.findall(r'^\\d+-',str(img))[0][:-1])\n            if num > last_img_num:\n                last_img_path = img\n                last_img_num = num\n    print(f'{install_path}/stable-diffusion-webui/log/images/{last_img_path} {install_path}/stable-diffusion-webui/outputs/txt2img-images')\n    !mkdir -p {install_path}/stable-diffusion-webui/outputs/txt2img-images\n    !cp -f {install_path}/stable-diffusion-webui/log/images/{last_img_path} {install_path}/stable-diffusion-webui/outputs/txt2img-images/\n    \n    print(f'{install_path}/stable-diffusion-webui/log/images/{last_img_path} {install_path}/stable-diffusion-webui/outputs/img2img-images')\n    !mkdir -p {install_path}/stable-diffusion-webui/outputs/img2img-images\n    !cp -f {install_path}/stable-diffusion-webui/log/images/{last_img_path} {install_path}/stable-diffusion-webui/outputs/img2img-images/\n            \n    \ndef start_webui(i):\n    # 只要不爆内存,其他方式关闭后会再次重启 访问地址会发生变化\n    print(i,'--port',str(webuiPort+1+i))\n    if i>0:\n        print(f'使用第{i+1}张显卡启动第{i+1}个webui,通过frpc或nrgok地址后加/{i}/进行访问(不能使用同一个浏览器)')\n    if useFrpc:\n        while True:\n            !venv/bin/python3 launch.py --device-id={i} --port {str(webuiPort+1+i)} {'' if i ==0 else '--nowebui'} # {'' if i == 0 else '--subpath=/'+str(i)}\n            print('5秒后重启webui')\n            time.sleep(5)\n    else:\n        !venv/bin/python3 launch.py --device-id={i} --port {str(webuiPort+1+i)} {'' if i ==0 else '--nowebui'} # {'' if i == 0 else '--subpath=/'+str(i)}\n    \n# 启动\ndef start():\n    print('启动webui')\n    %cd $install_path/stable-diffusion-webui\n    args = '' # ' --port=' + str(webuiPort+1)\n    if not disableShared:\n        args += ' --share'\n    if onlyApi:\n        args += ' --nowebui'\n    if ui_config_file is not None and ui_config_file != '' and Path(ui_config_file).exists(): # ui配置文件\n        args += ' --ui-config-file=' + pathJoin(ui_config_file)\n    if setting_file is not None and setting_file != '' and Path(setting_file).exists(): # 设置配置文件\n        args += ' --ui-settings-file=' + pathJoin(setting_file)\n    if not checkDefaultModel():\n        print('默认模型文件不存在,请检查配置:'+usedCkpt)\n    else:\n        args += ' --ckpt='+ pathJoin(usedCkpt)\n    if vaeHalf is False: \n        args += ' --no-half-vae'\n    if modelHalf is False:\n        args += ' --no-half'\n    if consoleProgressbars is False:\n        args += ' --disable-console-progressbars'\n    if consolePrompts is True:\n        args += ' --enable-console-prompts'\n    args += ' ' + otherArgs\n    os.environ['COMMANDLINE_ARGS']=args\n    !echo COMMANDLINE_ARGS=$COMMANDLINE_ARGS\n    %env REQS_FILE=requirements.txt\n    \n    threads = []\n    for i in range(torch.cuda.device_count()):\n        thread = threading.Thread(target = start_webui, name='solo_start_webui_'+str(i), daemon=True,args=([i]))\n        thread.start()\n        threads.append(thread)\n        while thread.is_alive() and not check_service('localhost',str(webuiPort+1+i)): # 当当前服务启动完成才允许退出此次循环\n            time.sleep(10)\n        time.sleep(5)\n    for thread in threads:\n        thread.join()","metadata":{"_kg_hide-input":true,"id":"GTjyBJihLCtE","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 入口函数\n---","metadata":{"id":"qLvsk8ByLCtF"}},{"cell_type":"code","source":"\n# 启动非webui相关的的内容,加快启动速度\ndef main():\n    global envInstalled\n    global extensionsDone\n    global huggingface_is_init\n    startTicks = time.time()\n    isInstall = True if os.getenv('IsInstall','False') == 'True' else False\n    if isInstall is False or reLoad: \n        print('启动 安装webui和运行环境')\n        install()\n        link_dir()\n        init_huggingface()\n        if enableThread:\n            threading.Thread(target = install_extensions,daemon=True, name='solo_install_extensions').start()\n            threading.Thread(target = load_models,daemon=True).start()\n            threading.Thread(target = install_dependencies,daemon=True, name='solo_install_dependencies').start()\n            if huggingface_is_init:\n                threading.Thread(target = download_huggingface_repo,daemon=True,\n                                 args=([huggingface_repo]),\n                                 kwargs={\"callback\":copy_last_log_to_images},\n                                 name='solo_download_huggingface_repo').start()\n\n        else:\n            install_extensions()\n            load_models()\n            install_dependencies()\n            if huggingface_is_init:\n                download_huggingface_repo(huggingface_repo,callback=copy_last_log_to_images)\n        t = 0\n        while not envInstalled or not extensionsDone:\n            if t%10==0:\n                print('等待python环境和插件安装...')\n            t = t+1\n            time.sleep(1)\n        use_config()\n        installProxyExe()\n        os.environ['IsInstall'] = 'True'\n    else:\n        envInstalled = True\n        extensionsDone = True\n    sys.stdout = sys_sys_stdout\n    stop_solo_threads()\n    localProxy()\n    threading.Thread(target = startProxy, daemon=True, name='solo_startProxy').start()\n    if init_huggingface():\n        start_sync_log_to_huggingface(huggingface_repo)\n    load_models(True)\n    ticks = time.time()\n    print(\"加载耗时:\",(ticks - startTicks),\"\")\n    start()\n","metadata":{"_kg_hide-input":true,"id":"IOKjaMlcLCtF","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 执行区域\n---","metadata":{"id":"0oaCRs2gLCtF"}},{"cell_type":"code","source":"# 启动\n# reLoad = True\n# hidden_console_info = False\nsys.stdout = sys_sys_stdout\nmain()","metadata":{"_kg_hide-output":true,"id":"O3DR0DWHLCtF","scrolled":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 停止一些不必要的后台线程\nstop_solo_threads()","metadata":{"scrolled":true,"trusted":true},"execution_count":null,"outputs":[]}]}