2023-02-01 04:46:36 +00:00
# Copyright (c) 2023 Eugene Brodsky (https://github.com/ebr)
2023-01-08 08:09:04 +00:00
"""
Installer user interaction
"""
2023-01-30 08:15:05 +00:00
import os
2023-01-08 08:09:04 +00:00
import platform
from pathlib import Path
2023-01-10 21:55:57 +00:00
from prompt_toolkit import prompt
from prompt_toolkit . completion import PathCompleter
2023-01-13 06:57:06 +00:00
from prompt_toolkit . validation import Validator
2023-01-08 08:09:04 +00:00
from rich import box , print
2023-02-06 05:57:21 +00:00
from rich . console import Console , Group , group
2023-01-08 08:09:04 +00:00
from rich . panel import Panel
from rich . prompt import Confirm
from rich . style import Style
2023-01-14 16:24:18 +00:00
from rich . syntax import Syntax
2023-01-17 05:47:36 +00:00
from rich . text import Text
2023-01-08 08:09:04 +00:00
2023-01-16 07:38:50 +00:00
"""
INVOKE_AI_SRC = https : / / github . com / invoke - ai / InvokeAI / archive / refs / tags / $ { INVOKEAI_VERSION } . zip
INSTRUCTIONS = https : / / invoke - ai . github . io / InvokeAI / installation / INSTALL_AUTOMATED /
TROUBLESHOOTING = https : / / invoke - ai . github . io / InvokeAI / installation / INSTALL_AUTOMATED / #troubleshooting
"""
2023-01-08 08:09:04 +00:00
OS = platform . uname ( ) . system
ARCH = platform . uname ( ) . machine
2023-01-14 16:24:18 +00:00
if OS == " Windows " :
# Windows terminals look better without a background colour
console = Console ( style = Style ( color = " grey74 " ) )
else :
console = Console ( style = Style ( color = " grey74 " , bgcolor = " grey19 " ) )
2023-01-30 08:15:05 +00:00
2023-01-08 08:09:04 +00:00
def welcome ( ) :
2023-02-06 05:57:21 +00:00
@group ( )
def text ( ) :
if ( platform_specific := _platform_specific_help ( ) ) != " " :
yield platform_specific
yield " "
yield Text . from_markup ( " Some of the installation steps take a long time to run. Please be patient. If the script appears to hang for more than 10 minutes, please interrupt with [i]Control-C[/] and retry. " , justify = " center " )
2023-01-08 08:09:04 +00:00
console . rule ( )
print (
Panel (
2023-01-09 05:13:01 +00:00
title = " [bold wheat1]Welcome to the InvokeAI Installer " ,
2023-02-06 05:57:21 +00:00
renderable = text ( ) ,
2023-01-08 08:09:04 +00:00
box = box . DOUBLE ,
2023-02-06 05:57:21 +00:00
expand = True ,
2023-01-08 08:09:04 +00:00
padding = ( 1 , 2 ) ,
style = Style ( bgcolor = " grey23 " , color = " orange1 " ) ,
2023-01-09 05:13:01 +00:00
subtitle = f " [bold grey39] { OS } - { ARCH } " ,
2023-01-08 08:09:04 +00:00
)
)
console . line ( )
2023-02-01 04:13:37 +00:00
def confirm_install ( dest : Path ) - > bool :
if dest . exists ( ) :
print ( f " :exclamation: Directory { dest } already exists :exclamation: " )
dest_confirmed = Confirm . ask (
" :stop_sign: Are you sure you want to (re)install in this location? " ,
default = False ,
)
else :
2023-02-02 05:59:23 +00:00
print ( f " InvokeAI will be installed in { dest } " )
dest_confirmed = not Confirm . ask ( f " Would you like to pick a different location? " , default = False )
2023-02-01 04:13:37 +00:00
console . line ( )
return dest_confirmed
2023-01-08 08:09:04 +00:00
2023-02-01 04:13:37 +00:00
def dest_path ( dest = None ) - > Path :
2023-01-08 08:09:04 +00:00
"""
Prompt the user for the destination path and create the path
2023-02-01 04:13:37 +00:00
: param dest : a filesystem path , defaults to None
: type dest : str , optional
2023-01-08 08:09:04 +00:00
: return : absolute path to the created installation directory
: rtype : Path
"""
2023-01-10 21:55:57 +00:00
if dest is not None :
2023-02-01 04:13:37 +00:00
dest = Path ( dest ) . expanduser ( ) . resolve ( )
2023-01-30 08:15:05 +00:00
else :
2023-02-01 04:13:37 +00:00
dest = Path . cwd ( ) . expanduser ( ) . resolve ( )
prev_dest = dest . expanduser ( ) . resolve ( )
2023-01-08 08:09:04 +00:00
2023-02-01 04:13:37 +00:00
dest_confirmed = confirm_install ( dest )
2023-01-14 16:24:18 +00:00
2023-01-30 08:15:05 +00:00
while not dest_confirmed :
2023-01-10 21:55:57 +00:00
2023-01-30 08:15:05 +00:00
# if the given destination already exists, the starting point for browsing is its parent directory.
# the user may have made a typo, or otherwise wants to place the root dir next to an existing one.
# if the destination dir does NOT exist, then the user must have changed their mind about the selection.
# since we can't read their mind, start browsing at Path.cwd().
browse_start = ( prev_dest . parent if prev_dest . exists ( ) else Path . cwd ( ) ) . expanduser ( ) . resolve ( )
path_completer = PathCompleter (
only_directories = True ,
expanduser = True ,
get_paths = lambda : [ browse_start ] ,
# get_paths=lambda: [".."].extend(list(browse_start.iterdir()))
)
2023-01-10 21:55:57 +00:00
2023-01-30 08:15:05 +00:00
console . line ( )
print ( f " [orange3]Please select the destination directory for the installation:[/] \ [ { browse_start } ]: " )
selected = prompt (
f " >>> " ,
complete_in_thread = True ,
completer = path_completer ,
default = str ( browse_start ) + os . sep ,
vi_mode = True ,
complete_while_typing = True
# Test that this is not needed on Windows
# complete_style=CompleteStyle.READLINE_LIKE,
)
prev_dest = dest
dest = Path ( selected )
console . line ( )
2023-01-10 21:55:57 +00:00
2023-02-01 04:13:37 +00:00
dest_confirmed = confirm_install ( dest . expanduser ( ) . resolve ( ) )
2023-01-30 08:15:05 +00:00
if not dest_confirmed :
dest = prev_dest
dest = dest . expanduser ( ) . resolve ( )
2023-01-08 08:09:04 +00:00
try :
dest . mkdir ( exist_ok = True , parents = True )
return dest
except PermissionError as exc :
print (
f " Failed to create directory { dest } due to insufficient permissions " ,
style = Style ( color = " red " ) ,
highlight = True ,
)
except OSError as exc :
console . print_exception ( exc )
if Confirm . ask ( " Would you like to try again? " ) :
dest_path ( init_path )
else :
console . rule ( " Goodbye! " )
2023-01-13 06:57:06 +00:00
def graphical_accelerator ( ) :
"""
Prompt the user to select the graphical accelerator in their system
This does not validate user ' s choices (yet), but only offers choices
valid for the platform .
CUDA is the fallback .
We may be able to detect the GPU driver by shelling out to ` modprobe ` or ` lspci ` ,
but this is not yet supported or reliable . Also , some users may have exotic preferences .
"""
if ARCH == " arm64 " and OS != " Darwin " :
print ( f " Only CPU acceleration is available on { ARCH } architecture. Proceeding with that. " )
return " cpu "
2023-01-30 08:15:05 +00:00
nvidia = (
" an [gold1 b]NVIDIA[/] GPU (using CUDA™) " ,
" cuda " ,
)
amd = (
" an [gold1 b]AMD[/] GPU (using ROCm™) " ,
" rocm " ,
)
cpu = (
" no compatible GPU, or specifically prefer to use the CPU " ,
" cpu " ,
)
idk = (
" I ' m not sure what to choose " ,
" idk " ,
)
2023-01-13 06:57:06 +00:00
if OS == " Windows " :
options = [ nvidia , cpu ]
if OS == " Linux " :
options = [ nvidia , amd , cpu ]
elif OS == " Darwin " :
options = [ cpu ]
# future CoreML?
if len ( options ) == 1 :
2023-01-30 08:15:05 +00:00
print ( f ' Your platform [gold1] { OS } - { ARCH } [/] only supports the " { options [ 0 ] [ 1 ] } " driver. Proceeding with that. ' )
2023-01-13 06:57:06 +00:00
return options [ 0 ] [ 1 ]
# "I don't know" is always added the last option
options . append ( idk )
options = { str ( i ) : opt for i , opt in enumerate ( options , 1 ) }
2023-01-13 09:10:34 +00:00
console . rule ( " :space_invader: GPU (Graphics Card) selection :space_invader: " )
2023-01-30 08:15:05 +00:00
console . print (
Panel (
Group (
" \n " . join (
[
f " Detected the [gold1] { OS } - { ARCH } [/] platform " ,
" " ,
2023-02-06 05:57:21 +00:00
" See [deep_sky_blue1]https://invoke-ai.github.io/InvokeAI/#system[/] to ensure your system meets the minimum requirements. " ,
2023-01-30 08:15:05 +00:00
" " ,
" [red3]🠶[/] [b]Your GPU drivers must be correctly installed before using InvokeAI![/] [red3]🠴[/] " ,
]
) ,
2023-01-13 06:57:06 +00:00
" " ,
2023-01-13 09:10:34 +00:00
" Please select the type of GPU installed in your computer. " ,
2023-01-30 08:15:05 +00:00
Panel (
" \n " . join ( [ f " [dark_goldenrod b i] { i } [/] [dark_red]🢒[/] { opt [ 0 ] } " for ( i , opt ) in options . items ( ) ] ) ,
box = box . MINIMAL ,
) ,
2023-01-13 06:57:06 +00:00
) ,
box = box . MINIMAL ,
2023-01-13 09:10:34 +00:00
padding = ( 1 , 1 ) ,
2023-01-13 06:57:06 +00:00
)
)
2023-01-30 08:15:05 +00:00
choice = prompt (
" Please make your selection: " ,
validator = Validator . from_callable (
lambda n : n in options . keys ( ) , error_message = " Please select one the above options "
) ,
)
2023-01-13 06:57:06 +00:00
2023-01-13 09:10:34 +00:00
if options [ choice ] [ 1 ] == " idk " :
2023-01-30 08:15:05 +00:00
console . print (
" No problem. We will try to install a version that [i]should[/i] be compatible. :crossed_fingers: "
)
2023-01-13 09:10:34 +00:00
2023-01-13 06:57:06 +00:00
return options [ choice ] [ 1 ]
2023-01-13 09:10:34 +00:00
def simple_banner ( message : str ) - > None :
"""
A simple banner with a message , defined here for styling consistency
: param message : The message to display
: type message : str
"""
console . rule ( message )
2023-01-30 08:15:05 +00:00
2023-01-14 16:24:18 +00:00
# TODO this does not yet work correctly
def windows_long_paths_registry ( ) - > None :
"""
Display a message about applying the Windows long paths registry fix
"""
2023-01-30 08:15:05 +00:00
with open ( str ( Path ( __file__ ) . parent / " WinLongPathsEnabled.reg " ) , " r " , encoding = " utf-16le " ) as code :
2023-01-14 16:24:18 +00:00
syntax = Syntax ( code . read ( ) , line_numbers = True )
2023-01-30 08:15:05 +00:00
console . print (
Panel (
Group (
" \n " . join (
[
" We will now apply a registry fix to enable long paths on Windows. InvokeAI needs this to function correctly. We are asking your permission to modify the Windows Registry on your behalf. " ,
" " ,
" This is the change that will be applied: " ,
syntax ,
]
)
) ,
title = " Windows Long Paths registry fix " ,
box = box . HORIZONTALS ,
padding = ( 1 , 1 ) ,
)
)
2023-01-14 16:24:18 +00:00
2023-01-13 09:10:34 +00:00
def introduction ( ) - > None :
"""
Display a banner when starting configuration of the InvokeAI application
"""
console . rule ( )
2023-01-30 08:15:05 +00:00
console . print (
Panel (
title = " :art: Configuring InvokeAI :art: " ,
renderable = Group (
" " ,
" [b]This script will: " ,
" " ,
" 1. Configure the InvokeAI application directory " ,
" 2. Help download the Stable Diffusion weight files " ,
" and other large models that are needed for text to image generation " ,
" 3. Create initial configuration files. " ,
" " ,
" [i]At any point you may interrupt this program and resume later. " ,
) ,
)
)
2023-01-17 05:47:36 +00:00
console . line ( 2 )
2023-02-05 17:43:13 +00:00
def _platform_specific_help ( ) - > str :
2023-02-06 05:57:21 +00:00
if OS == " Darwin " :
text = Text . from_markup ( """ [b wheat1]macOS Users![/] \n \n Please be sure you have the [b wheat1]Xcode command-line tools[/] installed before continuing. \n If not, cancel with [i]Control-C[/] and follow the Xcode install instructions at [deep_sky_blue1]https://www.freecodecamp.org/news/install-xcode-command-line-tools/[/]. """ )
elif OS == " Windows " :
text = Text . from_markup ( """ [b wheat1]Windows Users![/] \n \n Before you start, please do the following:
1. Double - click on the file [ b wheat1 ] WinLongPathsEnabled . reg [ / ] in order to
2023-02-05 17:43:13 +00:00
enable long path support on your system .
2023-02-06 05:57:21 +00:00
2. Make sure you have the [ b wheat1 ] Visual C + + core libraries [ / ] installed . If not , install from
[ deep_sky_blue1 ] https : / / learn . microsoft . com / en - US / cpp / windows / latest - supported - vc - redist ? view = msvc - 170 [ / ] """ )
else :
text = " "
2023-02-05 17:43:13 +00:00
return text