mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2025-08-13 17:57:20 +02:00
Compare commits
119 Commits
Author | SHA1 | Date | |
---|---|---|---|
80ecb82cc2 | |||
7fc9509d4d | |||
fabdf5fe30 | |||
1cc27e524b | |||
888c637f71 | |||
48b7d2587e | |||
923c889de8 | |||
8ae575d67a | |||
b51407486a | |||
a689b34ed1 | |||
aa98e60243 | |||
b641f1a230 | |||
9499685dda | |||
80d23cbbbf | |||
efa684c5e8 | |||
2edf64985d | |||
5fe7807462 | |||
e96b9005ca | |||
8c29e735e7 | |||
497e073a8c | |||
d4ce54a3c2 | |||
de37a81902 | |||
d6b8cb718a | |||
ed435d2b72 | |||
2b1f8533b0 | |||
5ebc6b698c | |||
577dd9048f | |||
ae409dd0ec | |||
cde855e1dc | |||
adcd4368e7 | |||
8bcdb205ed | |||
2cf8b2a453 | |||
6c156380f9 | |||
369d0ee502 | |||
4971a212e9 | |||
2111a81d18 | |||
6799b3d7da | |||
a3463274ee | |||
c10e773401 | |||
f7af259576 | |||
87c6a54634 | |||
d03521bf12 | |||
3eb1919c81 | |||
e53d6dbd5c | |||
01d2db8e96 | |||
b18c2aea05 | |||
a6e3c272e2 | |||
4000f98ba4 | |||
d06fd404ae | |||
c6f0e19e2f | |||
462af9989a | |||
eedea2fdcd | |||
9c3d946de0 | |||
ace3102601 | |||
48946100e9 | |||
0067e46192 | |||
921711a679 | |||
32dfb765dd | |||
8482f12909 | |||
306a56124c | |||
1f815d7562 | |||
f25d35fad8 | |||
f74c57449e | |||
a697bd935a | |||
ec294227bd | |||
f67758eaf3 | |||
f7ed65d749 | |||
7ffeb3964b | |||
025d4df774 | |||
45086a4b6e | |||
2db0023653 | |||
bfc21220a7 | |||
507491fbec | |||
c890ef6917 | |||
6756fb4fe7 | |||
6c089c0a78 | |||
b2ab3f987c | |||
c99b2edf98 | |||
e052610184 | |||
13f1725105 | |||
f2367932e1 | |||
7e94ec986e | |||
7bda3e6994 | |||
3a18606385 | |||
e25a94e815 | |||
c13f662e2d | |||
97ee085f30 | |||
1364fd5c45 | |||
cc3186a683 | |||
0c93c4754d | |||
7b4cfbeeaa | |||
8cebb53147 | |||
3e18f2f09c | |||
add09e52ef | |||
5429a509c6 | |||
3555fa36aa | |||
ee31519552 | |||
06b41aee58 | |||
cd19d50e1d | |||
a5e4eea5ca | |||
c6a6270e16 | |||
18d9d2602a | |||
f7ec9f2073 | |||
4c5f66185d | |||
105c1893cb | |||
061cee207f | |||
af4a925b54 | |||
a6f3e87921 | |||
9764d9109f | |||
46dfa57ee0 | |||
7f436061b8 | |||
332f2b0678 | |||
39b6c5d6f4 | |||
903acff924 | |||
bd5a6e6fb3 | |||
9b89ede9c4 | |||
bf205de3a1 | |||
8165086d02 | |||
82f14b087a |
39
CHANGES.md
Normal file
39
CHANGES.md
Normal file
@ -0,0 +1,39 @@
|
||||
# What's new?
|
||||
|
||||
## v2.4
|
||||
### Major Changes
|
||||
- **Automatic scanning for malicious model files** - using `picklescan`. Thanks @JeLuf
|
||||
- **Support for custom VAE models**. You can place your VAE files in the `models/vae` folder, and refresh the browser page to use them. More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/VAE-Variational-Auto-Encoder
|
||||
- **Experimental support for multiple GPUs!** It should work automatically. Just open one browser tab per GPU, and spread your tasks across your GPUs. For e.g. open our UI in two browser tabs if you have two GPUs. You can customize which GPUs it should use in the "Settings" tab, otherwise let it automatically pick the best GPUs. Thanks @madrang . More info: https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs
|
||||
- **Cleaner UI design** - Show settings and help in new tabs, instead of dropdown popups (which were buggy). Thanks @mdiller
|
||||
- **Progress bar.** Thanks @mdiller
|
||||
- **Custom Image Modifiers** - You can now save your custom image modifiers! Your saved modifiers can include special characters like `{}, (), [], |`
|
||||
- Drag and Drop **text files generated from previously saved images**, and copy settings to clipboard. Thanks @madrang
|
||||
- Paste settings from clipboard. Thanks @JeLuf
|
||||
- Bug fixes to reduce the chances of tasks crashing during long multi-hour runs (chrome can put long-running background tabs to sleep). Thanks @JeLuf and @madrang
|
||||
- **Improved documentation.** Thanks @JeLuf and @jsuelwald
|
||||
- Improved the codebase for dealing with system settings and UI settings. Thanks @mdiller
|
||||
- Help instructions next to some setttings, and in the tab
|
||||
- Show system info in the settings tab
|
||||
- Keyboard shortcut: Ctrl+Enter to start a task
|
||||
- Configuration to prevent the browser from opening on startup
|
||||
- Lots of minor bug fixes
|
||||
- A `What's New?` tab in the UI
|
||||
|
||||
### Detailed changelog
|
||||
* 2.4.13 - 21 Nov 2022 - Change the modifier weight via mouse wheel, drag to reorder selected modifiers, and some more modifier-related fixes. Thanks @patriceac
|
||||
* 2.4.12 - 21 Nov 2022 - Another fix for improving how long images take to generate. Reduces the time taken for an enqueued task to start processing.
|
||||
* 2.4.11 - 21 Nov 2022 - Installer improvements: avoid crashing if the username contains a space or special characters, allow moving/renaming the folder after installation on Windows, whitespace fix on git apply
|
||||
* 2.4.11 - 21 Nov 2022 - Validate inputs before submitting the Image request
|
||||
* 2.4.11 - 19 Nov 2022 - New system settings to manage the network config (port number and whether to only listen on localhost)
|
||||
* 2.4.11 - 19 Nov 2022 - Address a regression in how long images take to generate. Use the previous code for moving a model to CPU. This improves things by a second or two per image, but we still have a regression (investigating).
|
||||
* 2.4.10 - 18 Nov 2022 - Textarea for negative prompts. Thanks @JeLuf
|
||||
* 2.4.10 - 18 Nov 2022 - Improved design for Settings, and rounded toggle buttons instead of checkboxes for a more modern look. Thanks @mdiller
|
||||
* 2.4.9 - 18 Nov 2022 - Add Picklescan - a scanner for malicious model files. If it finds a malicious file, it will halt the web application and alert the user. Thanks @JeLuf
|
||||
* 2.4.8 - 18 Nov 2022 - A `Use these settings` button to use the settings from a previously generated image task. Thanks @patriceac
|
||||
* 2.4.7 - 18 Nov 2022 - Don't crash if a VAE file fails to load
|
||||
* 2.4.7 - 17 Nov 2022 - Fix a bug where Face Correction (GFPGAN) would fail on cuda:N (i.e. GPUs other than cuda:0), as well as fail on CPU if the system had an incompatible GPU.
|
||||
* 2.4.6 - 16 Nov 2022 - Fix a regression in VRAM usage during startup, which caused 'Out of Memory' errors when starting on GPUs with 4gb (or less) VRAM
|
||||
* 2.4.5 - 16 Nov 2022 - Add checkbox for "Open browser on startup".
|
||||
* 2.4.5 - 16 Nov 2022 - Add a directory for core plugins that ship with Stable Diffusion UI by default.
|
||||
* 2.4.5 - 16 Nov 2022 - Add a "What's New?" tab as a core plugin, which fetches the contents of CHANGES.md from the app's release branch.
|
@ -40,6 +40,7 @@ or for Windows
|
||||
`mklink /J \projects\stable-diffusion-ui-archive\ui \projects\stable-diffusion-ui-repo\ui` (link name first, source repo dir second)
|
||||
9) Run the project again (like in step 2) and ensure you can still use the UI.
|
||||
10) Congrats, now any changes you make in your repo `ui` folder are linked to this running archive of the app and can be previewed in the browser.
|
||||
11) Please update CHANGES.md in your pull requests.
|
||||
|
||||
Check the `ui/frontend/build/README.md` for instructions on running and building the React code.
|
||||
|
||||
|
1
NSIS/README.md
Normal file
1
NSIS/README.md
Normal file
@ -0,0 +1 @@
|
||||
Scripts to be used with the Nullsoft Scriptable Installation System
|
BIN
NSIS/astro.bmp
Normal file
BIN
NSIS/astro.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 KiB |
BIN
NSIS/sd.ico
Normal file
BIN
NSIS/sd.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
265
NSIS/sdui.nsi
Normal file
265
NSIS/sdui.nsi
Normal file
@ -0,0 +1,265 @@
|
||||
; Script generated by the HM NIS Edit Script Wizard.
|
||||
|
||||
Target x86-unicode
|
||||
Unicode True
|
||||
!AddPluginDir /x86-unicode "."
|
||||
; HM NIS Edit Wizard helper defines
|
||||
!define PRODUCT_NAME "Stable Diffusion UI"
|
||||
!define PRODUCT_VERSION "Installer 2.35"
|
||||
!define PRODUCT_PUBLISHER "cmdr2 and contributors"
|
||||
!define PRODUCT_WEB_SITE "https://stable-diffusion-ui.github.io"
|
||||
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Cmdr2\App Paths\installer.exe"
|
||||
|
||||
; MUI 1.67 compatible ------
|
||||
!include "MUI.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
!include "nsDialogs.nsh"
|
||||
|
||||
Var Dialog
|
||||
Var Label
|
||||
Var Button
|
||||
|
||||
Var InstDirLen
|
||||
Var LongPathsEnabled
|
||||
Var AccountType
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; This function returns the number of spaces in a string.
|
||||
; The string is passed on the stack (using Push $STRING)
|
||||
; The result is also returned on the stack and can be consumed with Pop $var
|
||||
; https://nsis.sourceforge.io/Check_for_spaces_in_a_directory_path
|
||||
Function CheckForSpaces
|
||||
Exch $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
StrCpy $R1 -1
|
||||
StrCpy $R3 $R0
|
||||
StrCpy $R0 0
|
||||
loop:
|
||||
StrCpy $R2 $R3 1 $R1
|
||||
IntOp $R1 $R1 - 1
|
||||
StrCmp $R2 "" done
|
||||
StrCmp $R2 " " 0 loop
|
||||
IntOp $R0 $R0 + 1
|
||||
Goto loop
|
||||
done:
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Exch $R0
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; The function DirectoryLeave is called after the user chose the installation directory.
|
||||
; If it calls "abort", the user is sent back to choose a different directory.
|
||||
Function DirectoryLeave
|
||||
; check whether the installation directory path is longer than 30 characters.
|
||||
; If yes, we suggest to the user to enable long filename support
|
||||
;----------------------------------------------------------------------------
|
||||
StrLen $InstDirLen "$INSTDIR"
|
||||
|
||||
; Check whether the registry key that allows for >260 characters in a path name is set
|
||||
ReadRegStr $LongPathsEnabled HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled"
|
||||
|
||||
${If} $InstDirLen > 30
|
||||
${AndIf} $LongPathsEnabled == "0"
|
||||
; Check whether we're in the Admin group
|
||||
UserInfo::GetAccountType
|
||||
Pop $AccountType
|
||||
|
||||
${If} $AccountType == "Admin"
|
||||
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION 'The path name is too long. $\n$\nYou can either enable long file name support in Windows,$\nor you can go back and choose a different path.$\n$\nFor details see: shorturl.at/auBD1$\n$\nEnable long path name support in Windows?' IDYES`
|
||||
; Enable long path names
|
||||
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
|
||||
${Else}
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Installation path name too long. The installation path must not have more than 30 characters."
|
||||
abort
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
; Check for spaces in the installation directory path.
|
||||
; ----------------------------------------------------
|
||||
|
||||
; $R0 = CheckForSpaces( $INSTDIR )
|
||||
Push $INSTDIR # Input string (install path).
|
||||
Call CheckForSpaces
|
||||
Pop $R0 # The function returns the number of spaces found in the input string.
|
||||
|
||||
; Check if any spaces exist in $INSTDIR.
|
||||
${If} $R0 != 0
|
||||
; Plural if more than 1 space in $INSTDIR.
|
||||
; If $R0 == 1: $R1 = ""; else: $R1 = "s"
|
||||
StrCmp $R0 1 0 +3
|
||||
StrCpy $R1 ""
|
||||
Goto +2
|
||||
StrCpy $R1 "s"
|
||||
|
||||
; Show message box then take the user back to the Directory page.
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Error: The Installaton directory \
|
||||
has $R0 space character$R1.$\nPlease choose an installation directory without space characters."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
; Check for NTFS filesystem. Installations on FAT fail.
|
||||
; -----------------------------------------------------
|
||||
StrCpy $5 $INSTDIR 3
|
||||
System::Call 'Kernel32::GetVolumeInformation(t "$5",t,i ${NSIS_MAX_STRLEN},*i,*i,*i,t.r1,i ${NSIS_MAX_STRLEN})i.r0'
|
||||
${If} $0 <> 0
|
||||
${AndIf} $1 == "NTFS"
|
||||
MessageBox mb_ok "$5 has filesystem type '$1'.$\nOnly NTFS filesystems are supported.$\nPlease choose a different drive."
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Open the MS download page in a browser and enable the [Next] button
|
||||
Function MSMediaFeaturepack
|
||||
ExecShell "open" "https://www.microsoft.com/en-us/software-download/mediafeaturepack"
|
||||
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 1
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Install the MS Media Feature Pack, if it is missing (e.g. on Windows 10 N)
|
||||
Function MediaPackDialog
|
||||
!insertmacro MUI_HEADER_TEXT "Windows Media Feature Pack" "Required software module is missing"
|
||||
|
||||
; Skip this dialog if mf.dll is installed
|
||||
${If} ${FileExists} "$WINDIR\system32\mf.dll"
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $Dialog
|
||||
|
||||
${If} $Dialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
${NSD_CreateLabel} 0 0 100% 48u "The Windows Media Feature Pack is missing on this computer. It is required for the Stable Diffusion UI.$\nYou can continue the installation after installing the Windows Media Feature Pack."
|
||||
Pop $Label
|
||||
|
||||
${NSD_CreateButton} 10% 49u 80% 12u "Download Meda Feature Pack from Microsoft"
|
||||
Pop $Button
|
||||
|
||||
GetFunctionAddress $0 MSMediaFeaturePack
|
||||
nsDialogs::OnClick $Button $0
|
||||
GetDlgItem $0 $HWNDPARENT 1
|
||||
EnableWindow $0 0
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI Settings
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_ICON "sd.ico"
|
||||
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "astro.bmp"
|
||||
|
||||
; Welcome page
|
||||
!define MUI_WELCOMEPAGE_TEXT "This installer will guide you through the installation of Stable Diffusion UI.$\n$\n\
|
||||
Click Next to continue."
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
Page custom MediaPackDialog
|
||||
|
||||
; License page
|
||||
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
|
||||
!insertmacro MUI_PAGE_LICENSE "..\CreativeML Open RAIL-M License"
|
||||
; Directory page
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE "DirectoryLeave"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
; Instfiles page
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
; Finish page
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Language files
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; MUI end
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
OutFile "Install Stable Diffusion UI.exe"
|
||||
InstallDir "C:\Stable-Diffusion-UI\"
|
||||
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
|
||||
ShowInstDetails show
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; List of files to be installed
|
||||
Section "MainSection" SEC01
|
||||
SetOutPath "$INSTDIR"
|
||||
File "..\CreativeML Open RAIL-M License"
|
||||
File "..\How to install and run.txt"
|
||||
File "..\LICENSE"
|
||||
File "..\Start Stable Diffusion UI.cmd"
|
||||
SetOutPath "$INSTDIR\scripts"
|
||||
File "..\scripts\bootstrap.bat"
|
||||
File "..\scripts\install_status.txt"
|
||||
File "..\scripts\on_env_start.bat"
|
||||
File "C:\windows\system32\curl.exe"
|
||||
CreateDirectory "$INSTDIR\profile"
|
||||
CreateDirectory "$SMPROGRAMS\Stable Diffusion UI"
|
||||
CreateShortCut "$SMPROGRAMS\Stable Diffusion UI\Start Stable Diffusion UI.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||
SectionEnd
|
||||
|
||||
;---------------------------------------------------------------------------------------------------------
|
||||
; Our installer only needs 25 KB, but once it has run, we need 25 GB
|
||||
; So we need to overwrite the automatically detected space requirements.
|
||||
; https://nsis.sourceforge.io/Docs/Chapter4.html#4.9.13.7
|
||||
; The example in section 4.9.13.7 seems to be wrong: the number
|
||||
; needs to be provided in Kilobytes.
|
||||
Function .onInit
|
||||
; Set required size of section 'SEC01' to 25 Gigabytes
|
||||
SectionSetSize ${SEC01} 26214400
|
||||
|
||||
|
||||
; Check system meory size. We need at least 8GB
|
||||
; ----------------------------------------------------
|
||||
|
||||
; allocate a few bytes of memory
|
||||
System::Alloc 64
|
||||
Pop $1
|
||||
|
||||
; Retrieve HW info from the Windows Kernel
|
||||
System::Call "*$1(i64)"
|
||||
System::Call "Kernel32::GlobalMemoryStatusEx(i r1)"
|
||||
; unpack the data into $R2 - $R10
|
||||
System::Call "*$1(i.r2, i.r3, l.r4, l.r5, l.r6, l.r7, l.r8, l.r9, l.r10)"
|
||||
|
||||
# free up the memory
|
||||
System::Free $1
|
||||
|
||||
; Result mapping:
|
||||
; "Structure size: $2 bytes"
|
||||
; "Memory load: $3%"
|
||||
; "Total physical memory: $4 bytes"
|
||||
; "Free physical memory: $5 bytes"
|
||||
; "Total page file: $6 bytes"
|
||||
; "Free page file: $7 bytes"
|
||||
; "Total virtual: $8 bytes"
|
||||
; "Free virtual: $9 bytes"
|
||||
|
||||
; Mem size in MB
|
||||
System::Int64Op $4 / 1048576
|
||||
Pop $4
|
||||
|
||||
${If} $4 < "8000"
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Warning!$\n$\nYour system has less than 8GB of memory (RAM).$\n$\n\
|
||||
You can still try to install Stable Diffusion UI,$\nbut it might have problems to start, or run$\nvery slowly."
|
||||
${EndIf}
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;Section -Post
|
||||
; WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\installer.exe"
|
||||
;SectionEnd
|
@ -1,5 +1,8 @@
|
||||
@echo off
|
||||
|
||||
@REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
@REM Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
|
||||
@copy sd-ui-files\scripts\on_env_start.bat scripts\ /Y
|
||||
@copy sd-ui-files\scripts\bootstrap.bat scripts\ /Y
|
||||
|
||||
@ -7,11 +10,17 @@ if exist "%cd%\profile" (
|
||||
set USERPROFILE=%cd%\profile
|
||||
)
|
||||
|
||||
@mkdir tmp
|
||||
@set TMP=%cd%\tmp
|
||||
@set TEMP=%cd%\tmp
|
||||
|
||||
@rem activate the installer env
|
||||
call conda activate
|
||||
|
||||
@REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
|
||||
@REM Note to self: Please rewrite this in Python. For the sake of your own sanity.
|
||||
@rem @if "%ERRORLEVEL%" NEQ "0" (
|
||||
@rem @echo. & echo "Error activating conda for Stable Diffusion. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!" & echo.
|
||||
@rem pause
|
||||
@rem exit /b
|
||||
@rem )
|
||||
|
||||
@REM remove the old version of the dev console script, if it's still present
|
||||
if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
@ -28,8 +37,8 @@ if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
@call git pull
|
||||
@call git -c advice.detachedHead=false checkout f6cfebffa752ee11a7b07497b8529d5971de916c
|
||||
|
||||
@call git apply ..\ui\sd_internal\ddim_callback.patch
|
||||
@call git apply ..\ui\sd_internal\env_yaml.patch
|
||||
@call git apply --whitespace=nowarn ..\ui\sd_internal\ddim_callback.patch
|
||||
@call git apply --whitespace=nowarn ..\ui\sd_internal\env_yaml.patch
|
||||
|
||||
@cd ..
|
||||
) else (
|
||||
@ -46,8 +55,8 @@ if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
@cd stable-diffusion
|
||||
@call git -c advice.detachedHead=false checkout f6cfebffa752ee11a7b07497b8529d5971de916c
|
||||
|
||||
@call git apply ..\ui\sd_internal\ddim_callback.patch
|
||||
@call git apply ..\ui\sd_internal\env_yaml.patch
|
||||
@call git apply --whitespace=nowarn ..\ui\sd_internal\ddim_callback.patch
|
||||
@call git apply --whitespace=nowarn ..\ui\sd_internal\env_yaml.patch
|
||||
|
||||
@cd ..
|
||||
)
|
||||
@ -68,8 +77,6 @@ if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
set TMP=%cd%\tmp
|
||||
set TEMP=%cd%\tmp
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
@ -107,8 +114,6 @@ set PATH=C:\Windows\System32;%PATH%
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
set TMP=%cd%\tmp
|
||||
set TEMP=%cd%\tmp
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
@ -142,8 +147,6 @@ set PATH=C:\Windows\System32;%PATH%
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
set TMP=%cd%\tmp
|
||||
set TEMP=%cd%\tmp
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
@ -171,8 +174,6 @@ set PATH=C:\Windows\System32;%PATH%
|
||||
@set PYTHONNOUSERSITE=1
|
||||
|
||||
set USERPROFILE=%cd%\profile
|
||||
set TMP=%cd%\tmp
|
||||
set TEMP=%cd%\tmp
|
||||
|
||||
set PYTHONPATH=%cd%;%cd%\env\lib\site-packages
|
||||
|
||||
@ -191,6 +192,16 @@ call WHERE uvicorn > .tmp
|
||||
exit /b
|
||||
)
|
||||
|
||||
@>nul 2>nul call python -m picklescan --help
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo. & echo Picklescan not found. Installing
|
||||
@call pip install picklescan || (
|
||||
echo "Error installing the picklescan package necessary for Stable Diffusion UI. Sorry about that, please try to:" & echo " 1. Run this installer again." & echo " 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting" & echo " 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB" & echo " 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues" & echo "Thanks!"
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
)
|
||||
|
||||
@>nul findstr /m "conda_sd_ui_deps_installed" ..\scripts\install_status.txt
|
||||
@if "%ERRORLEVEL%" NEQ "0" (
|
||||
@echo conda_sd_ui_deps_installed >> ..\scripts\install_status.txt
|
||||
@ -275,7 +286,7 @@ echo. > "..\models\vae\Put your VAE files here.txt"
|
||||
for %%I in ("RealESRGAN_x4plus.pth") do if "%%~zI" EQU "67040989" (
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The GFPGAN model file present at %cd%\RealESRGAN_x4plus.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
echo. & echo "The RealESRGAN model file present at %cd%\RealESRGAN_x4plus.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "RealESRGAN_x4plus.pth"
|
||||
)
|
||||
)
|
||||
@ -305,7 +316,7 @@ echo. > "..\models\vae\Put your VAE files here.txt"
|
||||
for %%I in ("RealESRGAN_x4plus_anime_6B.pth") do if "%%~zI" EQU "17938799" (
|
||||
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded"
|
||||
) else (
|
||||
echo. & echo "The GFPGAN model file present at %cd%\RealESRGAN_x4plus_anime_6B.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
echo. & echo "The RealESRGAN model file present at %cd%\RealESRGAN_x4plus_anime_6B.pth is invalid. It is only %%~zI bytes in size. Re-downloading.." & echo.
|
||||
del "RealESRGAN_x4plus_anime_6B.pth"
|
||||
)
|
||||
)
|
||||
@ -383,6 +394,14 @@ call python --version
|
||||
@set SD_UI_PATH=%cd%\ui
|
||||
@cd stable-diffusion
|
||||
|
||||
@rem
|
||||
@rem Rewrite easy-install.pth. This fixes the installation if the user has relocated the SDUI installation
|
||||
@rem
|
||||
>env\Lib\site-packages\easy-install.pth echo %cd%\src\taming-transformers
|
||||
>>env\Lib\site-packages\easy-install.pth echo %cd%\src\clip
|
||||
>>env\Lib\site-packages\easy-install.pth echo %cd%\src\gfpgan
|
||||
>>env\Lib\site-packages\easy-install.pth echo %cd%\src\realesrgan
|
||||
|
||||
@if NOT DEFINED SD_UI_BIND_PORT set SD_UI_BIND_PORT=9000
|
||||
@if NOT DEFINED SD_UI_BIND_IP set SD_UI_BIND_IP=0.0.0.0
|
||||
@uvicorn server:app --app-dir "%SD_UI_PATH%" --port %SD_UI_BIND_PORT% --host %SD_UI_BIND_IP%
|
||||
|
@ -30,8 +30,8 @@ if [ -e "scripts/install_status.txt" ] && [ `grep -c sd_git_cloned scripts/insta
|
||||
git pull
|
||||
git -c advice.detachedHead=false checkout f6cfebffa752ee11a7b07497b8529d5971de916c
|
||||
|
||||
git apply ../ui/sd_internal/ddim_callback.patch || fail "ddim patch failed"
|
||||
git apply ../ui/sd_internal/env_yaml.patch || fail "yaml patch failed"
|
||||
git apply --whitespace=nowarn ../ui/sd_internal/ddim_callback.patch || fail "ddim patch failed"
|
||||
git apply --whitespace=nowarn ../ui/sd_internal/env_yaml.patch || fail "yaml patch failed"
|
||||
|
||||
cd ..
|
||||
else
|
||||
@ -46,8 +46,8 @@ else
|
||||
cd stable-diffusion
|
||||
git -c advice.detachedHead=false checkout f6cfebffa752ee11a7b07497b8529d5971de916c
|
||||
|
||||
git apply ../ui/sd_internal/ddim_callback.patch || fail "ddim patch failed"
|
||||
git apply ../ui/sd_internal/env_yaml.patch || fail "yaml patch failed"
|
||||
git apply --whitespace=nowarn ../ui/sd_internal/ddim_callback.patch || fail "ddim patch failed"
|
||||
git apply --whitespace=nowarn ../ui/sd_internal/env_yaml.patch || fail "yaml patch failed"
|
||||
|
||||
cd ..
|
||||
fi
|
||||
@ -156,6 +156,13 @@ else
|
||||
echo conda_sd_ui_deps_installed >> ../scripts/install_status.txt
|
||||
fi
|
||||
|
||||
if python -m picklescan --help >/dev/null 2>&1; then
|
||||
echo "Picklescan is already installed."
|
||||
else
|
||||
echo "Picklescan not found, installing."
|
||||
pip install picklescan || fail "Picklescan installation failed."
|
||||
fi
|
||||
|
||||
|
||||
|
||||
mkdir -p "../models/stable-diffusion"
|
||||
|
@ -5,21 +5,22 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/png" href="/media/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="icon" type="image/png" href="/media/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="stylesheet" href="/media/css/fonts.css?v=1">
|
||||
<link rel="stylesheet" href="/media/css/themes.css?v=3">
|
||||
<link rel="stylesheet" href="/media/css/main.css?v=17">
|
||||
<link rel="stylesheet" href="/media/css/auto-save.css?v=5">
|
||||
<link rel="stylesheet" href="/media/css/modifier-thumbnails.css?v=4">
|
||||
<link rel="stylesheet" href="/media/css/fontawesome-all.min.css?v=1">
|
||||
<link rel="stylesheet" href="/media/css/fonts.css">
|
||||
<link rel="stylesheet" href="/media/css/themes.css">
|
||||
<link rel="stylesheet" href="/media/css/main.css">
|
||||
<link rel="stylesheet" href="/media/css/auto-save.css">
|
||||
<link rel="stylesheet" href="/media/css/modifier-thumbnails.css">
|
||||
<link rel="stylesheet" href="/media/css/fontawesome-all.min.css">
|
||||
<link rel="stylesheet" href="/media/css/drawingboard.min.css">
|
||||
<script src="/media/js/jquery-3.6.1.min.js"></script>
|
||||
<script src="/media/js/drawingboard.min.js"></script>
|
||||
<script src="/media/js/marked.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="top-nav">
|
||||
<div id="logo">
|
||||
<h1>Stable Diffusion UI <small>v2.4.5 <span id="updateBranchLabel"></span></small></h1>
|
||||
<h1>Stable Diffusion UI <small>v2.4.13 <span id="updateBranchLabel"></span></small></h1>
|
||||
</div>
|
||||
<div id="server-status">
|
||||
<div id="server-status-color">●</div>
|
||||
@ -46,14 +47,13 @@
|
||||
<label for="prompt"><b>Enter Prompt</b></label> <small>or</small> <button id="promptsFromFileBtn">Load from a file</button>
|
||||
<textarea id="prompt" class="col-free">a photograph of an astronaut riding a horse</textarea>
|
||||
<input id="prompt_from_file" name="prompt_from_file" type="file" /> <!-- hidden -->
|
||||
|
||||
<label for="negative_prompt" class="collapsible" id="negative_prompt_handle">
|
||||
Negative Prompt
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Writing-prompts#negative-prompts" target="_blank"><i class="fa-solid fa-circle-question help-btn"><span class="simple-tooltip right">Click to learn more about Negative Prompts</span></i></a>
|
||||
<small>(optional)</small>
|
||||
</label>
|
||||
<div class="collapsible-content">
|
||||
<input id="negative_prompt" name="negative_prompt" placeholder="list the things to remove from the image (e.g. fog, green)">
|
||||
<textarea id="negative_prompt" name="negative_prompt" placeholder="list the things to remove from the image (e.g. fog, green)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
<div id="editor-inputs-tags-list"></div>
|
||||
</div>
|
||||
|
||||
<button id="makeImage">Make Image</button>
|
||||
<button id="makeImage" class="primaryButton">Make Image</button>
|
||||
<button id="stopImage" class="secondaryButton">Stop All</button>
|
||||
</div>
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
<h4 class="collapsible">
|
||||
Image Settings
|
||||
<i id="reset-image-settings" class="fa-solid fa-arrow-rotate-left section-button">
|
||||
<span class="simple-tooltip left">
|
||||
<span class="simple-tooltip right">
|
||||
Reset Image Settings
|
||||
</span>
|
||||
</i>
|
||||
@ -241,7 +241,7 @@
|
||||
<div id="tab-content-settings" class="tab-content">
|
||||
<div id="system-settings" class="tab-content-inner">
|
||||
<h1>System Settings</h1>
|
||||
<table class="form-table"></table>
|
||||
<div class="parameters-table"></div>
|
||||
<br/>
|
||||
<button id="save-system-settings-btn" class="primaryButton">Save</button>
|
||||
<br/><br/>
|
||||
@ -263,6 +263,7 @@
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Overview" target="_blank"><i class="fa-solid fa-list fa-fw"></i> UI Overview</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Writing-Prompts" target="_blank"><i class="fa-solid fa-pen-to-square fa-fw"></i> Writing prompts</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Inpainting" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Inpainting</a>
|
||||
<li> <a href="https://github.com/cmdr2/stable-diffusion-ui/wiki/Run-on-Multiple-GPUs" target="_blank"><i class="fa-solid fa-paintbrush fa-fw"></i> Run on Multiple GPUs</a>
|
||||
</ul>
|
||||
|
||||
<li><span class="help-section">Installation</span>
|
||||
@ -327,15 +328,15 @@
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="media/js/parameters.js?v=9"></script>
|
||||
<script src="media/js/plugins.js?v=1"></script>
|
||||
<script src="media/js/utils.js?v=6"></script>
|
||||
<script src="media/js/inpainting-editor.js?v=1"></script>
|
||||
<script src="media/js/image-modifiers.js?v=6"></script>
|
||||
<script src="media/js/auto-save.js?v=8"></script>
|
||||
<script src="media/js/main.js?v=22.1"></script>
|
||||
<script src="media/js/themes.js?v=4"></script>
|
||||
<script src="media/js/dnd.js?v=9"></script>
|
||||
<script src="media/js/utils.js"></script>
|
||||
<script src="media/js/parameters.js"></script>
|
||||
<script src="media/js/plugins.js"></script>
|
||||
<script src="media/js/inpainting-editor.js"></script>
|
||||
<script src="media/js/image-modifiers.js"></script>
|
||||
<script src="media/js/auto-save.js"></script>
|
||||
<script src="media/js/main.js"></script>
|
||||
<script src="media/js/themes.js"></script>
|
||||
<script src="media/js/dnd.js"></script>
|
||||
<script>
|
||||
async function init() {
|
||||
await initSettings()
|
||||
|
@ -26,23 +26,56 @@
|
||||
float: left;
|
||||
}
|
||||
|
||||
.form-table small {
|
||||
|
||||
.parameters-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.parameters-table > div {
|
||||
background: var(--background-color2);
|
||||
display: flex;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.parameters-table small {
|
||||
color: rgb(153, 153, 153);
|
||||
}
|
||||
|
||||
#system-settings .form-table td {
|
||||
height: 24px;
|
||||
.parameters-table > div > div:nth-child(1) {
|
||||
font-size: 20px;
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
#system-settings .form-table td:last-child div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#system-settings .form-table td:last-child div > :not([type="checkbox"]):first-child {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#system-settings .form-table td:last-child div small {
|
||||
padding-left: 5px;
|
||||
.parameters-table > div > div:nth-child(2) {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.parameters-table > div > div:nth-child(3) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.parameters-table > div:first-child {
|
||||
border-radius: 12px 12px 0px 0px;
|
||||
}
|
||||
|
||||
.parameters-table > div:last-child {
|
||||
border-radius: 0px 0px 12px 12px;
|
||||
}
|
||||
|
||||
.parameters-table .fa-fire {
|
||||
color: #F7630C;
|
||||
}
|
@ -22,13 +22,27 @@ a:visited {
|
||||
label {
|
||||
font-size: 10pt;
|
||||
}
|
||||
code {
|
||||
background: var(--background-color4);
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
#prompt {
|
||||
width: 100%;
|
||||
height: 65pt;
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
border: 2px solid var(--background-color2);
|
||||
}
|
||||
#negative_prompt {
|
||||
width: 100%;
|
||||
height: 50pt;
|
||||
font-size: 13px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
.image_preview_container {
|
||||
margin-top: 10pt;
|
||||
@ -211,7 +225,6 @@ label {
|
||||
display: none !important;
|
||||
}
|
||||
#editor-modifiers {
|
||||
max-width: 600px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@ -437,6 +450,17 @@ img {
|
||||
.secondaryButton:hover {
|
||||
background: rgb(177, 27, 0);
|
||||
}
|
||||
.useSettings {
|
||||
background: var(--accent-color);
|
||||
border: 1px solid var(--accent-color);
|
||||
color: rgb(255, 221, 255);
|
||||
padding: 3pt 6pt;
|
||||
margin-right: 6pt;
|
||||
float: right;
|
||||
}
|
||||
.useSettings:hover {
|
||||
background: hsl(var(--accent-hue), 100%, calc(var(--accent-lightness) + 6%));
|
||||
}
|
||||
.stopTask {
|
||||
float: right;
|
||||
}
|
||||
@ -490,6 +514,10 @@ img {
|
||||
border-radius: 6px 0px;
|
||||
}
|
||||
|
||||
#editor-settings {
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
#editor-settings-entries {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -579,6 +607,57 @@ input::file-selector-button {
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
|
||||
.input-toggle {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: calc(var(--input-height) * 2);
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.input-toggle > input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.input-toggle > label {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
height: var(--input-height);
|
||||
padding: 0;
|
||||
line-height: var(--input-height);
|
||||
border: var(--input-border-size) solid var(--input-border-color);
|
||||
border-radius: var(--input-height);
|
||||
background: var(--input-background-color);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
.input-toggle > label:before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: calc(var(--input-height) - ((var(--input-border-size) + var(--input-switch-padding)) * 2));
|
||||
margin: 0px;
|
||||
background: var(--input-text-color);
|
||||
position: absolute;
|
||||
top: calc(var(--input-border-size) + var(--input-switch-padding));
|
||||
bottom: calc(var(--input-border-size) + var(--input-switch-padding));
|
||||
right: calc(var(--input-border-size) + var(--input-switch-padding) + var(--input-height));
|
||||
border-radius: calc(var(--input-height) - ((var(--input-border-size) + var(--input-switch-padding)) * 2));
|
||||
transition: all 0.2s ease-in 0s;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.input-toggle > input:checked + label {
|
||||
background: var(--accent-color);
|
||||
}
|
||||
.input-toggle > input:checked + label:before {
|
||||
right: calc(var(--input-border-size) + var(--input-switch-padding));
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* MOBILE SUPPORT */
|
||||
@media screen and (max-width: 700px) {
|
||||
#top-nav {
|
||||
@ -620,6 +699,9 @@ input::file-selector-button {
|
||||
#editor {
|
||||
padding: 16px 8px;
|
||||
}
|
||||
#editor-settings {
|
||||
min-width: 0px;
|
||||
}
|
||||
.tab-content-inner {
|
||||
margin: 0px;
|
||||
}
|
||||
@ -643,21 +725,15 @@ input::file-selector-button {
|
||||
padding: 0px !important;
|
||||
margin: 24px !important;
|
||||
}
|
||||
.simple-tooltip.right {
|
||||
right: initial;
|
||||
left: 0px;
|
||||
top: 50%;
|
||||
transform: translate(calc(-100% + 15%), -50%);
|
||||
}
|
||||
:hover > .simple-tooltip.right {
|
||||
transform: translate(100%, -50%);
|
||||
.simple-tooltip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 700px) {
|
||||
/* #editor {
|
||||
max-width: 480px;
|
||||
} */
|
||||
}*/
|
||||
.float-container {
|
||||
padding: 20px;
|
||||
}
|
||||
@ -898,6 +974,9 @@ input::file-selector-button {
|
||||
i.active {
|
||||
background: var(--accent-color);
|
||||
}
|
||||
.primaryButton.active {
|
||||
background: hsl(var(--accent-hue), 100%, 50%);
|
||||
}
|
||||
#system-info {
|
||||
max-width: 800px;
|
||||
font-size: 10pt;
|
||||
@ -910,6 +989,16 @@ i.active {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
#save-system-settings-btn {
|
||||
button:hover {
|
||||
transition-duration: 0.1s;
|
||||
background: hsl(var(--accent-hue), 100%, calc(var(--accent-lightness) + 6%));
|
||||
}
|
||||
|
||||
button:active {
|
||||
transition-duration: 0.1s;
|
||||
background-color: hsl(var(--accent-hue), 100%, calc(var(--accent-lightness) + 24%));
|
||||
}
|
||||
|
||||
button#save-system-settings-btn {
|
||||
padding: 4pt 8pt;
|
||||
}
|
||||
|
@ -1,22 +1,26 @@
|
||||
:root {
|
||||
--background-color1: rgb(32, 33, 36); /* main parts of the page */
|
||||
--background-color2: rgb(44, 45, 48); /* main panels */
|
||||
--background-color3: rgb(47, 49, 53);
|
||||
--background-color4: rgb(18, 18, 19); /* settings dropdowns */
|
||||
--main-hue: 222;
|
||||
--main-saturation: 4%;
|
||||
--value-base: 13%;
|
||||
--value-step: 5%;
|
||||
--background-color1: hsl(var(--main-hue), var(--main-saturation), var(--value-base));
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (0.5 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1.5 * var(--value-step))));
|
||||
|
||||
--accent-hue: 266;
|
||||
--accent-hue: 267;
|
||||
--accent-lightness: 36%;
|
||||
--accent-lightness-hover: 40%;
|
||||
|
||||
--text-color: #eee;
|
||||
|
||||
--input-text-color: black;
|
||||
--input-background-color: #e9e9ed;
|
||||
--input-border-color: #8f8f9d;
|
||||
--input-text-color: #eee;
|
||||
--input-background-color: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (0.7 * var(--value-step))));
|
||||
--input-border-color: var(--background-color4);
|
||||
|
||||
--button-text-color: var(--input-text-color);
|
||||
--button-color: #e9e9ed;
|
||||
--button-border: 1px solid #8f8f9d;
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
/* other */
|
||||
--input-border-radius: 4px;
|
||||
@ -24,6 +28,8 @@
|
||||
--accent-color: hsl(var(--accent-hue), 100%, var(--accent-lightness));
|
||||
--accent-color-hover: hsl(var(--accent-hue), 100%, var(--accent-lightness-hover));
|
||||
--primary-button-border: none;
|
||||
--input-switch-padding: 1px;
|
||||
--input-height: 18px;
|
||||
}
|
||||
|
||||
.theme-light {
|
||||
@ -33,6 +39,7 @@
|
||||
--background-color4: #cccccc;
|
||||
|
||||
--text-color: black;
|
||||
--button-text-color: white;
|
||||
|
||||
--input-text-color: black;
|
||||
--input-background-color: #f8f9fa;
|
||||
@ -47,12 +54,7 @@
|
||||
|
||||
--accent-hue: 235;
|
||||
--accent-lightness: 65%;
|
||||
--primary-button-border: none;
|
||||
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
--input-text-color: #ccc;
|
||||
--input-border-size: 2px;
|
||||
--input-background-color: #202225;
|
||||
--input-border-color: var(--input-background-color);
|
||||
@ -67,17 +69,10 @@
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--input-background-color: var(--background-color3);
|
||||
|
||||
--accent-hue: 212;
|
||||
--primary-button-border: none;
|
||||
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
--input-border-size: 1px;
|
||||
--input-background-color: var(--background-color3);
|
||||
--input-text-color: #ccc;
|
||||
--input-border-color: var(--background-color4);
|
||||
}
|
||||
|
||||
|
||||
@ -90,16 +85,8 @@
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--primary-button-border: none;
|
||||
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
--input-border-size: 1px;
|
||||
--input-background-color: var(--background-color3);
|
||||
--input-text-color: #ccc;
|
||||
--input-border-color: var(--background-color4);
|
||||
}
|
||||
|
||||
.theme-super-dark {
|
||||
@ -111,16 +98,9 @@
|
||||
--background-color2: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1 * var(--value-step))));
|
||||
--background-color3: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (2 * var(--value-step))));
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) + (1.4 * var(--value-step))));
|
||||
|
||||
--primary-button-border: none;
|
||||
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
--input-border-size: 0px;
|
||||
--input-background-color: var(--background-color3);
|
||||
--input-text-color: #ccc;
|
||||
--input-border-color: var(--background-color4);
|
||||
--input-border-size: 0px;
|
||||
}
|
||||
|
||||
.theme-wild {
|
||||
@ -134,13 +114,33 @@
|
||||
--background-color4: hsl(var(--main-hue), var(--main-saturation), calc(var(--value-base) - (3 * var(--value-step))));
|
||||
|
||||
--accent-hue: 212;
|
||||
--primary-button-border: none;
|
||||
|
||||
--button-color: var(--accent-color);
|
||||
--button-border: none;
|
||||
|
||||
--input-border-size: 1px;
|
||||
--input-background-color: hsl(222, var(--main-saturation), calc(var(--value-base) - (2 * var(--value-step))));
|
||||
--input-text-color: red;
|
||||
--input-border-color: green;
|
||||
}
|
||||
|
||||
.theme-gnomie {
|
||||
--background-color1: #242424;
|
||||
--background-color2: #353535;
|
||||
--background-color3: #494949;
|
||||
--background-color4: #000000;
|
||||
|
||||
--accent-hue: 213;
|
||||
--accent-lightness: 55%;
|
||||
--accent-color: #2168bf;
|
||||
|
||||
--input-border-radius: 6px;
|
||||
--input-text-color: #ffffff;
|
||||
--input-background-color: #2a2a2a;
|
||||
--input-border-size: 0px;
|
||||
--input-border-color: var(--input-background-color);
|
||||
}
|
||||
|
||||
.theme-gnomie .panel-box {
|
||||
border: none;
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
@ -213,6 +213,7 @@ function fillSaveSettingsConfigTable() {
|
||||
})
|
||||
})
|
||||
})
|
||||
prettifyInputs(saveSettingsConfigTable)
|
||||
}
|
||||
|
||||
// configureSettingsSaveBtn
|
||||
@ -224,7 +225,7 @@ var autoSaveSettings = document.getElementById("auto_save_settings")
|
||||
var configSettingsButton = document.createElement("button")
|
||||
configSettingsButton.textContent = "Configure"
|
||||
configSettingsButton.style.margin = "0px 5px"
|
||||
autoSaveSettings.insertAdjacentElement("afterend", configSettingsButton)
|
||||
autoSaveSettings.insertAdjacentElement("beforebegin", configSettingsButton)
|
||||
autoSaveSettings.addEventListener("change", () => {
|
||||
configSettingsButton.style.display = autoSaveSettings.checked ? "block" : "none"
|
||||
})
|
||||
|
@ -161,18 +161,7 @@ const TASK_MAPPING = {
|
||||
setUI: (use_stable_diffusion_model) => {
|
||||
const oldVal = stableDiffusionModelField.value
|
||||
|
||||
let pathIdx = use_stable_diffusion_model.lastIndexOf('/') // Linux, Mac paths
|
||||
if (pathIdx < 0) {
|
||||
pathIdx = use_stable_diffusion_model.lastIndexOf('\\') // Windows paths.
|
||||
}
|
||||
if (pathIdx >= 0) {
|
||||
use_stable_diffusion_model = use_stable_diffusion_model.slice(pathIdx + 1)
|
||||
}
|
||||
const modelExt = '.ckpt'
|
||||
if (use_stable_diffusion_model.endsWith(modelExt)) {
|
||||
use_stable_diffusion_model = use_stable_diffusion_model.slice(0, use_stable_diffusion_model.length - modelExt.length)
|
||||
}
|
||||
|
||||
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, ['.ckpt'])
|
||||
stableDiffusionModelField.value = use_stable_diffusion_model
|
||||
|
||||
if (!stableDiffusionModelField.value) {
|
||||
@ -182,6 +171,19 @@ const TASK_MAPPING = {
|
||||
readUI: () => stableDiffusionModelField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
use_vae_model: { name: 'VAE model',
|
||||
setUI: (use_vae_model) => {
|
||||
const oldVal = vaeModelField.value
|
||||
|
||||
if (use_vae_model !== '') {
|
||||
use_vae_model = getModelPath(use_vae_model, ['.vae.pt', '.ckpt'])
|
||||
use_vae_model = use_vae_model !== '' ? use_vae_model : oldVal
|
||||
}
|
||||
vaeModelField.value = use_vae_model
|
||||
},
|
||||
readUI: () => vaeModelField.value,
|
||||
parse: (val) => val
|
||||
},
|
||||
|
||||
numOutputsParallel: { name: 'Parallel Images',
|
||||
setUI: (numOutputsParallel) => {
|
||||
@ -243,7 +245,9 @@ const TASK_MAPPING = {
|
||||
parse: (val) => val
|
||||
}
|
||||
}
|
||||
function restoreTaskToUI(task) {
|
||||
function restoreTaskToUI(task, fieldsToSkip) {
|
||||
fieldsToSkip = fieldsToSkip || []
|
||||
|
||||
if ('numOutputsTotal' in task) {
|
||||
numOutputsTotalField.value = task.numOutputsTotal
|
||||
}
|
||||
@ -255,10 +259,47 @@ function restoreTaskToUI(task) {
|
||||
return
|
||||
}
|
||||
for (const key in TASK_MAPPING) {
|
||||
if (key in task.reqBody) {
|
||||
if (key in task.reqBody && !fieldsToSkip.includes(key)) {
|
||||
TASK_MAPPING[key].setUI(task.reqBody[key])
|
||||
}
|
||||
}
|
||||
|
||||
// restore the original tag
|
||||
promptField.value = task.reqBody.original_prompt || task.reqBody.prompt
|
||||
|
||||
// Restore modifiers
|
||||
if (task.reqBody.active_tags) {
|
||||
refreshModifiersState(task.reqBody.active_tags)
|
||||
}
|
||||
|
||||
// properly reset checkboxes
|
||||
if (!('use_face_correction' in task.reqBody)) {
|
||||
useFaceCorrectionField.checked = false
|
||||
}
|
||||
if (!('use_upscale' in task.reqBody)) {
|
||||
useUpscalingField.checked = false
|
||||
}
|
||||
if (!('mask' in task.reqBody)) {
|
||||
maskSetting.checked = false
|
||||
}
|
||||
upscaleModelField.disabled = !useUpscalingField.checked
|
||||
|
||||
// Show the source picture if present
|
||||
initImagePreview.src = (task.reqBody.init_image == undefined ? '' : task.reqBody.init_image)
|
||||
if (IMAGE_REGEX.test(initImagePreview.src)) {
|
||||
Boolean(task.reqBody.mask) ? inpaintingEditor.setImg(task.reqBody.mask) : inpaintingEditor.resetBackground()
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
inpaintingEditorContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'table-row'
|
||||
//samplerSelectionContainer.style.display = 'none'
|
||||
// maskSetting.checked = false
|
||||
inpaintingEditorContainer.style.display = maskSetting.checked ? 'block' : 'none'
|
||||
} else {
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
// inpaintingEditorContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
// maskSetting.style.display = 'none'
|
||||
}
|
||||
}
|
||||
function readUI() {
|
||||
const reqBody = {}
|
||||
@ -271,6 +312,21 @@ function readUI() {
|
||||
'reqBody': reqBody
|
||||
}
|
||||
}
|
||||
function getModelPath(filename, extensions)
|
||||
{
|
||||
let pathIdx = filename.lastIndexOf('/') // Linux, Mac paths
|
||||
if (pathIdx < 0) {
|
||||
pathIdx = filename.lastIndexOf('\\') // Windows paths.
|
||||
}
|
||||
if (pathIdx >= 0) {
|
||||
filename = filename.slice(pathIdx + 1)
|
||||
}
|
||||
extensions.forEach(ext => {
|
||||
if (filename.endsWith(ext)) {
|
||||
filename = filename.slice(0, filename.length - ext.length)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const TASK_TEXT_MAPPING = {
|
||||
width: 'Width',
|
||||
@ -326,29 +382,35 @@ function parseTaskFromText(str) {
|
||||
return task
|
||||
}
|
||||
|
||||
async function readFile(file, i) {
|
||||
const fileContent = (await file.text()).trim()
|
||||
|
||||
// JSON File.
|
||||
if (fileContent.startsWith('{') && fileContent.endsWith('}')) {
|
||||
async function parseContent(text) {
|
||||
text = text.trim();
|
||||
if (text.startsWith('{') && text.endsWith('}')) {
|
||||
try {
|
||||
const task = JSON.parse(fileContent)
|
||||
const task = JSON.parse(text)
|
||||
restoreTaskToUI(task)
|
||||
return true
|
||||
} catch (e) {
|
||||
console.warn(`file[${i}]:${file.name} - File couldn't be parsed.`, e)
|
||||
console.warn(`JSON text content couldn't be parsed.`, e)
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// Normal txt file.
|
||||
const task = parseTaskFromText(fileContent)
|
||||
const task = parseTaskFromText(text)
|
||||
if (task) {
|
||||
restoreTaskToUI(task)
|
||||
return true
|
||||
} else {
|
||||
console.warn(`file[${i}]:${file.name} - File couldn't be parsed.`)
|
||||
console.warn(`Raw text content couldn't be parsed.`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function readFile(file, i) {
|
||||
console.log(`Event %o reading file[${i}]:${file.name}...`, e)
|
||||
const fileContent = (await file.text()).trim()
|
||||
return await parseContent(fileContent)
|
||||
}
|
||||
|
||||
function dropHandler(ev) {
|
||||
console.log('Content dropped...')
|
||||
let items = []
|
||||
@ -395,72 +457,73 @@ const TASK_REQ_NO_EXPORT = [
|
||||
"use_full_precision",
|
||||
"save_to_disk_path"
|
||||
]
|
||||
const resetSettings = document.getElementById('reset-image-settings')
|
||||
|
||||
// Retrieve clipboard content and try to parse it
|
||||
async function pasteFromClipboard() {
|
||||
//const text = await navigator.clipboard.readText()
|
||||
let text = await navigator.clipboard.readText();
|
||||
text=text.trim();
|
||||
if (text.startsWith('{') && text.endsWith('}')) {
|
||||
try {
|
||||
const task = JSON.parse(text)
|
||||
restoreTaskToUI(task)
|
||||
} catch (e) {
|
||||
console.warn(`Clipboard JSON couldn't be parsed.`, e)
|
||||
}
|
||||
function checkReadTextClipboardPermission (result) {
|
||||
if (result.state != "granted" && result.state != "prompt") {
|
||||
return
|
||||
}
|
||||
// Normal txt file.
|
||||
const task = parseTaskFromText(text)
|
||||
if (task) {
|
||||
restoreTaskToUI(task)
|
||||
} else {
|
||||
console.warn(`Clipboard content - File couldn't be parsed.`)
|
||||
}
|
||||
// PASTE ICON
|
||||
const pasteIcon = document.createElement('i')
|
||||
pasteIcon.className = 'fa-solid fa-paste section-button'
|
||||
pasteIcon.innerHTML = `<span class="simple-tooltip right">Paste Image Settings</span>`
|
||||
pasteIcon.addEventListener('click', async (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
pasteIcon.classList.add('active')
|
||||
// In 350 ms remove the 'active' class
|
||||
asyncDelay(350).then(() => pasteIcon.classList.remove('active'))
|
||||
|
||||
// Retrieve clipboard content and try to parse it
|
||||
const text = await navigator.clipboard.readText();
|
||||
await parseContent(text)
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
||||
}
|
||||
navigator.permissions.query({ name: "clipboard-read" }).then(checkReadTextClipboardPermission, (reason) => console.log('clipboard-read is not available. %o', reason))
|
||||
|
||||
document.addEventListener('paste', async (event) => {
|
||||
if (event.target) {
|
||||
const targetTag = event.target.tagName.toLowerCase()
|
||||
// Disable when targeting input elements.
|
||||
if (targetTag === 'input' || targetTag === 'textarea') {
|
||||
return
|
||||
}
|
||||
}
|
||||
const paste = (event.clipboardData || window.clipboardData).getData('text')
|
||||
const selection = window.getSelection()
|
||||
if (selection.toString().trim().length <= 0 && await parseContent(paste)) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
// Adds a copy and a paste icon if the browser grants permission to write to clipboard.
|
||||
function checkWriteToClipboardPermission (result) {
|
||||
if (result.state == "granted" || result.state == "prompt") {
|
||||
const resetSettings = document.getElementById('reset-image-settings')
|
||||
|
||||
// COPY ICON
|
||||
const copyIcon = document.createElement('i')
|
||||
copyIcon.className = 'fa-solid fa-clipboard section-button'
|
||||
copyIcon.innerHTML = `<span class="simple-tooltip right">Copy Image Settings</span>`
|
||||
copyIcon.addEventListener('click', (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
copyIcon.classList.add('active')
|
||||
// In 1000 ms remove the 'active' class
|
||||
asyncDelay(1000).then(() => copyIcon.classList.remove('active'))
|
||||
const uiState = readUI()
|
||||
TASK_REQ_NO_EXPORT.forEach((key) => delete uiState.reqBody[key])
|
||||
if (uiState.reqBody.init_image && !IMAGE_REGEX.test(uiState.reqBody.init_image)) {
|
||||
delete uiState.reqBody.init_image
|
||||
delete uiState.reqBody.prompt_strength
|
||||
}
|
||||
navigator.clipboard.writeText(JSON.stringify(uiState, undefined, 4))
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(copyIcon, resetSettings)
|
||||
|
||||
// PASTE ICON
|
||||
const pasteIcon = document.createElement('i')
|
||||
pasteIcon.className = 'fa-solid fa-paste section-button'
|
||||
pasteIcon.innerHTML = `<span class="simple-tooltip right">Paste Image Settings</span>`
|
||||
pasteIcon.addEventListener('click', (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
pasteIcon.classList.add('active')
|
||||
// In 1000 ms remove the 'active' class
|
||||
asyncDelay(1000).then(() => pasteIcon.classList.remove('active'))
|
||||
pasteFromClipboard()
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
||||
if (result.state != "granted" && result.state != "prompt") {
|
||||
return
|
||||
}
|
||||
// COPY ICON
|
||||
const copyIcon = document.createElement('i')
|
||||
copyIcon.className = 'fa-solid fa-clipboard section-button'
|
||||
copyIcon.innerHTML = `<span class="simple-tooltip right">Copy Image Settings</span>`
|
||||
copyIcon.addEventListener('click', (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
copyIcon.classList.add('active')
|
||||
// In 350 ms remove the 'active' class
|
||||
asyncDelay(350).then(() => copyIcon.classList.remove('active'))
|
||||
const uiState = readUI()
|
||||
TASK_REQ_NO_EXPORT.forEach((key) => delete uiState.reqBody[key])
|
||||
if (uiState.reqBody.init_image && !IMAGE_REGEX.test(uiState.reqBody.init_image)) {
|
||||
delete uiState.reqBody.init_image
|
||||
delete uiState.reqBody.prompt_strength
|
||||
}
|
||||
navigator.clipboard.writeText(JSON.stringify(uiState, undefined, 4))
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(copyIcon, resetSettings)
|
||||
}
|
||||
|
||||
// Determine which access we have to the clipboard. Clipboard access is only available on localhost or via TLS.
|
||||
// Determine which access we have to the clipboard. Clipboard access is only available on localhost or via TLS.
|
||||
navigator.permissions.query({ name: "clipboard-write" }).then(checkWriteToClipboardPermission, (e) => {
|
||||
if (e instanceof TypeError && typeof navigator?.clipboard?.writeText === 'function') {
|
||||
// Fix for firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1560373
|
||||
|
@ -148,6 +148,60 @@ async function loadModifiers() {
|
||||
loadCustomModifiers()
|
||||
}
|
||||
|
||||
function refreshModifiersState(newTags) {
|
||||
// clear existing modifiers
|
||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(modifierCard => {
|
||||
const modifierName = modifierCard.querySelector('.modifier-card-label').innerText
|
||||
if (activeTags.map(x => x.name).includes(modifierName)) {
|
||||
modifierCard.classList.remove(activeCardClass)
|
||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
|
||||
}
|
||||
})
|
||||
activeTags = []
|
||||
|
||||
// set new modifiers
|
||||
newTags.forEach(tag => {
|
||||
let found = false
|
||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(modifierCard => {
|
||||
const modifierName = modifierCard.querySelector('.modifier-card-label').innerText
|
||||
if (tag == modifierName) {
|
||||
// add modifier to active array
|
||||
if (!activeTags.map(x => x.name).includes(tag)) { // only add each tag once even if several custom modifier cards share the same tag
|
||||
activeTags.push({
|
||||
'name': modifierName,
|
||||
'element': modifierCard.cloneNode(true),
|
||||
'originElement': modifierCard
|
||||
})
|
||||
}
|
||||
modifierCard.classList.add(activeCardClass)
|
||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '-'
|
||||
found = true
|
||||
}
|
||||
})
|
||||
if (found == false) { // custom tag went missing, create one here
|
||||
let modifierCard = createModifierCard(tag, undefined) // create a modifier card for the missing tag, no image
|
||||
|
||||
modifierCard.addEventListener('click', () => {
|
||||
if (activeTags.map(x => x.name).includes(tag)) {
|
||||
// remove modifier from active array
|
||||
activeTags = activeTags.filter(x => x.name != tag)
|
||||
modifierCard.classList.remove(activeCardClass)
|
||||
|
||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
|
||||
}
|
||||
refreshTagsList()
|
||||
})
|
||||
|
||||
activeTags.push({
|
||||
'name': tag,
|
||||
'element': modifierCard,
|
||||
'originElement': undefined // no origin element for missing tags
|
||||
})
|
||||
}
|
||||
})
|
||||
refreshTagsList()
|
||||
}
|
||||
|
||||
function refreshTagsList() {
|
||||
editorModifierTagsList.innerHTML = ''
|
||||
|
||||
@ -167,7 +221,7 @@ function refreshTagsList() {
|
||||
tag.element.addEventListener('click', () => {
|
||||
let idx = activeTags.indexOf(tag)
|
||||
|
||||
if (idx !== -1) {
|
||||
if (idx !== -1 && activeTags[idx].originElement !== undefined) {
|
||||
activeTags[idx].originElement.classList.remove(activeCardClass)
|
||||
activeTags[idx].originElement.querySelector('.modifier-card-image-overlay').innerText = '+'
|
||||
|
||||
|
@ -789,7 +789,9 @@ function getCurrentUserRequest() {
|
||||
stream_progress_updates: true,
|
||||
stream_image_progress: (numOutputsTotal > 50 ? false : streamImageProgressField.checked),
|
||||
show_only_filtered_image: showOnlyFilteredImageField.checked,
|
||||
output_format: outputFormatField.value
|
||||
output_format: outputFormatField.value,
|
||||
original_prompt: promptField.value,
|
||||
active_tags: (activeTags.map(x => x.name))
|
||||
}
|
||||
}
|
||||
if (IMAGE_REGEX.test(initImagePreview.src)) {
|
||||
@ -821,16 +823,26 @@ function getCurrentUserRequest() {
|
||||
function makeImage() {
|
||||
if (!isServerAvailable()) {
|
||||
alert('The server is not available.')
|
||||
return
|
||||
}
|
||||
const taskTemplate = getCurrentUserRequest()
|
||||
const newTaskRequests = []
|
||||
getPrompts().forEach((prompt) => newTaskRequests.push(Object.assign({}, taskTemplate, {
|
||||
reqBody: Object.assign({ prompt: prompt }, taskTemplate.reqBody)
|
||||
})))
|
||||
newTaskRequests.forEach(createTask)
|
||||
} else if (!randomSeedField.checked && seedField.value == '') {
|
||||
alert('The "Seed" field must not be empty.')
|
||||
} else if (numOutputsTotalField.value == '') {
|
||||
alert('The "Number of Images" field must not be empty.')
|
||||
} else if (numOutputsParallelField.value == '') {
|
||||
alert('The "Number of parallel Images" field must not be empty.')
|
||||
} else if (numInferenceStepsField.value == '') {
|
||||
alert('The "Inference Steps" field must not be empty.')
|
||||
} else if (guidanceScaleField.value == '') {
|
||||
alert('The Guidance Scale field must not be empty.')
|
||||
} else {
|
||||
const taskTemplate = getCurrentUserRequest()
|
||||
const newTaskRequests = []
|
||||
getPrompts().forEach((prompt) => newTaskRequests.push(Object.assign({}, taskTemplate, {
|
||||
reqBody: Object.assign({ prompt: prompt }, taskTemplate.reqBody)
|
||||
})))
|
||||
newTaskRequests.forEach(createTask)
|
||||
|
||||
initialText.style.display = 'none'
|
||||
initialText.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
function createTask(task) {
|
||||
@ -856,6 +868,7 @@ function createTask(task) {
|
||||
taskEntry.innerHTML = ` <div class="header-content panel collapsible active">
|
||||
<div class="taskStatusLabel">Enqueued</div>
|
||||
<button class="secondaryButton stopTask"><i class="fa-solid fa-trash-can"></i> Remove</button>
|
||||
<button class="secondaryButton useSettings"><i class="fa-solid fa-redo"></i> Use these settings</button>
|
||||
<div class="preview-prompt collapsible active"></div>
|
||||
<div class="taskConfig">${taskConfig}</div>
|
||||
<div class="outputMsg"></div>
|
||||
@ -894,6 +907,12 @@ function createTask(task) {
|
||||
}
|
||||
})
|
||||
|
||||
task['useSettings'] = taskEntry.querySelector('.useSettings')
|
||||
task['useSettings'].addEventListener('click', function(e) {
|
||||
e.stopPropagation()
|
||||
restoreTaskToUI(task, TASK_REQ_NO_EXPORT)
|
||||
})
|
||||
|
||||
imagePreview.insertBefore(taskEntry, previewTools.nextSibling)
|
||||
|
||||
task.previewPrompt.innerText = task.reqBody.prompt
|
||||
@ -1079,10 +1098,6 @@ useUpscalingField.addEventListener('change', function(e) {
|
||||
upscaleModelField.disabled = !this.checked
|
||||
})
|
||||
|
||||
if (useBetaChannelField.checked) {
|
||||
updateBranchLabel.innerText = "(beta)"
|
||||
}
|
||||
|
||||
makeImageBtn.addEventListener('click', makeImage)
|
||||
|
||||
document.onkeydown = function(e) {
|
||||
@ -1141,8 +1156,16 @@ async function getModels() {
|
||||
let res = await fetch('/get/models')
|
||||
const models = await res.json()
|
||||
|
||||
console.log('get models response', models)
|
||||
console.log('got models response', models)
|
||||
|
||||
if ( "scan-error" in models ) {
|
||||
// let previewPane = document.getElementById('tab-content-wrapper')
|
||||
let previewPane = document.getElementById('preview')
|
||||
previewPane.style.background="red"
|
||||
previewPane.style.textAlign="center"
|
||||
previewPane.innerHTML = '<H1>🔥Malware alert!🔥</H1><h2>The file <i>' + models['scan-error'] + '</i> in your <tt>models/stable-diffusion</tt> folder is probably malware infected.</h2><h2>Please delete this file from the folder before proceeding!</h2>After deleting the file, reload this page.<br><br><button onClick="window.location.reload();">Reload Page</button>'
|
||||
makeImageBtn.disabled = true
|
||||
}
|
||||
let modelOptions = models['options']
|
||||
let stableDiffusionOptions = modelOptions['stable-diffusion']
|
||||
let vaeOptions = modelOptions['vae']
|
||||
@ -1284,7 +1307,7 @@ document.querySelectorAll('.popup').forEach(popup => {
|
||||
})
|
||||
|
||||
var tabElements = [];
|
||||
document.querySelectorAll(".tab").forEach(tab => {
|
||||
function linkTabContents(tab) {
|
||||
var name = tab.id.replace("tab-", "");
|
||||
var content = document.getElementById(`tab-content-${name}`)
|
||||
tabElements.push({
|
||||
@ -1305,7 +1328,9 @@ document.querySelectorAll(".tab").forEach(tab => {
|
||||
content.classList.toggle("active")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
document.querySelectorAll(".tab").forEach(linkTabContents)
|
||||
|
||||
window.addEventListener("beforeunload", function(e) {
|
||||
const msg = "Unsaved pictures will be lost!";
|
||||
@ -1321,3 +1346,4 @@ window.addEventListener("beforeunload", function(e) {
|
||||
});
|
||||
|
||||
createCollapsibles()
|
||||
prettifyInputs(document);
|
||||
|
6
ui/media/js/marked.min.js
vendored
Normal file
6
ui/media/js/marked.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -28,18 +28,21 @@ var PARAMETERS = [
|
||||
type: ParameterType.select,
|
||||
label: "Theme",
|
||||
default: "theme-default",
|
||||
note: "customize the look and feel of the ui",
|
||||
options: [ // Note: options expanded dynamically
|
||||
{
|
||||
value: "theme-default",
|
||||
label: "Default"
|
||||
}
|
||||
]
|
||||
],
|
||||
icon: "fa-palette"
|
||||
},
|
||||
{
|
||||
id: "save_to_disk",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Auto-Save Images",
|
||||
note: "automatically saves images to the specified location",
|
||||
icon: "fa-download",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
@ -55,6 +58,7 @@ var PARAMETERS = [
|
||||
type: ParameterType.checkbox,
|
||||
label: "Enable Sound",
|
||||
note: "plays a sound on task completion",
|
||||
icon: "fa-volume-low",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
@ -62,20 +66,23 @@ var PARAMETERS = [
|
||||
type: ParameterType.checkbox,
|
||||
label: "Open browser on startup",
|
||||
note: "starts the default browser on startup",
|
||||
icon: "fa-window-restore",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "turbo",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Turbo Mode",
|
||||
default: true,
|
||||
note: "generates images faster, but uses an additional 1 GB of GPU memory",
|
||||
icon: "fa-forward",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "use_cpu",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Use CPU (not GPU)",
|
||||
note: "warning: this will be *very* slow",
|
||||
icon: "fa-microchip",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
@ -96,6 +103,7 @@ var PARAMETERS = [
|
||||
type: ParameterType.checkbox,
|
||||
label: "Use Full Precision",
|
||||
note: "for GPU-only. warning: this will consume more VRAM",
|
||||
icon: "fa-crosshairs",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
@ -103,13 +111,33 @@ var PARAMETERS = [
|
||||
type: ParameterType.checkbox,
|
||||
label: "Auto-Save Settings",
|
||||
note: "restores settings on browser load",
|
||||
icon: "fa-gear",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "listen_to_network",
|
||||
type: ParameterType.checkbox,
|
||||
label: "Make Stable Diffusion available on your network",
|
||||
note: "Other devices on your network can access this web page",
|
||||
icon: "fa-network-wired",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: "listen_port",
|
||||
type: ParameterType.custom,
|
||||
label: "Network port",
|
||||
note: "Port that this server listens to. The '9000' part in 'http://localhost:9000'",
|
||||
icon: "fa-anchor",
|
||||
render: (parameter) => {
|
||||
return `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">`
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "use_beta_channel",
|
||||
type: ParameterType.checkbox,
|
||||
label: "🔥Beta channel",
|
||||
label: "Beta channel",
|
||||
note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.",
|
||||
icon: "fa-fire",
|
||||
default: false,
|
||||
},
|
||||
];
|
||||
@ -140,16 +168,18 @@ function getParameterElement(parameter) {
|
||||
}
|
||||
}
|
||||
|
||||
let parametersTable = document.querySelector("#system-settings table")
|
||||
let parametersTable = document.querySelector("#system-settings .parameters-table")
|
||||
/* fill in the system settings popup table */
|
||||
function initParameters() {
|
||||
PARAMETERS.forEach(parameter => {
|
||||
var element = getParameterElement(parameter)
|
||||
var note = parameter.note ? `<small>${parameter.note}</small>` : "";
|
||||
var newrow = document.createElement('tr')
|
||||
var icon = parameter.icon ? `<i class="fa ${parameter.icon}"></i>` : "";
|
||||
var newrow = document.createElement('div')
|
||||
newrow.innerHTML = `
|
||||
<td><label for="${parameter.id}">${parameter.label}</label></td>
|
||||
<td><div>${element}${note}<div></td>`
|
||||
<div>${icon}</div>
|
||||
<div><label for="${parameter.id}">${parameter.label}</label>${note}</div>
|
||||
<div>${element}</div>`
|
||||
parametersTable.appendChild(newrow)
|
||||
parameter.settingsEntry = newrow
|
||||
})
|
||||
@ -164,6 +194,8 @@ let useGPUsField = document.querySelector('#use_gpus')
|
||||
let useFullPrecisionField = document.querySelector('#use_full_precision')
|
||||
let saveToDiskField = document.querySelector('#save_to_disk')
|
||||
let diskPathField = document.querySelector('#diskPath')
|
||||
let listenToNetworkField = document.querySelector("#listen_to_network")
|
||||
let listenPortField = document.querySelector("#listen_port")
|
||||
let useBetaChannelField = document.querySelector("#use_beta_channel")
|
||||
let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_start")
|
||||
|
||||
@ -193,10 +225,17 @@ async function getAppConfig() {
|
||||
|
||||
if (config.update_branch === 'beta') {
|
||||
useBetaChannelField.checked = true
|
||||
document.querySelector("#updateBranchLabel").innerText = "(beta)"
|
||||
}
|
||||
if (config.ui && config.ui.open_browser_on_start === false) {
|
||||
uiOpenBrowserOnStartField.checked = false
|
||||
}
|
||||
if (config.net && config.net.listen_to_network === false) {
|
||||
listenToNetworkField.checked = false
|
||||
}
|
||||
if (config.net && config.net.listen_port !== undefined) {
|
||||
listenPortField.value = config.net.listen_port
|
||||
}
|
||||
|
||||
console.log('get config status response', config)
|
||||
} catch (e) {
|
||||
@ -224,6 +263,7 @@ function getCurrentRenderDeviceSelection() {
|
||||
useCPUField.addEventListener('click', function() {
|
||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
||||
let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus')
|
||||
console.log("hello", this.checked);
|
||||
if (this.checked) {
|
||||
gpuSettingEntry.style.display = 'none'
|
||||
autoPickGPUSettingEntry.style.display = 'none'
|
||||
@ -322,9 +362,20 @@ async function getDevices() {
|
||||
saveSettingsBtn.addEventListener('click', function() {
|
||||
let updateBranch = (useBetaChannelField.checked ? 'beta' : 'main')
|
||||
|
||||
changeAppConfig({
|
||||
'render_devices': getCurrentRenderDeviceSelection(),
|
||||
'update_branch': updateBranch,
|
||||
'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked
|
||||
})
|
||||
if (listenPortField.value == '') {
|
||||
alert('The network port field must not be empty.')
|
||||
} else if (listenPortField.value<1 || listenPortField.value>65535) {
|
||||
alert('The network port must be a number from 1 to 65535')
|
||||
} else {
|
||||
changeAppConfig({
|
||||
'render_devices': getCurrentRenderDeviceSelection(),
|
||||
'update_branch': updateBranch,
|
||||
'ui_open_browser_on_start': uiOpenBrowserOnStartField.checked,
|
||||
'listen_to_network': listenToNetworkField.checked,
|
||||
'listen_port': listenPortField.value
|
||||
})
|
||||
}
|
||||
|
||||
saveSettingsBtn.classList.add('active')
|
||||
asyncDelay(300).then(() => saveSettingsBtn.classList.remove('active'))
|
||||
})
|
||||
|
@ -14,7 +14,7 @@ function initTheme() {
|
||||
.flatMap(sheet => Array.from(sheet.cssRules))
|
||||
.forEach(rule => {
|
||||
var selector = rule.selectorText; // TODO: also do selector == ":root", re-run un-set props
|
||||
if (selector && selector.startsWith(".theme-")) {
|
||||
if (selector && selector.startsWith(".theme-") && !selector.includes(" ")) {
|
||||
var theme_key = selector.substring(1);
|
||||
THEMES.push({
|
||||
key: theme_key,
|
||||
|
@ -358,3 +358,19 @@ function preventNonNumericalInput(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/* inserts custom html to allow prettifying of inputs */
|
||||
function prettifyInputs(root_element) {
|
||||
root_element.querySelectorAll(`input[type="checkbox"]`).forEach(element => {
|
||||
var parent = element.parentNode;
|
||||
if (!parent.classList.contains("input-toggle")) {
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.classList.add("input-toggle");
|
||||
parent.replaceChild(wrapper, element);
|
||||
wrapper.appendChild(element);
|
||||
var label = document.createElement("label");
|
||||
label.htmlFor = element.id;
|
||||
wrapper.appendChild(label);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
91
ui/plugins/ui/Modifiers-dnd.plugin.js
Normal file
91
ui/plugins/ui/Modifiers-dnd.plugin.js
Normal file
@ -0,0 +1,91 @@
|
||||
(function () {
|
||||
"use strict"
|
||||
|
||||
var styleSheet = document.createElement("style");
|
||||
styleSheet.textContent = `
|
||||
.modifier-card-tiny.drag-sort-active {
|
||||
background: transparent;
|
||||
border: 2px dashed white;
|
||||
opacity:0.2;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(styleSheet);
|
||||
|
||||
// observe for changes in tag list
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
// mutations.forEach(function (mutation) {
|
||||
if (editorModifierTagsList.childNodes.length > 0) {
|
||||
ModifierDragAndDrop(editorModifierTagsList)
|
||||
}
|
||||
// })
|
||||
})
|
||||
|
||||
observer.observe(editorModifierTagsList, {
|
||||
childList: true
|
||||
})
|
||||
|
||||
let current
|
||||
function ModifierDragAndDrop(target) {
|
||||
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
||||
overlays.forEach (i => {
|
||||
i.parentElement.draggable = true;
|
||||
|
||||
i.parentElement.ondragstart = (e) => {
|
||||
current = i
|
||||
i.parentElement.getElementsByClassName('modifier-card-image-overlay')[0].innerText = ''
|
||||
i.parentElement.draggable = true
|
||||
i.parentElement.classList.add('drag-sort-active')
|
||||
for(let item of document.querySelector('#editor-inputs-tags-list').getElementsByClassName('modifier-card-image-overlay')) {
|
||||
if (item.parentElement.parentElement.getElementsByClassName('modifier-card-overlay')[0] != current) {
|
||||
item.parentElement.parentElement.getElementsByClassName('modifier-card-image-overlay')[0].style.opacity = 0
|
||||
if(item.parentElement.getElementsByClassName('modifier-card-image').length > 0) {
|
||||
item.parentElement.getElementsByClassName('modifier-card-image')[0].style.filter = 'none'
|
||||
}
|
||||
item.parentElement.parentElement.style.transform = 'none'
|
||||
item.parentElement.parentElement.style.boxShadow = 'none'
|
||||
}
|
||||
item.innerText = ''
|
||||
}
|
||||
}
|
||||
|
||||
i.ondragenter = (e) => {
|
||||
e.preventDefault()
|
||||
if (i != current) {
|
||||
let currentPos = 0, droppedPos = 0;
|
||||
for (let it = 0; it < overlays.length; it++) {
|
||||
if (current == overlays[it]) { currentPos = it; }
|
||||
if (i == overlays[it]) { droppedPos = it; }
|
||||
}
|
||||
|
||||
if (i.parentElement != current.parentElement) {
|
||||
let currentPos = 0, droppedPos = 0
|
||||
for (let it = 0; it < overlays.length; it++) {
|
||||
if (current == overlays[it]) { currentPos = it }
|
||||
if (i == overlays[it]) { droppedPos = it }
|
||||
}
|
||||
if (currentPos < droppedPos) {
|
||||
current = i.parentElement.parentNode.insertBefore(current.parentElement, i.parentElement.nextSibling).getElementsByClassName('modifier-card-overlay')[0]
|
||||
} else {
|
||||
current = i.parentElement.parentNode.insertBefore(current.parentElement, i.parentElement).getElementsByClassName('modifier-card-overlay')[0]
|
||||
}
|
||||
// update activeTags
|
||||
const tag = activeTags.splice(currentPos, 1)
|
||||
activeTags.splice(droppedPos, 0, tag[0])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
i.ondragover = (e) => {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
i.parentElement.ondragend = (e) => {
|
||||
i.parentElement.classList.remove('drag-sort-active')
|
||||
for(let item of document.querySelector('#editor-inputs-tags-list').getElementsByClassName('modifier-card-image-overlay')) {
|
||||
item.style.opacity = ''
|
||||
item.innerText = '-'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})()
|
60
ui/plugins/ui/Modifiers-wheel.plugin.js
Normal file
60
ui/plugins/ui/Modifiers-wheel.plugin.js
Normal file
@ -0,0 +1,60 @@
|
||||
(function () {
|
||||
"use strict"
|
||||
|
||||
// observe for changes in tag list
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
// mutations.forEach(function (mutation) {
|
||||
if (editorModifierTagsList.childNodes.length > 0) {
|
||||
ModifierMouseWheel(editorModifierTagsList)
|
||||
}
|
||||
// })
|
||||
})
|
||||
|
||||
observer.observe(editorModifierTagsList, {
|
||||
childList: true
|
||||
})
|
||||
|
||||
function ModifierMouseWheel(target) {
|
||||
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
||||
overlays.forEach (i => {
|
||||
i.onwheel = (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const delta = Math.sign(event.deltaY)
|
||||
let s = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText
|
||||
if (delta < 0) {
|
||||
// wheel scrolling up
|
||||
if (s.substring(0, 1) == '[' && s.substring(s.length-1) == ']') {
|
||||
s = s.substring(1, s.length - 1)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s.substring(0, 10) !== '('.repeat(10) && s.substring(s.length-10) !== ')'.repeat(10)) {
|
||||
s = '(' + s + ')'
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
// wheel scrolling down
|
||||
if (s.substring(0, 1) == '(' && s.substring(s.length-1) == ')') {
|
||||
s = s.substring(1, s.length - 1)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s.substring(0, 10) !== '['.repeat(10) && s.substring(s.length-10) !== ']'.repeat(10)) {
|
||||
s = '[' + s + ']'
|
||||
}
|
||||
}
|
||||
}
|
||||
i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText = s
|
||||
// update activeTags
|
||||
for (let it = 0; it < overlays.length; it++) {
|
||||
if (i == overlays[it]) {
|
||||
activeTags[it].name = s
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})()
|
@ -0,0 +1,3 @@
|
||||
Custom plugins in this folder will be shipped to all the users by default.
|
||||
|
||||
This allows UI features to be built as plugins (testing our Plugins API, and keeping our core lean and modular).
|
47
ui/plugins/ui/release-notes.plugin.js
Normal file
47
ui/plugins/ui/release-notes.plugin.js
Normal file
@ -0,0 +1,47 @@
|
||||
(function() {
|
||||
document.querySelector('#tab-container').insertAdjacentHTML('beforeend', `
|
||||
<span id="tab-news" class="tab">
|
||||
<span><i class="fa fa-bolt icon"></i> What's new?</span>
|
||||
</span>
|
||||
`)
|
||||
|
||||
document.querySelector('#tab-content-wrapper').insertAdjacentHTML('beforeend', `
|
||||
<div id="tab-content-news" class="tab-content">
|
||||
<div id="news" class="tab-content-inner">
|
||||
Loading..
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
|
||||
document.querySelector('body').insertAdjacentHTML('beforeend', `
|
||||
<style>
|
||||
#tab-content-news .tab-content-inner {
|
||||
max-width: 100%;
|
||||
text-align: left;
|
||||
padding: 10pt;
|
||||
}
|
||||
</style>
|
||||
`)
|
||||
|
||||
linkTabContents(document.querySelector('#tab-news'))
|
||||
|
||||
let markedScript = document.createElement('script')
|
||||
markedScript.src = '/media/js/marked.min.js'
|
||||
|
||||
markedScript.onload = async function() {
|
||||
let appConfig = await fetch('/get/app_config')
|
||||
appConfig = await appConfig.json()
|
||||
|
||||
let updateBranch = appConfig.update_branch || 'main'
|
||||
|
||||
let news = document.querySelector('#news')
|
||||
let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`)
|
||||
if (releaseNotes.status != 200) {
|
||||
return
|
||||
}
|
||||
releaseNotes = await releaseNotes.text()
|
||||
news.innerHTML = marked.parse(releaseNotes)
|
||||
}
|
||||
|
||||
document.querySelector('body').appendChild(markedScript)
|
||||
})()
|
@ -118,8 +118,8 @@ def load_model_ckpt():
|
||||
model.cdevice = torch.device(thread_data.device)
|
||||
model.unet_bs = thread_data.unet_bs
|
||||
model.turbo = thread_data.turbo
|
||||
if thread_data.device != 'cpu':
|
||||
model.to(thread_data.device)
|
||||
# if thread_data.device != 'cpu':
|
||||
# model.to(thread_data.device)
|
||||
#if thread_data.reduced_memory:
|
||||
#model.model1.to("cpu")
|
||||
#model.model2.to("cpu")
|
||||
@ -129,33 +129,42 @@ def load_model_ckpt():
|
||||
_, _ = modelCS.load_state_dict(sd, strict=False)
|
||||
modelCS.eval()
|
||||
modelCS.cond_stage_model.device = torch.device(thread_data.device)
|
||||
if thread_data.device != 'cpu':
|
||||
if thread_data.reduced_memory:
|
||||
modelCS.to('cpu')
|
||||
else:
|
||||
modelCS.to(thread_data.device) # Preload on device if not already there.
|
||||
# if thread_data.device != 'cpu':
|
||||
# if thread_data.reduced_memory:
|
||||
# modelCS.to('cpu')
|
||||
# else:
|
||||
# modelCS.to(thread_data.device) # Preload on device if not already there.
|
||||
thread_data.modelCS = modelCS
|
||||
|
||||
modelFS = instantiate_from_config(config.modelFirstStage)
|
||||
_, _ = modelFS.load_state_dict(sd, strict=False)
|
||||
|
||||
if thread_data.vae_file is not None:
|
||||
for model_extension in ['.ckpt', '.vae.pt']:
|
||||
if os.path.exists(thread_data.vae_file + model_extension):
|
||||
print(f"Loading VAE weights from: {thread_data.vae_file}{model_extension}")
|
||||
vae_ckpt = torch.load(thread_data.vae_file + model_extension, map_location="cpu")
|
||||
vae_dict = {k: v for k, v in vae_ckpt["state_dict"].items() if k[0:4] != "loss"}
|
||||
modelFS.first_stage_model.load_state_dict(vae_dict, strict=False)
|
||||
break
|
||||
else:
|
||||
print(f'Cannot find VAE file: {thread_data.vae_file}{model_extension}')
|
||||
try:
|
||||
loaded = False
|
||||
for model_extension in ['.ckpt', '.vae.pt']:
|
||||
if os.path.exists(thread_data.vae_file + model_extension):
|
||||
print(f"Loading VAE weights from: {thread_data.vae_file}{model_extension}")
|
||||
vae_ckpt = torch.load(thread_data.vae_file + model_extension, map_location="cpu")
|
||||
vae_dict = {k: v for k, v in vae_ckpt["state_dict"].items() if k[0:4] != "loss"}
|
||||
modelFS.first_stage_model.load_state_dict(vae_dict, strict=False)
|
||||
loaded = True
|
||||
break
|
||||
|
||||
if not loaded:
|
||||
print(f'Cannot find VAE: {thread_data.vae_file}')
|
||||
thread_data.vae_file = None
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
print(f'Could not load VAE: {thread_data.vae_file}')
|
||||
thread_data.vae_file = None
|
||||
|
||||
modelFS.eval()
|
||||
if thread_data.device != 'cpu':
|
||||
if thread_data.reduced_memory:
|
||||
modelFS.to('cpu')
|
||||
else:
|
||||
modelFS.to(thread_data.device) # Preload on device if not already there.
|
||||
# if thread_data.device != 'cpu':
|
||||
# if thread_data.reduced_memory:
|
||||
# modelFS.to('cpu')
|
||||
# else:
|
||||
# modelFS.to(thread_data.device) # Preload on device if not already there.
|
||||
thread_data.modelFS = modelFS
|
||||
del sd
|
||||
|
||||
@ -210,35 +219,48 @@ def unload_models():
|
||||
|
||||
gc()
|
||||
|
||||
def wait_model_move_to(model, target_device): # Send to target_device and wait until complete.
|
||||
if thread_data.device == target_device: return
|
||||
start_mem = torch.cuda.memory_allocated(thread_data.device) / 1e6
|
||||
if start_mem <= 0: return
|
||||
model_name = model.__class__.__name__
|
||||
print(f'Device {thread_data.device} - Sending model {model_name} to {target_device} | Memory transfer starting. Memory Used: {round(start_mem)}Mb')
|
||||
start_time = time.time()
|
||||
model.to(target_device)
|
||||
time_step = start_time
|
||||
WARNING_TIMEOUT = 1.5 # seconds - Show activity in console after timeout.
|
||||
last_mem = start_mem
|
||||
is_transfering = True
|
||||
while is_transfering:
|
||||
time.sleep(0.5) # 500ms
|
||||
mem = torch.cuda.memory_allocated(thread_data.device) / 1e6
|
||||
is_transfering = bool(mem > 0 and mem < last_mem) # still stuff loaded, but less than last time.
|
||||
last_mem = mem
|
||||
if not is_transfering:
|
||||
break;
|
||||
if time.time() - time_step > WARNING_TIMEOUT: # Long delay, print to console to show activity.
|
||||
print(f'Device {thread_data.device} - Waiting for Memory transfer. Memory Used: {round(mem)}Mb, Transfered: {round(start_mem - mem)}Mb')
|
||||
time_step = time.time()
|
||||
print(f'Device {thread_data.device} - {model_name} Moved: {round(start_mem - last_mem)}Mb in {round(time.time() - start_time, 3)} seconds to {target_device}')
|
||||
# def wait_model_move_to(model, target_device): # Send to target_device and wait until complete.
|
||||
# if thread_data.device == target_device: return
|
||||
# start_mem = torch.cuda.memory_allocated(thread_data.device) / 1e6
|
||||
# if start_mem <= 0: return
|
||||
# model_name = model.__class__.__name__
|
||||
# print(f'Device {thread_data.device} - Sending model {model_name} to {target_device} | Memory transfer starting. Memory Used: {round(start_mem)}Mb')
|
||||
# start_time = time.time()
|
||||
# model.to(target_device)
|
||||
# time_step = start_time
|
||||
# WARNING_TIMEOUT = 1.5 # seconds - Show activity in console after timeout.
|
||||
# last_mem = start_mem
|
||||
# is_transfering = True
|
||||
# while is_transfering:
|
||||
# time.sleep(0.5) # 500ms
|
||||
# mem = torch.cuda.memory_allocated(thread_data.device) / 1e6
|
||||
# is_transfering = bool(mem > 0 and mem < last_mem) # still stuff loaded, but less than last time.
|
||||
# last_mem = mem
|
||||
# if not is_transfering:
|
||||
# break;
|
||||
# if time.time() - time_step > WARNING_TIMEOUT: # Long delay, print to console to show activity.
|
||||
# print(f'Device {thread_data.device} - Waiting for Memory transfer. Memory Used: {round(mem)}Mb, Transfered: {round(start_mem - mem)}Mb')
|
||||
# time_step = time.time()
|
||||
# print(f'Device {thread_data.device} - {model_name} Moved: {round(start_mem - last_mem)}Mb in {round(time.time() - start_time, 3)} seconds to {target_device}')
|
||||
|
||||
def move_to_cpu(model):
|
||||
if thread_data.device != "cpu":
|
||||
d = torch.device(thread_data.device)
|
||||
mem = torch.cuda.memory_allocated(d) / 1e6
|
||||
model.to("cpu")
|
||||
while torch.cuda.memory_allocated(d) / 1e6 >= mem:
|
||||
time.sleep(1)
|
||||
|
||||
def load_model_gfpgan():
|
||||
if thread_data.gfpgan_file is None: raise ValueError(f'Thread gfpgan_file is undefined.')
|
||||
|
||||
# hack for a bug in facexlib: https://github.com/xinntao/facexlib/pull/19/files
|
||||
from facexlib.detection import retinaface
|
||||
retinaface.device = torch.device(thread_data.device)
|
||||
print('forced retinaface.device to', thread_data.device)
|
||||
|
||||
model_path = thread_data.gfpgan_file + ".pth"
|
||||
device = 'cuda:0' if force_gfpgan_to_cuda0 else thread_data.device
|
||||
thread_data.model_gfpgan = GFPGANer(device=torch.device(device), model_path=model_path, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None)
|
||||
thread_data.model_gfpgan = GFPGANer(device=torch.device(thread_data.device), model_path=model_path, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None)
|
||||
print('loaded', thread_data.gfpgan_file, 'to', thread_data.model_gfpgan.device, 'precision', thread_data.precision)
|
||||
|
||||
def load_model_real_esrgan():
|
||||
@ -288,10 +310,10 @@ def apply_filters(filter_name, image_data, model_path=None):
|
||||
print(f'Applying filter {filter_name}...')
|
||||
gc() # Free space before loading new data.
|
||||
|
||||
if filter_name == 'gfpgan':
|
||||
if isinstance(image_data, torch.Tensor):
|
||||
image_data.to('cuda:0' if force_gfpgan_to_cuda0 else thread_data.device)
|
||||
if isinstance(image_data, torch.Tensor):
|
||||
image_data.to(thread_data.device)
|
||||
|
||||
if filter_name == 'gfpgan':
|
||||
if model_path is not None and model_path != thread_data.gfpgan_file:
|
||||
thread_data.gfpgan_file = model_path
|
||||
load_model_gfpgan()
|
||||
@ -303,9 +325,6 @@ def apply_filters(filter_name, image_data, model_path=None):
|
||||
image_data = output[:,:,::-1]
|
||||
|
||||
if filter_name == 'real_esrgan':
|
||||
if isinstance(image_data, torch.Tensor):
|
||||
image_data.to(thread_data.device)
|
||||
|
||||
if model_path is not None and model_path != thread_data.real_esrgan_file:
|
||||
thread_data.real_esrgan_file = model_path
|
||||
load_model_real_esrgan()
|
||||
@ -473,7 +492,8 @@ def do_mk_img(req: Request):
|
||||
mask = mask.half()
|
||||
|
||||
# Send to CPU and wait until complete.
|
||||
wait_model_move_to(thread_data.modelFS, 'cpu')
|
||||
# wait_model_move_to(thread_data.modelFS, 'cpu')
|
||||
move_to_cpu(thread_data.modelFS)
|
||||
|
||||
assert 0. <= req.prompt_strength <= 1., 'can only work with strength in [0.0, 1.0]'
|
||||
t_enc = int(req.prompt_strength * req.num_inference_steps)
|
||||
@ -549,10 +569,6 @@ def do_mk_img(req: Request):
|
||||
img_data[i] = x_sample
|
||||
del x_samples, x_samples_ddim, x_sample
|
||||
|
||||
if thread_data.reduced_memory:
|
||||
# Send to CPU and wait until complete.
|
||||
wait_model_move_to(thread_data.modelFS, 'cpu')
|
||||
|
||||
print("saving images")
|
||||
for i in range(batch_size):
|
||||
img = Image.fromarray(img_data[i])
|
||||
@ -606,6 +622,7 @@ def do_mk_img(req: Request):
|
||||
|
||||
# if thread_data.reduced_memory:
|
||||
# unload_filters()
|
||||
move_to_cpu(thread_data.modelFS)
|
||||
del img_data
|
||||
gc()
|
||||
if thread_data.device != 'cpu':
|
||||
@ -645,7 +662,9 @@ def _txt2img(opt_W, opt_H, opt_n_samples, opt_ddim_steps, opt_scale, start_code,
|
||||
shape = [opt_n_samples, opt_C, opt_H // opt_f, opt_W // opt_f]
|
||||
|
||||
# Send to CPU and wait until complete.
|
||||
wait_model_move_to(thread_data.modelCS, 'cpu')
|
||||
# wait_model_move_to(thread_data.modelCS, 'cpu')
|
||||
|
||||
move_to_cpu(thread_data.modelCS)
|
||||
|
||||
if sampler_name == 'ddim':
|
||||
thread_data.model.make_schedule(ddim_num_steps=opt_ddim_steps, ddim_eta=opt_ddim_eta, verbose=False)
|
||||
|
@ -217,10 +217,6 @@ def thread_get_next_task():
|
||||
task = None
|
||||
try: # Select a render task.
|
||||
for queued_task in tasks_queue:
|
||||
if queued_task.request.use_face_correction and runtime.thread_data.device == 'cpu' and is_alive() == 1:
|
||||
queued_task.error = Exception('The CPU cannot be used to run this task currently. Please remove "Fix incorrect faces" from Image Settings and try again.')
|
||||
task = queued_task
|
||||
break
|
||||
if queued_task.render_device and runtime.thread_data.device != queued_task.render_device:
|
||||
# Is asking for a specific render device.
|
||||
if is_alive(queued_task.render_device) > 0:
|
||||
@ -272,7 +268,7 @@ def thread_render(device):
|
||||
return
|
||||
task = thread_get_next_task()
|
||||
if task is None:
|
||||
time.sleep(1)
|
||||
time.sleep(0.05)
|
||||
continue
|
||||
if task.error is not None:
|
||||
print(task.error)
|
||||
@ -440,7 +436,7 @@ def stop_render_thread(device):
|
||||
try:
|
||||
device_manager.validate_device_id(device, log_prefix='stop_render_thread')
|
||||
except:
|
||||
print(traceback.format_exec())
|
||||
print(traceback.format_exc())
|
||||
return False
|
||||
|
||||
if not manager_lock.acquire(blocking=True, timeout=LOCK_TIMEOUT): raise Exception('stop_render_thread' + ERR_LOCK_FAILED)
|
||||
|
107
ui/server.py
107
ui/server.py
@ -7,6 +7,8 @@ import traceback
|
||||
|
||||
import sys
|
||||
import os
|
||||
import picklescan.scanner
|
||||
import rich
|
||||
|
||||
SD_DIR = os.getcwd()
|
||||
print('started in ', SD_DIR)
|
||||
@ -16,7 +18,13 @@ sys.path.append(os.path.dirname(SD_UI_DIR))
|
||||
|
||||
CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, '..', 'scripts'))
|
||||
MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'models'))
|
||||
UI_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'plugins', 'ui'))
|
||||
|
||||
USER_UI_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'plugins', 'ui'))
|
||||
CORE_UI_PLUGINS_DIR = os.path.abspath(os.path.join(SD_UI_DIR, 'plugins', 'ui'))
|
||||
UI_PLUGINS_SOURCES = ((CORE_UI_PLUGINS_DIR, 'core'), (USER_UI_PLUGINS_DIR, 'user'))
|
||||
|
||||
STABLE_DIFFUSION_MODEL_EXTENSIONS = ['.ckpt']
|
||||
VAE_MODEL_EXTENSIONS = ['.vae.pt', '.ckpt']
|
||||
|
||||
OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder
|
||||
TASK_TTL = 15 * 60 # Discard last session's task timeout
|
||||
@ -49,15 +57,25 @@ app = FastAPI()
|
||||
modifiers_cache = None
|
||||
outpath = os.path.join(os.path.expanduser("~"), OUTPUT_DIRNAME)
|
||||
|
||||
os.makedirs(UI_PLUGINS_DIR, exist_ok=True)
|
||||
os.makedirs(USER_UI_PLUGINS_DIR, exist_ok=True)
|
||||
|
||||
# don't show access log entries for URLs that start with the given prefix
|
||||
ACCESS_LOG_SUPPRESS_PATH_PREFIXES = ['/ping', '/image', '/modifier-thumbnails']
|
||||
|
||||
NOCACHE_HEADERS={"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
|
||||
|
||||
app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media')), name="media")
|
||||
app.mount('/plugins', StaticFiles(directory=UI_PLUGINS_DIR), name="plugins")
|
||||
class NoCacheStaticFiles(StaticFiles):
|
||||
def is_not_modified(self, response_headers, request_headers) -> bool:
|
||||
if 'content-type' in response_headers and ('javascript' in response_headers['content-type'] or 'css' in response_headers['content-type']):
|
||||
response_headers.update(NOCACHE_HEADERS)
|
||||
return False
|
||||
|
||||
return super().is_not_modified(response_headers, request_headers)
|
||||
|
||||
app.mount('/media', NoCacheStaticFiles(directory=os.path.join(SD_UI_DIR, 'media')), name="media")
|
||||
|
||||
for plugins_dir, dir_prefix in UI_PLUGINS_SOURCES:
|
||||
app.mount(f'/plugins/{dir_prefix}', NoCacheStaticFiles(directory=plugins_dir), name=f"plugins-{dir_prefix}")
|
||||
|
||||
def getConfig(default_val=APP_CONFIG_DEFAULTS):
|
||||
try:
|
||||
@ -65,13 +83,21 @@ def getConfig(default_val=APP_CONFIG_DEFAULTS):
|
||||
if not os.path.exists(config_json_path):
|
||||
return default_val
|
||||
with open(config_json_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
config = json.load(f)
|
||||
if 'net' not in config:
|
||||
config['net'] = {}
|
||||
if os.getenv('SD_UI_BIND_PORT') is not None:
|
||||
config['net']['listen_port'] = int(os.getenv('SD_UI_BIND_PORT'))
|
||||
if os.getenv('SD_UI_BIND_IP') is not None:
|
||||
config['net']['listen_to_network'] = ( os.getenv('SD_UI_BIND_IP') == '0.0.0.0' )
|
||||
return config
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
print(traceback.format_exc())
|
||||
return default_val
|
||||
|
||||
def setConfig(config):
|
||||
print( json.dumps(config) )
|
||||
try: # config.json
|
||||
config_json_path = os.path.join(CONFIG_DIR, 'config.json')
|
||||
with open(config_json_path, 'w', encoding='utf-8') as f:
|
||||
@ -85,10 +111,10 @@ def setConfig(config):
|
||||
|
||||
if 'update_branch' in config:
|
||||
config_bat.append(f"@set update_branch={config['update_branch']}")
|
||||
if os.getenv('SD_UI_BIND_PORT') is not None:
|
||||
config_bat.append(f"@set SD_UI_BIND_PORT={os.getenv('SD_UI_BIND_PORT')}")
|
||||
if os.getenv('SD_UI_BIND_IP') is not None:
|
||||
config_bat.append(f"@set SD_UI_BIND_IP={os.getenv('SD_UI_BIND_IP')}")
|
||||
|
||||
config_bat.append(f"@set SD_UI_BIND_PORT={config['net']['listen_port']}")
|
||||
bind_ip = '0.0.0.0' if config['net']['listen_to_network'] else '127.0.0.1'
|
||||
config_bat.append(f"@set SD_UI_BIND_IP={bind_ip}")
|
||||
|
||||
if len(config_bat) > 0:
|
||||
with open(config_bat_path, 'w', encoding='utf-8') as f:
|
||||
@ -102,10 +128,10 @@ def setConfig(config):
|
||||
|
||||
if 'update_branch' in config:
|
||||
config_sh.append(f"export update_branch={config['update_branch']}")
|
||||
if os.getenv('SD_UI_BIND_PORT') is not None:
|
||||
config_sh.append(f"export SD_UI_BIND_PORT={os.getenv('SD_UI_BIND_PORT')}")
|
||||
if os.getenv('SD_UI_BIND_IP') is not None:
|
||||
config_sh.append(f"export SD_UI_BIND_IP={os.getenv('SD_UI_BIND_IP')}")
|
||||
|
||||
config_sh.append(f"export SD_UI_BIND_PORT={config['net']['listen_port']}")
|
||||
bind_ip = '0.0.0.0' if config['net']['listen_to_network'] else '127.0.0.1'
|
||||
config_sh.append(f"export SD_UI_BIND_IP={bind_ip}")
|
||||
|
||||
if len(config_sh) > 1:
|
||||
with open(config_sh_path, 'w', encoding='utf-8') as f:
|
||||
@ -147,11 +173,11 @@ def resolve_model_to_use(model_name:str, model_type:str, model_dir:str, model_ex
|
||||
raise Exception('No valid models found.')
|
||||
|
||||
def resolve_ckpt_to_use(model_name:str=None):
|
||||
return resolve_model_to_use(model_name, model_type='stable-diffusion', model_dir='stable-diffusion', model_extensions=['.ckpt'], default_models=APP_CONFIG_DEFAULT_MODELS)
|
||||
return resolve_model_to_use(model_name, model_type='stable-diffusion', model_dir='stable-diffusion', model_extensions=STABLE_DIFFUSION_MODEL_EXTENSIONS, default_models=APP_CONFIG_DEFAULT_MODELS)
|
||||
|
||||
def resolve_vae_to_use(model_name:str=None):
|
||||
try:
|
||||
return resolve_model_to_use(model_name, model_type='vae', model_dir='vae', model_extensions=['.vae.pt', '.ckpt'], default_models=[])
|
||||
return resolve_model_to_use(model_name, model_type='vae', model_dir='vae', model_extensions=VAE_MODEL_EXTENSIONS, default_models=[])
|
||||
except:
|
||||
return None
|
||||
|
||||
@ -160,6 +186,8 @@ class SetAppConfigRequest(BaseModel):
|
||||
render_devices: Union[List[str], List[int], str, int] = None
|
||||
model_vae: str = None
|
||||
ui_open_browser_on_start: bool = None
|
||||
listen_to_network: bool = None
|
||||
listen_port: int = None
|
||||
|
||||
@app.post('/app_config')
|
||||
async def setAppConfig(req : SetAppConfigRequest):
|
||||
@ -172,6 +200,14 @@ async def setAppConfig(req : SetAppConfigRequest):
|
||||
if 'ui' not in config:
|
||||
config['ui'] = {}
|
||||
config['ui']['open_browser_on_start'] = req.ui_open_browser_on_start
|
||||
if req.listen_to_network is not None:
|
||||
if 'net' not in config:
|
||||
config['net'] = {}
|
||||
config['net']['listen_to_network'] = bool(req.listen_to_network)
|
||||
if req.listen_port is not None:
|
||||
if 'net' not in config:
|
||||
config['net'] = {}
|
||||
config['net']['listen_port'] = int(req.listen_port)
|
||||
try:
|
||||
setConfig(config)
|
||||
|
||||
@ -183,6 +219,20 @@ async def setAppConfig(req : SetAppConfigRequest):
|
||||
print(traceback.format_exc())
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
def is_malicious_model(file_path):
|
||||
try:
|
||||
scan_result = picklescan.scanner.scan_file_path(file_path)
|
||||
if scan_result.issues_count > 0 or scan_result.infected_files > 0:
|
||||
rich.print(":warning: [bold red]Scan %s: %d scanned, %d issue, %d infected.[/bold red]" % (file_path, scan_result.scanned_files, scan_result.issues_count, scan_result.infected_files))
|
||||
return True
|
||||
else:
|
||||
rich.print("Scan %s: [green]%d scanned, %d issue, %d infected.[/green]" % (file_path, scan_result.scanned_files, scan_result.issues_count, scan_result.infected_files))
|
||||
return False
|
||||
except Exception as e:
|
||||
print('error while scanning', file_path, 'error:', e)
|
||||
|
||||
return False
|
||||
|
||||
def getModels():
|
||||
models = {
|
||||
'active': {
|
||||
@ -202,16 +252,22 @@ def getModels():
|
||||
|
||||
for file in os.listdir(models_dir):
|
||||
for model_extension in model_extensions:
|
||||
if file.endswith(model_extension):
|
||||
model_name = file[:-len(model_extension)]
|
||||
models['options'][model_type].append(model_name)
|
||||
if not file.endswith(model_extension):
|
||||
continue
|
||||
|
||||
if is_malicious_model(os.path.join(models_dir, file)):
|
||||
models['scan-error'] = file
|
||||
return
|
||||
|
||||
model_name = file[:-len(model_extension)]
|
||||
models['options'][model_type].append(model_name)
|
||||
|
||||
models['options'][model_type] = [*set(models['options'][model_type])] # remove duplicates
|
||||
models['options'][model_type].sort()
|
||||
|
||||
# custom models
|
||||
listModels(models_dirname='stable-diffusion', model_type='stable-diffusion', model_extensions=['.ckpt'])
|
||||
listModels(models_dirname='vae', model_type='vae', model_extensions=['.vae.pt', '.ckpt'])
|
||||
listModels(models_dirname='stable-diffusion', model_type='stable-diffusion', model_extensions=STABLE_DIFFUSION_MODEL_EXTENSIONS)
|
||||
listModels(models_dirname='vae', model_type='vae', model_extensions=VAE_MODEL_EXTENSIONS)
|
||||
|
||||
# legacy
|
||||
custom_weight_path = os.path.join(SD_DIR, 'custom-model.ckpt')
|
||||
@ -223,9 +279,10 @@ def getModels():
|
||||
def getUIPlugins():
|
||||
plugins = []
|
||||
|
||||
for file in os.listdir(UI_PLUGINS_DIR):
|
||||
if file.endswith('.plugin.js'):
|
||||
plugins.append(f'/plugins/{file}')
|
||||
for plugins_dir, dir_prefix in UI_PLUGINS_SOURCES:
|
||||
for file in os.listdir(plugins_dir):
|
||||
if file.endswith('.plugin.js'):
|
||||
plugins.append(f'/plugins/{dir_prefix}/{file}')
|
||||
|
||||
return plugins
|
||||
|
||||
@ -396,7 +453,9 @@ update_render_threads()
|
||||
def open_browser():
|
||||
config = getConfig()
|
||||
ui = config.get('ui', {})
|
||||
net = config.get('net', {'listen_port':9000})
|
||||
port = net.get('listen_port', 9000)
|
||||
if ui.get('open_browser_on_start', True):
|
||||
import webbrowser; webbrowser.open('http://localhost:9000')
|
||||
import webbrowser; webbrowser.open(f"http://localhost:{port}")
|
||||
|
||||
open_browser()
|
||||
|
Reference in New Issue
Block a user