2023-02-13 04:59:18 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# Copyright (c) 2022 Lincoln D. Stein (https://github.com/lstein)
|
|
|
|
# Before running stable-diffusion on an internet-isolated machine,
|
|
|
|
# run this script from one with internet connectivity. The
|
|
|
|
# two machines must share a common .cache directory.
|
2023-02-15 06:07:39 +00:00
|
|
|
|
2023-02-21 19:12:57 +00:00
|
|
|
"""
|
2023-02-16 05:34:15 +00:00
|
|
|
This is the npyscreen frontend to the model installation application.
|
2023-02-17 19:34:48 +00:00
|
|
|
The work is actually done in backend code in model_install_backend.py.
|
2023-02-21 19:12:57 +00:00
|
|
|
"""
|
2023-02-16 05:34:15 +00:00
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
from argparse import Namespace
|
2023-02-21 19:12:57 +00:00
|
|
|
from pathlib import Path
|
2023-03-03 06:02:00 +00:00
|
|
|
from shutil import get_terminal_size
|
2023-02-15 06:07:39 +00:00
|
|
|
from typing import List
|
2023-02-13 04:59:18 +00:00
|
|
|
|
|
|
|
import npyscreen
|
2023-02-15 06:07:39 +00:00
|
|
|
import torch
|
2023-02-14 21:32:54 +00:00
|
|
|
from npyscreen import widget
|
2023-02-13 04:59:18 +00:00
|
|
|
from omegaconf import OmegaConf
|
|
|
|
|
2023-04-29 13:43:40 +00:00
|
|
|
import invokeai.backend.util.logging as logger
|
2023-03-03 06:02:00 +00:00
|
|
|
|
|
|
|
from ...backend.config.model_install_backend import (
|
|
|
|
Dataset_path,
|
|
|
|
default_config_file,
|
|
|
|
default_dataset,
|
|
|
|
install_requested_models,
|
|
|
|
recommended_datasets,
|
|
|
|
)
|
|
|
|
from ...backend.util import choose_precision, choose_torch_device
|
|
|
|
from .widgets import (
|
|
|
|
CenteredTitleText,
|
|
|
|
MultiSelectColumns,
|
|
|
|
OffsetButtonPress,
|
|
|
|
TextBox,
|
|
|
|
set_min_terminal_size,
|
|
|
|
)
|
2023-05-04 04:43:51 +00:00
|
|
|
from invokeai.app.services.config import get_invokeai_config
|
2023-02-23 00:18:07 +00:00
|
|
|
|
|
|
|
# minimum size for the UI
|
|
|
|
MIN_COLS = 120
|
2023-05-30 17:49:43 +00:00
|
|
|
MIN_LINES = 50
|
2023-02-13 04:59:18 +00:00
|
|
|
|
2023-05-04 04:43:51 +00:00
|
|
|
config = get_invokeai_config()
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2023-02-19 21:08:58 +00:00
|
|
|
class addModelsForm(npyscreen.FormMultiPage):
|
2023-02-22 12:05:51 +00:00
|
|
|
# for responsive resizing - disabled
|
2023-03-03 06:02:00 +00:00
|
|
|
# FIX_MINIMUM_SIZE_WHEN_CREATED = False
|
|
|
|
|
2023-02-19 21:55:09 +00:00
|
|
|
def __init__(self, parentApp, name, multipage=False, *args, **keywords):
|
2023-02-21 19:12:57 +00:00
|
|
|
self.multipage = multipage
|
2023-05-30 17:49:43 +00:00
|
|
|
|
2023-05-30 04:38:37 +00:00
|
|
|
self.initial_models = OmegaConf.load(Dataset_path)['diffusers']
|
|
|
|
self.control_net_models = OmegaConf.load(Dataset_path)['controlnet']
|
|
|
|
self.installed_cn_models = self._get_installed_cn_models()
|
2023-05-30 17:49:43 +00:00
|
|
|
self._add_additional_cn_models(self.control_net_models,self.installed_cn_models)
|
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
try:
|
2023-02-16 05:34:15 +00:00
|
|
|
self.existing_models = OmegaConf.load(default_config_file())
|
2023-02-13 04:59:18 +00:00
|
|
|
except:
|
|
|
|
self.existing_models = dict()
|
2023-05-30 04:38:37 +00:00
|
|
|
self.starter_model_list = list(self.initial_models.keys())
|
2023-02-21 19:12:57 +00:00
|
|
|
self.installed_models = dict()
|
2023-02-19 21:55:09 +00:00
|
|
|
super().__init__(parentApp=parentApp, name=name, *args, **keywords)
|
2023-02-13 04:59:18 +00:00
|
|
|
|
|
|
|
def create(self):
|
2023-02-23 00:18:07 +00:00
|
|
|
window_width, window_height = get_terminal_size()
|
2023-02-14 21:32:54 +00:00
|
|
|
starter_model_labels = self._get_starter_model_labels()
|
2023-02-13 04:59:18 +00:00
|
|
|
recommended_models = [
|
|
|
|
x
|
|
|
|
for x in self.starter_model_list
|
|
|
|
if self.initial_models[x].get("recommended", False)
|
|
|
|
]
|
2023-02-15 06:07:39 +00:00
|
|
|
self.installed_models = sorted(
|
2023-02-21 19:12:57 +00:00
|
|
|
[x for x in list(self.initial_models.keys()) if x in self.existing_models]
|
2023-02-14 21:32:54 +00:00
|
|
|
)
|
2023-05-30 04:38:37 +00:00
|
|
|
|
|
|
|
cn_model_list = sorted(self.control_net_models.keys())
|
|
|
|
|
2023-02-16 05:34:15 +00:00
|
|
|
self.nextrely -= 1
|
2023-02-15 06:07:39 +00:00
|
|
|
self.add_widget_intelligent(
|
|
|
|
npyscreen.FixedText,
|
2023-02-21 19:12:57 +00:00
|
|
|
value="Use ctrl-N and ctrl-P to move to the <N>ext and <P>revious fields,",
|
2023-02-15 06:07:39 +00:00
|
|
|
editable=False,
|
2023-03-03 06:02:00 +00:00
|
|
|
color="CAUTION",
|
2023-02-15 06:07:39 +00:00
|
|
|
)
|
|
|
|
self.add_widget_intelligent(
|
|
|
|
npyscreen.FixedText,
|
2023-02-22 02:33:44 +00:00
|
|
|
value="Use cursor arrows to make a selection, and space to toggle checkboxes.",
|
2023-02-15 06:07:39 +00:00
|
|
|
editable=False,
|
2023-03-03 06:02:00 +00:00
|
|
|
color="CAUTION",
|
2023-02-15 06:07:39 +00:00
|
|
|
)
|
2023-02-16 05:34:15 +00:00
|
|
|
self.nextrely += 1
|
2023-02-24 05:53:48 +00:00
|
|
|
if len(self.starter_model_list) > 0:
|
|
|
|
self.add_widget_intelligent(
|
|
|
|
CenteredTitleText,
|
2023-05-30 04:38:37 +00:00
|
|
|
name="== DIFFUSERS MODELS (recommended ones selected) ==",
|
2023-02-24 05:53:48 +00:00
|
|
|
editable=False,
|
|
|
|
color="CONTROL",
|
|
|
|
)
|
|
|
|
self.nextrely -= 1
|
|
|
|
self.add_widget_intelligent(
|
|
|
|
CenteredTitleText,
|
|
|
|
name="Select from a starter set of Stable Diffusion models from HuggingFace.",
|
|
|
|
editable=False,
|
|
|
|
labelColor="CAUTION",
|
|
|
|
)
|
|
|
|
self.nextrely -= 1
|
|
|
|
# if user has already installed some initial models, then don't patronize them
|
|
|
|
# by showing more recommendations
|
|
|
|
show_recommended = not self.existing_models
|
|
|
|
self.models_selected = self.add_widget_intelligent(
|
|
|
|
npyscreen.MultiSelect,
|
|
|
|
name="Install Starter Models",
|
|
|
|
values=starter_model_labels,
|
|
|
|
value=[
|
|
|
|
self.starter_model_list.index(x)
|
|
|
|
for x in self.starter_model_list
|
2023-05-30 04:38:37 +00:00
|
|
|
if (show_recommended and x in recommended_models)\
|
|
|
|
or (x in self.existing_models)
|
2023-02-24 05:53:48 +00:00
|
|
|
],
|
|
|
|
max_height=len(starter_model_labels) + 1,
|
|
|
|
relx=4,
|
|
|
|
scroll_exit=True,
|
|
|
|
)
|
2023-05-30 04:38:37 +00:00
|
|
|
self.purge_deleted = self.add_widget_intelligent(
|
|
|
|
npyscreen.Checkbox,
|
2023-05-30 17:49:43 +00:00
|
|
|
name="Purge unchecked diffusers models from disk",
|
2023-05-30 04:38:37 +00:00
|
|
|
value=False,
|
|
|
|
scroll_exit=True,
|
|
|
|
relx=4,
|
|
|
|
)
|
2023-05-30 17:49:43 +00:00
|
|
|
self.nextrely += 1
|
2023-02-22 02:33:44 +00:00
|
|
|
self.add_widget_intelligent(
|
|
|
|
CenteredTitleText,
|
2023-03-03 06:02:00 +00:00
|
|
|
name="== IMPORT LOCAL AND REMOTE MODELS ==",
|
2023-02-22 02:33:44 +00:00
|
|
|
editable=False,
|
|
|
|
color="CONTROL",
|
|
|
|
)
|
|
|
|
self.nextrely -= 1
|
|
|
|
|
2023-02-14 21:32:54 +00:00
|
|
|
for line in [
|
2023-03-03 06:02:00 +00:00
|
|
|
"In the box below, enter URLs, file paths, or HuggingFace repository IDs.",
|
|
|
|
"Separate model names by lines or whitespace (Use shift-control-V to paste):",
|
2023-02-14 21:32:54 +00:00
|
|
|
]:
|
2023-02-14 05:02:19 +00:00
|
|
|
self.add_widget_intelligent(
|
2023-02-22 02:33:44 +00:00
|
|
|
CenteredTitleText,
|
2023-02-14 21:32:54 +00:00
|
|
|
name=line,
|
2023-02-14 05:02:19 +00:00
|
|
|
editable=False,
|
2023-02-22 02:33:44 +00:00
|
|
|
labelColor="CONTROL",
|
2023-03-03 06:02:00 +00:00
|
|
|
relx=4,
|
2023-02-14 05:02:19 +00:00
|
|
|
)
|
2023-02-14 21:32:54 +00:00
|
|
|
self.nextrely -= 1
|
2023-02-15 06:07:39 +00:00
|
|
|
self.import_model_paths = self.add_widget_intelligent(
|
2023-05-30 17:49:43 +00:00
|
|
|
TextBox, max_height=4, scroll_exit=True, editable=True, relx=4
|
2023-02-14 21:32:54 +00:00
|
|
|
)
|
2023-02-16 05:34:15 +00:00
|
|
|
self.nextrely += 1
|
2023-02-21 19:12:57 +00:00
|
|
|
self.show_directory_fields = self.add_widget_intelligent(
|
2023-02-16 05:34:15 +00:00
|
|
|
npyscreen.FormControlCheckbox,
|
2023-02-21 19:12:57 +00:00
|
|
|
name="Select a directory for models to import",
|
2023-02-16 05:34:15 +00:00
|
|
|
value=False,
|
|
|
|
)
|
|
|
|
self.autoload_directory = self.add_widget_intelligent(
|
|
|
|
npyscreen.TitleFilename,
|
2023-02-21 19:12:57 +00:00
|
|
|
name="Directory (<tab> autocompletes):",
|
2023-02-16 05:34:15 +00:00
|
|
|
select_dir=True,
|
|
|
|
must_exist=True,
|
|
|
|
use_two_lines=False,
|
2023-02-21 19:12:57 +00:00
|
|
|
labelColor="DANGER",
|
2023-02-16 05:34:15 +00:00
|
|
|
begin_entry_at=34,
|
|
|
|
scroll_exit=True,
|
|
|
|
)
|
|
|
|
self.autoscan_on_startup = self.add_widget_intelligent(
|
|
|
|
npyscreen.Checkbox,
|
2023-02-21 19:12:57 +00:00
|
|
|
name="Scan this directory each time InvokeAI starts for new models to import",
|
2023-02-16 05:34:15 +00:00
|
|
|
value=False,
|
2023-02-21 19:12:57 +00:00
|
|
|
relx=4,
|
2023-02-16 05:34:15 +00:00
|
|
|
scroll_exit=True,
|
|
|
|
)
|
2023-05-30 17:49:43 +00:00
|
|
|
self.add_widget_intelligent(
|
|
|
|
CenteredTitleText,
|
|
|
|
name="== CONTROLNET MODELS ==",
|
|
|
|
editable=False,
|
|
|
|
color="CONTROL",
|
|
|
|
)
|
|
|
|
self.nextrely -= 1
|
|
|
|
self.add_widget_intelligent(
|
|
|
|
CenteredTitleText,
|
|
|
|
name="Select the desired ControlNet models. Unchecked models will be purged from disk.",
|
|
|
|
editable=False,
|
|
|
|
labelColor="CAUTION",
|
|
|
|
)
|
|
|
|
columns=6
|
|
|
|
self.cn_models_selected = self.add_widget_intelligent(
|
|
|
|
MultiSelectColumns,
|
|
|
|
columns=columns,
|
|
|
|
name="Install ControlNet Models",
|
|
|
|
values=cn_model_list,
|
|
|
|
value=[
|
|
|
|
cn_model_list.index(x)
|
|
|
|
for x in cn_model_list
|
|
|
|
if x in self.installed_cn_models
|
|
|
|
],
|
|
|
|
max_height=len(cn_model_list)//columns + 1,
|
|
|
|
relx=4,
|
|
|
|
scroll_exit=True,
|
|
|
|
)
|
|
|
|
self.nextrely += 1
|
|
|
|
self.add_widget_intelligent(
|
|
|
|
npyscreen.TitleFixedText,
|
|
|
|
name='Additional ControlNet HuggingFace repo_ids to install (space separated):',
|
|
|
|
relx=4,
|
|
|
|
color='CONTROL',
|
|
|
|
editable=False,
|
|
|
|
scroll_exit=True
|
|
|
|
)
|
|
|
|
self.nextrely -= 1
|
|
|
|
self.additional_controlnet_ids = self.add_widget_intelligent(
|
|
|
|
TextBox, max_height=2, scroll_exit=True, editable=True, relx=4
|
|
|
|
)
|
2023-02-19 21:08:58 +00:00
|
|
|
self.cancel = self.add_widget_intelligent(
|
|
|
|
npyscreen.ButtonPress,
|
2023-02-21 19:12:57 +00:00
|
|
|
name="CANCEL",
|
|
|
|
rely=-3,
|
2023-02-19 21:08:58 +00:00
|
|
|
when_pressed_function=self.on_cancel,
|
|
|
|
)
|
2023-02-21 19:12:57 +00:00
|
|
|
done_label = "DONE"
|
|
|
|
back_label = "BACK"
|
2023-02-19 21:08:58 +00:00
|
|
|
button_length = len(done_label)
|
|
|
|
button_offset = 0
|
|
|
|
if self.multipage:
|
2023-02-21 19:12:57 +00:00
|
|
|
button_length += len(back_label) + 1
|
|
|
|
button_offset += len(back_label) + 1
|
2023-02-19 21:08:58 +00:00
|
|
|
self.back_button = self.add_widget_intelligent(
|
2023-02-22 02:33:44 +00:00
|
|
|
OffsetButtonPress,
|
2023-02-19 21:08:58 +00:00
|
|
|
name=back_label,
|
2023-02-21 19:12:57 +00:00
|
|
|
relx=(window_width - button_length) // 2,
|
2023-02-22 02:33:44 +00:00
|
|
|
offset=-3,
|
2023-02-21 19:12:57 +00:00
|
|
|
rely=-3,
|
|
|
|
when_pressed_function=self.on_back,
|
2023-02-19 21:08:58 +00:00
|
|
|
)
|
|
|
|
self.ok_button = self.add_widget_intelligent(
|
2023-02-22 02:33:44 +00:00
|
|
|
OffsetButtonPress,
|
2023-02-19 21:08:58 +00:00
|
|
|
name=done_label,
|
2023-02-22 02:33:44 +00:00
|
|
|
offset=+3,
|
2023-02-21 19:12:57 +00:00
|
|
|
relx=button_offset + 1 + (window_width - button_length) // 2,
|
|
|
|
rely=-3,
|
|
|
|
when_pressed_function=self.on_ok,
|
2023-02-19 21:08:58 +00:00
|
|
|
)
|
|
|
|
|
2023-02-21 19:12:57 +00:00
|
|
|
for i in [self.autoload_directory, self.autoscan_on_startup]:
|
2023-02-16 05:34:15 +00:00
|
|
|
self.show_directory_fields.addVisibleWhenSelected(i)
|
2023-02-15 06:07:39 +00:00
|
|
|
|
2023-02-21 16:47:41 +00:00
|
|
|
self.show_directory_fields.when_value_edited = self._clear_scan_directory
|
|
|
|
|
2023-02-15 06:07:39 +00:00
|
|
|
def resize(self):
|
|
|
|
super().resize()
|
2023-03-03 06:02:00 +00:00
|
|
|
if hasattr(self, "models_selected"):
|
2023-02-24 05:53:48 +00:00
|
|
|
self.models_selected.values = self._get_starter_model_labels()
|
2023-02-21 16:47:41 +00:00
|
|
|
|
|
|
|
def _clear_scan_directory(self):
|
|
|
|
if not self.show_directory_fields.value:
|
2023-02-21 19:12:57 +00:00
|
|
|
self.autoload_directory.value = ""
|
|
|
|
|
|
|
|
def _get_starter_model_labels(self) -> List[str]:
|
2023-02-23 00:18:07 +00:00
|
|
|
window_width, window_height = get_terminal_size()
|
2023-02-14 21:32:54 +00:00
|
|
|
label_width = 25
|
|
|
|
checkbox_width = 4
|
|
|
|
spacing_width = 2
|
|
|
|
description_width = window_width - label_width - checkbox_width - spacing_width
|
|
|
|
im = self.initial_models
|
2023-02-16 05:34:15 +00:00
|
|
|
names = self.starter_model_list
|
2023-02-21 19:12:57 +00:00
|
|
|
descriptions = [
|
|
|
|
im[x].description[0 : description_width - 3] + "..."
|
|
|
|
if len(im[x].description) > description_width
|
|
|
|
else im[x].description
|
|
|
|
for x in names
|
|
|
|
]
|
2023-02-14 21:32:54 +00:00
|
|
|
return [
|
2023-02-21 19:12:57 +00:00
|
|
|
f"%-{label_width}s %s" % (names[x], descriptions[x])
|
|
|
|
for x in range(0, len(names))
|
2023-02-14 21:32:54 +00:00
|
|
|
]
|
2023-02-13 04:59:18 +00:00
|
|
|
|
2023-05-30 04:38:37 +00:00
|
|
|
def _get_installed_cn_models(self)->list[str]:
|
2023-05-30 17:49:43 +00:00
|
|
|
cn_dir = config.controlnet_path
|
|
|
|
installed_cn_models = set()
|
|
|
|
for root, dirs, files in os.walk(cn_dir):
|
|
|
|
for name in dirs:
|
|
|
|
if Path(root, name, '.download_complete').exists():
|
|
|
|
installed_cn_models.add(name.replace('--','/'))
|
|
|
|
return installed_cn_models
|
|
|
|
|
|
|
|
def _add_additional_cn_models(self, known_models: dict, installed_models: set):
|
|
|
|
for i in installed_models:
|
|
|
|
if i in known_models:
|
|
|
|
continue
|
|
|
|
# translate from name to repo_id
|
|
|
|
repo_id = i.replace('--','/')
|
|
|
|
known_models.update({i: repo_id})
|
|
|
|
|
2023-02-21 19:12:57 +00:00
|
|
|
def _get_columns(self) -> int:
|
2023-02-23 00:18:07 +00:00
|
|
|
window_width, window_height = get_terminal_size()
|
2023-02-21 19:12:57 +00:00
|
|
|
cols = (
|
|
|
|
4
|
|
|
|
if window_width > 240
|
|
|
|
else 3
|
|
|
|
if window_width > 160
|
|
|
|
else 2
|
|
|
|
if window_width > 80
|
|
|
|
else 1
|
|
|
|
)
|
|
|
|
return min(cols, len(self.installed_models))
|
2023-02-15 06:07:39 +00:00
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
def on_ok(self):
|
2023-02-16 05:34:15 +00:00
|
|
|
self.parentApp.setNextForm(None)
|
2023-02-13 04:59:18 +00:00
|
|
|
self.editing = False
|
2023-02-15 06:07:39 +00:00
|
|
|
self.parentApp.user_cancelled = False
|
|
|
|
self.marshall_arguments()
|
2023-02-13 04:59:18 +00:00
|
|
|
|
2023-02-19 21:08:58 +00:00
|
|
|
def on_back(self):
|
|
|
|
self.parentApp.switchFormPrevious()
|
2023-02-15 06:07:39 +00:00
|
|
|
self.editing = False
|
|
|
|
|
2023-02-19 21:08:58 +00:00
|
|
|
def on_cancel(self):
|
|
|
|
if npyscreen.notify_yes_no(
|
2023-02-21 19:12:57 +00:00
|
|
|
"Are you sure you want to cancel?\nYou may re-run this script later using the invoke.sh or invoke.bat command.\n"
|
2023-02-19 21:08:58 +00:00
|
|
|
):
|
|
|
|
self.parentApp.setNextForm(None)
|
|
|
|
self.parentApp.user_cancelled = True
|
|
|
|
self.editing = False
|
|
|
|
|
2023-02-15 06:07:39 +00:00
|
|
|
def marshall_arguments(self):
|
2023-02-21 19:12:57 +00:00
|
|
|
"""
|
2023-02-15 06:07:39 +00:00
|
|
|
Assemble arguments and store as attributes of the application:
|
|
|
|
.starter_models: dict of model names to install from INITIAL_CONFIGURE.yaml
|
|
|
|
True => Install
|
|
|
|
False => Remove
|
|
|
|
.scan_directory: Path to a directory of models to scan and import
|
|
|
|
.autoscan_on_startup: True if invokeai should scan and import at startup time
|
|
|
|
.import_model_paths: list of URLs, repo_ids and file paths to import
|
2023-02-21 19:12:57 +00:00
|
|
|
"""
|
2023-02-19 18:12:05 +00:00
|
|
|
# we're using a global here rather than storing the result in the parentapp
|
|
|
|
# due to some bug in npyscreen that is causing attributes to be lost
|
|
|
|
selections = self.parentApp.user_selections
|
|
|
|
|
2023-02-15 06:07:39 +00:00
|
|
|
# starter models to install/remove
|
2023-03-03 06:02:00 +00:00
|
|
|
if hasattr(self, "models_selected"):
|
2023-02-24 05:53:48 +00:00
|
|
|
starter_models = dict(
|
|
|
|
map(
|
2023-03-03 06:02:00 +00:00
|
|
|
lambda x: (self.starter_model_list[x], True),
|
|
|
|
self.models_selected.value,
|
2023-02-24 05:53:48 +00:00
|
|
|
)
|
2023-02-21 19:12:57 +00:00
|
|
|
)
|
2023-02-24 05:53:48 +00:00
|
|
|
else:
|
|
|
|
starter_models = dict()
|
2023-05-30 04:38:37 +00:00
|
|
|
selections.purge_deleted_models = self.purge_deleted.value
|
|
|
|
|
|
|
|
selections.install_models = [x for x in starter_models if x not in self.existing_models]
|
|
|
|
selections.remove_models = [x for x in self.starter_model_list if x in self.existing_models and x not in starter_models]
|
|
|
|
|
2023-05-30 17:49:43 +00:00
|
|
|
selections.control_net_map = self.control_net_models
|
|
|
|
selections.install_cn_models = [self.cn_models_selected.values[x]
|
2023-05-30 04:38:37 +00:00
|
|
|
for x in self.cn_models_selected.value
|
|
|
|
if self.cn_models_selected.values[x] not in self.installed_cn_models
|
|
|
|
]
|
2023-05-30 17:49:43 +00:00
|
|
|
selections.remove_cn_models = [x
|
2023-05-30 04:38:37 +00:00
|
|
|
for x in self.cn_models_selected.values
|
|
|
|
if x in self.installed_cn_models
|
|
|
|
and self.cn_models_selected.values.index(x) not in self.cn_models_selected.value
|
|
|
|
]
|
2023-05-30 17:49:43 +00:00
|
|
|
if (additional_cns := self.additional_controlnet_ids.value.split()):
|
|
|
|
valid_cns = [x for x in additional_cns if '/' in x]
|
|
|
|
selections.install_cn_models.extend(valid_cns)
|
|
|
|
selections.control_net_map.update({x: x for x in valid_cns})
|
2023-02-15 06:07:39 +00:00
|
|
|
|
|
|
|
# load directory and whether to scan on startup
|
2023-02-16 05:34:15 +00:00
|
|
|
if self.show_directory_fields.value:
|
2023-02-19 18:12:05 +00:00
|
|
|
selections.scan_directory = self.autoload_directory.value
|
|
|
|
selections.autoscan_on_startup = self.autoscan_on_startup.value
|
2023-02-16 05:34:15 +00:00
|
|
|
else:
|
2023-02-19 18:12:05 +00:00
|
|
|
selections.scan_directory = None
|
|
|
|
selections.autoscan_on_startup = False
|
2023-02-15 06:07:39 +00:00
|
|
|
|
|
|
|
# URLs and the like
|
2023-02-19 18:12:05 +00:00
|
|
|
selections.import_model_paths = self.import_model_paths.value.split()
|
2023-02-21 19:12:57 +00:00
|
|
|
|
|
|
|
|
2023-02-14 05:02:19 +00:00
|
|
|
class AddModelApplication(npyscreen.NPSAppManaged):
|
2023-02-19 18:12:05 +00:00
|
|
|
def __init__(self):
|
2023-02-13 04:59:18 +00:00
|
|
|
super().__init__()
|
2023-02-19 18:12:05 +00:00
|
|
|
self.user_cancelled = False
|
|
|
|
self.user_selections = Namespace(
|
2023-05-30 04:38:37 +00:00
|
|
|
install_models=None,
|
|
|
|
remove_models=None,
|
2023-02-21 19:12:57 +00:00
|
|
|
purge_deleted_models=False,
|
2023-05-30 04:38:37 +00:00
|
|
|
install_cn_models = None,
|
|
|
|
remove_cn_models = None,
|
2023-05-30 17:49:43 +00:00
|
|
|
control_net_map = None,
|
2023-02-21 19:12:57 +00:00
|
|
|
scan_directory=None,
|
|
|
|
autoscan_on_startup=None,
|
|
|
|
import_model_paths=None,
|
2023-02-19 18:12:05 +00:00
|
|
|
)
|
2023-02-13 04:59:18 +00:00
|
|
|
|
|
|
|
def onStart(self):
|
|
|
|
npyscreen.setTheme(npyscreen.Themes.DefaultTheme)
|
2023-02-19 18:12:05 +00:00
|
|
|
self.main_form = self.addForm(
|
2023-02-21 19:12:57 +00:00
|
|
|
"MAIN", addModelsForm, name="Install Stable Diffusion Models"
|
2023-02-13 04:59:18 +00:00
|
|
|
)
|
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2023-02-16 05:34:15 +00:00
|
|
|
# --------------------------------------------------------
|
2023-02-19 18:12:05 +00:00
|
|
|
def process_and_execute(opt: Namespace, selections: Namespace):
|
2023-05-30 04:38:37 +00:00
|
|
|
models_to_install = selections.install_models
|
|
|
|
models_to_remove = selections.remove_models
|
2023-02-19 18:12:05 +00:00
|
|
|
directory_to_scan = selections.scan_directory
|
|
|
|
scan_at_startup = selections.autoscan_on_startup
|
|
|
|
potential_models_to_install = selections.import_model_paths
|
2023-05-30 17:49:43 +00:00
|
|
|
print(f'selections.install_cn_models={selections.install_cn_models}')
|
|
|
|
print(f'selections.remove_cn_models={selections.remove_cn_models}')
|
|
|
|
print(f'selections.cn_model_map={selections.control_net_map}')
|
|
|
|
install_requested_models(
|
|
|
|
install_initial_models=models_to_install,
|
|
|
|
remove_models=models_to_remove,
|
|
|
|
install_cn_models=selections.install_cn_models,
|
|
|
|
remove_cn_models=selections.remove_cn_models,
|
|
|
|
cn_model_map=selections.control_net_map,
|
|
|
|
scan_directory=Path(directory_to_scan) if directory_to_scan else None,
|
|
|
|
external_models=potential_models_to_install,
|
|
|
|
scan_at_startup=scan_at_startup,
|
|
|
|
precision="float32"
|
|
|
|
if opt.full_precision
|
|
|
|
else choose_precision(torch.device(choose_torch_device())),
|
|
|
|
purge_deleted=selections.purge_deleted_models,
|
|
|
|
config_file_path=Path(opt.config_file) if opt.config_file else None,
|
|
|
|
)
|
2023-02-21 19:12:57 +00:00
|
|
|
|
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
# --------------------------------------------------------
|
|
|
|
def select_and_download_models(opt: Namespace):
|
2023-02-21 19:12:57 +00:00
|
|
|
precision = (
|
|
|
|
"float32"
|
|
|
|
if opt.full_precision
|
|
|
|
else choose_precision(torch.device(choose_torch_device()))
|
|
|
|
)
|
2023-02-13 04:59:18 +00:00
|
|
|
if opt.default_only:
|
2023-02-17 02:37:50 +00:00
|
|
|
install_requested_models(
|
2023-02-21 19:12:57 +00:00
|
|
|
install_initial_models=default_dataset(),
|
|
|
|
precision=precision,
|
2023-02-19 21:08:58 +00:00
|
|
|
)
|
|
|
|
elif opt.yes_to_all:
|
|
|
|
install_requested_models(
|
2023-02-21 19:12:57 +00:00
|
|
|
install_initial_models=recommended_datasets(),
|
|
|
|
precision=precision,
|
2023-02-17 02:37:50 +00:00
|
|
|
)
|
2023-02-13 04:59:18 +00:00
|
|
|
else:
|
2023-02-23 00:18:07 +00:00
|
|
|
set_min_terminal_size(MIN_COLS, MIN_LINES)
|
2023-02-16 05:34:15 +00:00
|
|
|
installApp = AddModelApplication()
|
|
|
|
installApp.run()
|
2023-02-19 18:12:05 +00:00
|
|
|
|
|
|
|
if not installApp.user_cancelled:
|
|
|
|
process_and_execute(opt, installApp.user_selections)
|
2023-02-13 04:59:18 +00:00
|
|
|
|
2023-02-21 19:12:57 +00:00
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
# -------------------------------------
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser(description="InvokeAI model downloader")
|
|
|
|
parser.add_argument(
|
|
|
|
"--full-precision",
|
|
|
|
dest="full_precision",
|
|
|
|
action=argparse.BooleanOptionalAction,
|
|
|
|
type=bool,
|
|
|
|
default=False,
|
|
|
|
help="use 32-bit weights instead of faster 16-bit weights",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--yes",
|
|
|
|
"-y",
|
|
|
|
dest="yes_to_all",
|
|
|
|
action="store_true",
|
|
|
|
help='answer "yes" to all prompts',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--default_only",
|
|
|
|
action="store_true",
|
|
|
|
help="only install the default model",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--config_file",
|
|
|
|
"-c",
|
|
|
|
dest="config_file",
|
|
|
|
type=str,
|
|
|
|
default=None,
|
|
|
|
help="path to configuration file to create",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--root_dir",
|
|
|
|
dest="root",
|
|
|
|
type=str,
|
|
|
|
default=None,
|
|
|
|
help="path to root of install directory",
|
|
|
|
)
|
|
|
|
opt = parser.parse_args()
|
|
|
|
|
|
|
|
# setting a global here
|
2023-05-30 04:38:37 +00:00
|
|
|
if opt.root and Path(opt.root).exists():
|
|
|
|
config.root = Path(opt.root)
|
|
|
|
|
2023-05-22 21:51:47 +00:00
|
|
|
if not (config.root_dir / config.conf_path.parent).exists():
|
2023-04-29 13:43:40 +00:00
|
|
|
logger.info(
|
2023-04-19 00:49:00 +00:00
|
|
|
"Your InvokeAI root directory is not set up. Calling invokeai-configure."
|
2023-02-21 19:12:57 +00:00
|
|
|
)
|
2023-03-03 06:02:00 +00:00
|
|
|
from invokeai.frontend.install import invokeai_configure
|
2023-02-21 19:12:57 +00:00
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
invokeai_configure()
|
2023-02-21 16:47:41 +00:00
|
|
|
sys.exit(0)
|
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
try:
|
|
|
|
select_and_download_models(opt)
|
2023-02-14 21:32:54 +00:00
|
|
|
except AssertionError as e:
|
2023-04-29 13:43:40 +00:00
|
|
|
logger.error(e)
|
2023-02-14 21:32:54 +00:00
|
|
|
sys.exit(-1)
|
2023-02-13 04:59:18 +00:00
|
|
|
except KeyboardInterrupt:
|
2023-04-29 13:43:40 +00:00
|
|
|
logger.info("Goodbye! Come back soon.")
|
2023-02-23 05:43:25 +00:00
|
|
|
except widget.NotEnoughSpaceForWidget as e:
|
2023-02-14 21:32:54 +00:00
|
|
|
if str(e).startswith("Height of 1 allocated"):
|
2023-04-29 13:43:40 +00:00
|
|
|
logger.error(
|
2023-04-19 00:49:00 +00:00
|
|
|
"Insufficient vertical space for the interface. Please make your window taller and try again"
|
2023-02-14 21:32:54 +00:00
|
|
|
)
|
2023-02-21 19:12:57 +00:00
|
|
|
elif str(e).startswith("addwstr"):
|
2023-04-29 13:43:40 +00:00
|
|
|
logger.error(
|
2023-04-19 00:49:00 +00:00
|
|
|
"Insufficient horizontal space for the interface. Please make your window wider and try again."
|
2023-02-14 21:32:54 +00:00
|
|
|
)
|
2023-02-21 19:12:57 +00:00
|
|
|
|
2023-03-03 06:02:00 +00:00
|
|
|
|
2023-02-13 04:59:18 +00:00
|
|
|
# -------------------------------------
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|