diff --git a/kiauh/components/moonraker/moonraker_setup.py b/kiauh/components/moonraker/moonraker_setup.py index a5545e7..23bbba8 100644 --- a/kiauh/components/moonraker/moonraker_setup.py +++ b/kiauh/components/moonraker/moonraker_setup.py @@ -27,6 +27,9 @@ from components.moonraker import ( ) from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker_dialogs import print_moonraker_overview +from components.moonraker.services.moonraker_instance_service import ( + MoonrakerInstanceService, + ) from components.moonraker.utils.sysdeps_parser import SysDepsParser from components.moonraker.utils.utils import ( backup_moonraker_dir, @@ -39,8 +42,9 @@ from components.webui_client.client_utils import ( ) from components.webui_client.mainsail_data import MainsailData from core.instance_manager.instance_manager import InstanceManager -from core.logger import Logger +from core.logger import DialogType, Logger from core.settings.kiauh_settings import KiauhSettings +from core.types.color import Color from utils.common import check_install_dependencies from utils.fs_utils import check_file_exist from utils.git_utils import git_clone_wrapper, git_pull_wrapper @@ -54,6 +58,7 @@ from utils.sys_utils import ( cmd_sysctl_manage, cmd_sysctl_service, create_python_venv, + get_ipv4_addr, install_python_requirements, parse_packages_from_file, ) @@ -65,12 +70,18 @@ def install_moonraker() -> None: if not check_moonraker_install_requirements(klipper_list): return - moonraker_list: List[Moonraker] = get_instances(Moonraker) - instances: List[Moonraker] = [] + instance_service = MoonrakerInstanceService() + instance_service.load_instances() + + moonraker_list: List[Moonraker] = instance_service.get_all_instances() + new_instances: List[Moonraker] = [] selected_option: str | Klipper if len(klipper_list) == 1: - instances.append(Moonraker(klipper_list[0].suffix)) + suffix: str = klipper_list[0].suffix + new_inst = instance_service.create_new_instance(suffix) + new_instances.append(new_inst) + else: print_moonraker_overview( klipper_list, @@ -89,12 +100,15 @@ def install_moonraker() -> None: return if selected_option == "a": - instances.extend([Moonraker(k.suffix) for k in klipper_list]) + new_inst_list: List[Moonraker] = ( + [instance_service.create_new_instance(k.suffix) for k in klipper_list]) + new_instances.extend(new_inst_list) else: klipper_instance: Klipper | None = options.get(selected_option) if klipper_instance is None: raise Exception("Error selecting instance!") - instances.append(Moonraker(klipper_instance.suffix)) + new_inst = instance_service.create_new_instance(klipper_instance.suffix) + new_instances.append(new_inst) create_example_cfg = get_confirm("Create example moonraker.conf?") @@ -103,8 +117,8 @@ def install_moonraker() -> None: setup_moonraker_prerequesites() install_moonraker_polkit() - used_ports_map = {m.suffix: m.port for m in moonraker_list} - for instance in instances: + ports_map = instance_service.get_instance_port_map() + for instance in new_instances: instance.create() cmd_sysctl_service(instance.service_file_path.name, "enable") @@ -112,7 +126,7 @@ def install_moonraker() -> None: # if a webclient and/or it's config is installed, patch # its update section to the config clients = get_existing_clients() - create_example_moonraker_conf(instance, used_ports_map, clients) + create_example_moonraker_conf(instance, ports_map, clients) cmd_sysctl_service(instance.service_file_path.name, "start") @@ -123,6 +137,26 @@ def install_moonraker() -> None: if MainsailData().client_dir.exists() and len(moonraker_list) > 1: enable_mainsail_remotemode() + instance_service.load_instances() + new_instances = [instance_service.get_instance_by_suffix(i.suffix) for i in + new_instances] + + ip: str = get_ipv4_addr() + # noinspection HttpUrlsUsage + url_list = [f"● {i.service_file_path.stem}: http://{ip}:{i.port}" for i in + new_instances if i.port] + dialog_content = [] + if url_list: + dialog_content.append("You can access Moonraker via the following URL:") + dialog_content.extend(url_list) + + Logger.print_dialog( + DialogType.CUSTOM, + custom_title="Moonraker successfully installed!", + custom_color=Color.GREEN, + content=dialog_content) + + except Exception as e: Logger.print_error(f"Error while installing Moonraker: {e}") return diff --git a/kiauh/components/moonraker/services/__init__.py b/kiauh/components/moonraker/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kiauh/components/moonraker/services/moonraker_instance_service.py b/kiauh/components/moonraker/services/moonraker_instance_service.py new file mode 100644 index 0000000..a08457a --- /dev/null +++ b/kiauh/components/moonraker/services/moonraker_instance_service.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from typing import Dict, List + +from components.moonraker.moonraker import Moonraker +from utils.instance_utils import get_instances + + +class MoonrakerInstanceService: + __cls_instance = None + __instances: List[Moonraker] = [] + + def __new__(cls) -> "MoonrakerInstanceService": + if cls.__cls_instance is None: + cls.__cls_instance = super(MoonrakerInstanceService, cls).__new__(cls) + return cls.__cls_instance + + def __init__(self) -> None: + if not hasattr(self, "__initialized"): + self.__initialized = False + if self.__initialized: + return + self.__initialized = True + + def load_instances(self) -> None: + self.__instances = get_instances(Moonraker) + + def create_new_instance(self, suffix: str) -> Moonraker: + instance = Moonraker(suffix) + self.__instances.append(instance) + return instance + + def get_all_instances(self) -> List[Moonraker]: + return self.__instances + + def get_instance_by_suffix(self, suffix: str) -> Moonraker | None: + instances: List[Moonraker] = [i for i in self.__instances if i.suffix == suffix] + return instances[0] if instances else None + + def get_instance_port_map(self) -> Dict[str, int]: + return {i.suffix: i.port for i in self.__instances} diff --git a/kiauh/core/logger.py b/kiauh/core/logger.py index 023a5f2..67689c9 100644 --- a/kiauh/core/logger.py +++ b/kiauh/core/logger.py @@ -27,6 +27,12 @@ class DialogType(Enum): LINE_WIDTH = 53 +BORDER_TOP: str = "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓" +BORDER_BOTTOM: str = "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛" +BORDER_TITLE: str = "┠───────────────────────────────────────────────────────┨" +BORDER_LEFT: str = "┃" +BORDER_RIGHT: str = "┃" + class Logger: @staticmethod def print_info(msg, prefix=True, start="", end="\n") -> None: @@ -81,23 +87,27 @@ class Logger: :param margin_top: The number of empty lines to print before the dialog. :param margin_bottom: The number of empty lines to print after the dialog. """ - dialog_color = Logger._get_dialog_color(title, custom_color) + color = Logger._get_dialog_color(title, custom_color) dialog_title = Logger._get_dialog_title(title, custom_title) - dialog_title_formatted = Logger._format_dialog_title(dialog_title, dialog_color) - dialog_content = Logger.format_content( - content, - LINE_WIDTH, - dialog_color, - center_content, - ) - top = Logger._format_top_border(dialog_color) - bottom = Logger._format_bottom_border(dialog_color) print("\n" * margin_top) - print( - f"{top}{dialog_title_formatted}{dialog_content}{bottom}", - end="", - ) + + print(Color.apply(BORDER_TOP, color)) + + if dialog_title: + print(Color.apply(f"┃ {dialog_title:^{LINE_WIDTH}} ┃", color)) + print(Color.apply(BORDER_TITLE, color)) + + if content: + print(Logger.format_content( + content, + LINE_WIDTH, + color, + center_content, + )) + + print(Color.apply(BORDER_BOTTOM, color)) + print("\n" * margin_bottom) @staticmethod @@ -119,31 +129,6 @@ class Logger: return color - @staticmethod - def _format_top_border(color: Color) -> str: - _border = Color.apply( - "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", color - ) - return _border - - @staticmethod - def _format_bottom_border(color: Color) -> str: - _border = Color.apply( - "\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛", color - ) - return _border - - @staticmethod - def _format_dialog_title(title: str | None, color: Color) -> str: - if title is None: - return "" - - _title = Color.apply(f"┃ {title:^{LINE_WIDTH}} ┃\n", color) - _title += Color.apply( - "┠───────────────────────────────────────────────────────┨\n", color - ) - return _title - @staticmethod def format_content( content: List[str],