From 825f514349edcd3ecea2b50e5641218b8fe9202c Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 20:13:58 +0100 Subject: [PATCH 01/14] sort items alphabetically --- boiiiwd_package/src/library_tab.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index 639aa2d..47f5981 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -221,6 +221,18 @@ def filter_items(self, event): button_view_list.grid_remove() button.grid_remove() + def add_item_helper(self, text, image_path, workshop_id, folder, invalid_warn=True, item_type=None): + image = ctk.CTkImage(Image.open(image_path)) + self.add_item(text, image=image, workshop_id=workshop_id, folder=folder, invalid_warn=invalid_warn) + + # sort by type then alphabet (name), index 5 is type 0 is name/text + def sorting_key(self, item): + item_type, item_name = item[5], item[0] + if item_type == "map": + return (0, item_name) + else: + return (1, item_name) + def load_items(self, boiiiFolder, dont_add=False): if self.refresh_next_time and not dont_add: self.refresh_next_time = False @@ -244,6 +256,7 @@ def load_items(self, boiiiFolder, dont_add=False): total_size = 0 folders_to_process = [mods_folder, maps_folder] + ui_items_to_add = [] items_file = os.path.join(application_path, LIBRARY_FILE) if not self.is_valid_json_format(items_file): @@ -303,9 +316,9 @@ def load_items(self, boiiiFolder, dont_add=False): self.added_items.add(text_to_add) if image_path is b_mod_img or image_path is b_map_img and not dont_add: - self.add_item(text_to_add, image=ctk.CTkImage(Image.open(image_path)), workshop_id=workshop_id, folder=zone_path.parent, invalid_warn=True) + ui_items_to_add.append((text_to_add, image_path, workshop_id, zone_path.parent, True, item_type)) elif not dont_add: - self.add_item(text_to_add, image=ctk.CTkImage(Image.open(image_path)), workshop_id=workshop_id, folder=zone_path.parent) + ui_items_to_add.append((text_to_add, image_path, workshop_id, zone_path.parent, False, item_type)) id_found, folder_found = self.item_exists_in_file(items_file, workshop_id, curr_folder_name) item_info = { "id": workshop_id, @@ -338,6 +351,11 @@ def load_items(self, boiiiFolder, dont_add=False): if not workshop_id in self.ids_added and curr_folder_name not in self.item_block_list: self.ids_added.add(workshop_id) + # sort items by type then alphabet + ui_items_to_add.sort(key=self.sorting_key) + for item in ui_items_to_add: + self.add_item_helper(*item) + if not self.file_cleaned and os.path.exists(items_file): self.file_cleaned = True self.clean_json_file(items_file) From fdf43088528055d7c3895eeb10aeec7da975e0a6 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 21:10:37 +0100 Subject: [PATCH 02/14] add --- boiiiwd_package/src/library_tab.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index 47f5981..404984b 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -221,17 +221,14 @@ def filter_items(self, event): button_view_list.grid_remove() button.grid_remove() - def add_item_helper(self, text, image_path, workshop_id, folder, invalid_warn=True, item_type=None): + def add_item_helper(self, text, image_path, workshop_id, folder, invalid_warn=False, item_type=None): image = ctk.CTkImage(Image.open(image_path)) self.add_item(text, image=image, workshop_id=workshop_id, folder=folder, invalid_warn=invalid_warn) # sort by type then alphabet (name), index 5 is type 0 is name/text def sorting_key(self, item): - item_type, item_name = item[5], item[0] - if item_type == "map": - return (0, item_name) - else: - return (1, item_name) + item_type, item_name = item[5], item[0] + return (0, item_name) if item_type == "map" else (1, item_name) def load_items(self, boiiiFolder, dont_add=False): if self.refresh_next_time and not dont_add: From fea0d4f590258b1a68b14c9e3818d209df9c3880 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 22:06:11 +0100 Subject: [PATCH 03/14] toplevels imp, get rid of topmosts in steam to boiii and details windows --- boiiiwd_package/src/library_tab.py | 5 ++++- boiiiwd_package/src/main.py | 5 ++++- boiiiwd_package/src/settings_tab.py | 5 ++++- boiiiwd_package/src/update_window.py | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index 404984b..5dcc91e 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -614,7 +614,9 @@ def main_thread(): if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) top.title("Map/Mod Information") - top.attributes('-topmost', 'true') + _, _, x, y = get_window_size_from_registry() + top.geometry(f"+{x+50}+{y-50}") + # top.attributes('-topmost', 'true') size_text = "Size (Workshop):" if offline_date: @@ -792,6 +794,7 @@ def check_for_updates(): buttons_frame.grid_columnconfigure(2, weight=1) finally: + top.after(10, top.focus_force) for button_view in self.button_view_list: button_view.configure(state="normal") self.after(0, main_thread) diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index 65ffbf3..efa3c3e 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -657,7 +657,9 @@ def main_thread(): top = ctk.CTkToplevel(self) top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) top.title("Map/Mod Information") - top.attributes('-topmost', 'true') + _, _, x, y = get_window_size_from_registry() + top.geometry(f"+{x+50}+{y-50}") + # top.attributes('-topmost', 'true') def close_window(): top.destroy() @@ -772,6 +774,7 @@ def main_thread(): top.grid_rowconfigure(2, weight=1) top.grid_columnconfigure(0, weight=1) top.grid_columnconfigure(1, weight=1) + top.after(10, top.focus_force) self.after(0, main_thread) diff --git a/boiiiwd_package/src/settings_tab.py b/boiiiwd_package/src/settings_tab.py index bc69743..dd2030c 100644 --- a/boiiiwd_package/src/settings_tab.py +++ b/boiiiwd_package/src/settings_tab.py @@ -510,7 +510,9 @@ def main_thread(): if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) top.title("Steam to boiii -> Workshop items") - top.attributes('-topmost', 'true') + _, _, x, y = get_window_size_from_registry() + top.geometry(f"+{x}+{y}") + # top.attributes('-topmost', 'true') top.resizable(False, False) # Create input boxes center_frame = ctk.CTkFrame(top) @@ -716,6 +718,7 @@ def start_thread(): main_app.app.create_context_menu(boiii_folder_entry) copy_var.set(True) progress_bar.set(0) + main_app.app.after(100, top.focus_force) except Exception as e: show_message("Error", f"{e}", icon="cancel") diff --git a/boiiiwd_package/src/update_window.py b/boiiiwd_package/src/update_window.py index a70af6c..7ba52f4 100644 --- a/boiiiwd_package/src/update_window.py +++ b/boiiiwd_package/src/update_window.py @@ -46,7 +46,8 @@ class UpdateWindow(ctk.CTkToplevel): def __init__(self, master, update_url): super().__init__(master) self.title("BOIIIWD Self-Updater") - self.geometry("400x150") + _, _, x, y = get_window_size_from_registry() + self.geometry(f"400x150+{x}+{y}") if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): self.after(250, lambda: self.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) self.protocol("WM_DELETE_WINDOW", self.cancel_update) From 17928dcbf1a7282073f4c08eb3f4d626a625c955 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 22:06:40 +0100 Subject: [PATCH 04/14] toplevels imp, get rid of topmosts in steam to boiii and details windows --- boiiiwd_package/src/settings_tab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boiiiwd_package/src/settings_tab.py b/boiiiwd_package/src/settings_tab.py index dd2030c..85a1e1e 100644 --- a/boiiiwd_package/src/settings_tab.py +++ b/boiiiwd_package/src/settings_tab.py @@ -509,7 +509,7 @@ def main_thread(): top = ctk.CTkToplevel(self) if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) - top.title("Steam to boiii -> Workshop items") + top.title("Steam to boiii") _, _, x, y = get_window_size_from_registry() top.geometry(f"+{x}+{y}") # top.attributes('-topmost', 'true') From 8f732df053168d8d36674c5a67bea30dc8f2c421 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 22:22:08 +0100 Subject: [PATCH 05/14] steam2b fix --- boiiiwd_package/src/settings_tab.py | 410 ++++++++++++++-------------- 1 file changed, 202 insertions(+), 208 deletions(-) diff --git a/boiiiwd_package/src/settings_tab.py b/boiiiwd_package/src/settings_tab.py index 85a1e1e..4b32789 100644 --- a/boiiiwd_package/src/settings_tab.py +++ b/boiiiwd_package/src/settings_tab.py @@ -502,225 +502,219 @@ def settings_reset_steamcmd(self): reset_steamcmd() def from_steam_to_boiii_toplevel(self): - def main_thread(): - try: - # to make sure json file is up to date - main_app.app.library_tab.load_items(main_app.app.edit_destination_folder.get(), dont_add=True) - top = ctk.CTkToplevel(self) - if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): - top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) - top.title("Steam to boiii") - _, _, x, y = get_window_size_from_registry() - top.geometry(f"+{x}+{y}") - # top.attributes('-topmost', 'true') - top.resizable(False, False) - # Create input boxes - center_frame = ctk.CTkFrame(top) - center_frame.grid(row=0, column=0, padx=20, pady=20) - - # Create input boxes - steam_folder_label = ctk.CTkLabel(center_frame, text="Steam Folder:") - steam_folder_label.grid(row=0, column=0, padx=(20, 20), pady=(10, 0), sticky='w') - steam_folder_entry = ctk.CTkEntry(center_frame, width=225) - steam_folder_entry.grid(row=1, column=0, columnspan=2, padx=(0, 20), pady=(10, 10), sticky='nes') - button_steam_browse = ctk.CTkButton(center_frame, text="Select", width=10) - button_steam_browse.grid(row=1, column=2, padx=(0, 20), pady=(10, 10), sticky="wnes") - - boiii_folder_label = ctk.CTkLabel(center_frame, text="boiii Folder:") - boiii_folder_label.grid(row=2, column=0, padx=(20, 20), pady=(10, 0), sticky='w') - boiii_folder_entry = ctk.CTkEntry(center_frame, width=225) - boiii_folder_entry.grid(row=3, column=0, columnspan=2, padx=(0, 20), pady=(10, 10), sticky='nes') - button_BOIII_browse = ctk.CTkButton(center_frame, text="Select", width=10) - button_BOIII_browse.grid(row=3, column=2, padx=(0, 20), pady=(10, 10), sticky="wnes") - - # Create option to choose between cut or copy - operation_label = ctk.CTkLabel(center_frame, text="Choose operation:") - operation_label.grid(row=4, column=0, padx=(20, 20), pady=(10, 10), sticky='wnes') - copy_var = ctk.BooleanVar() - cut_var = ctk.BooleanVar() - copy_check = ctk.CTkCheckBox(center_frame, text="Copy", variable=copy_var) - cut_check = ctk.CTkCheckBox(center_frame, text="Cut", variable=cut_var) - copy_check.grid(row=4, column=1, padx=(0, 10), pady=(10, 10), sticky='wnes') - cut_check.grid(row=4, column=2, padx=(0, 10), pady=(10, 10), sticky='nes') - - # Create progress bar - progress_bar = ctk.CTkProgressBar(center_frame, mode="determinate", height=20, corner_radius=7) - progress_bar.grid(row=5, column=0, columnspan=3, padx=(20, 20), pady=(10, 10), sticky='wnes') - progress_text = ctk.CTkLabel(progress_bar, text="0%", font=("Helvetica", 12), fg_color="transparent", text_color="white", height=0, width=0, corner_radius=0) - progress_text.place(relx=0.5, rely=0.5, anchor="center") - - copy_button = ctk.CTkButton(center_frame, text="Start (Copy)") - copy_button.grid(row=6, column=0, columnspan=3,padx=(20, 20), pady=(10, 10), sticky='wnes') - - # funcs - # had to use this shit again cuz of threading issues with widgets - def copy_with_progress(src, dst): + try: + # to make sure json file is up to date + main_app.app.library_tab.load_items(main_app.app.edit_destination_folder.get(), dont_add=True) + top = ctk.CTkToplevel(self) + if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): + top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) + top.title("Steam to boiii") + _, _, x, y = get_window_size_from_registry() + top.geometry(f"+{x}+{y}") + # top.attributes('-topmost', 'true') + top.resizable(False, False) + # Create input boxes + center_frame = ctk.CTkFrame(top) + + # Create input boxes + steam_folder_label = ctk.CTkLabel(center_frame, text="Steam Folder:") + steam_folder_entry = ctk.CTkEntry(center_frame, width=225) + button_steam_browse = ctk.CTkButton(center_frame, text="Select", width=10) + boiii_folder_label = ctk.CTkLabel(center_frame, text="boiii Folder:") + boiii_folder_entry = ctk.CTkEntry(center_frame, width=225) + button_BOIII_browse = ctk.CTkButton(center_frame, text="Select", width=10) + # Create option to choose between cut or copy + operation_label = ctk.CTkLabel(center_frame, text="Choose operation:") + copy_var = ctk.BooleanVar() + cut_var = ctk.BooleanVar() + copy_check = ctk.CTkCheckBox(center_frame, text="Copy", variable=copy_var) + cut_check = ctk.CTkCheckBox(center_frame, text="Cut", variable=cut_var) + + # Create progress bar + progress_bar = ctk.CTkProgressBar(center_frame, mode="determinate", height=20, corner_radius=7) + progress_text = ctk.CTkLabel(progress_bar, text="0%", font=("Helvetica", 12), fg_color="transparent", text_color="white", height=0, width=0, corner_radius=0) + copy_button = ctk.CTkButton(center_frame, text="Start (Copy)") + + # funcs + # had to use this shit again cuz of threading issues with widgets + def copy_with_progress(src, dst): + try: + total_files = sum([len(files) for root, dirs, files in os.walk(src)]) + progress = 0 + + def copy_progress(src, dst): + nonlocal progress + shutil.copy2(src, dst) + progress += 1 + top.after(0, progress_text.configure(text=f"Copying files: {progress}/{total_files}")) + value = (progress / total_files) * 100 + valuep = value / 100 + progress_bar.set(valuep) + try: - total_files = sum([len(files) for root, dirs, files in os.walk(src)]) - progress = 0 - - def copy_progress(src, dst): - nonlocal progress - shutil.copy2(src, dst) - progress += 1 - top.after(0, progress_text.configure(text=f"Copying files: {progress}/{total_files}")) - value = (progress / total_files) * 100 - valuep = value / 100 - progress_bar.set(valuep) - - try: - shutil.copytree(src, dst, dirs_exist_ok=True, copy_function=copy_progress) - except Exception as E: - show_message("Error", f"Error copying files: {E}", icon="cancel") - finally: - top.after(0, progress_text.configure(text="0%")) - top.after(0, progress_bar.set(0.0)) - - def check_status(var, op_var): - if var.get(): - op_var.set(False) - if cut_var.get(): - copy_button.configure(text=f"Start (Cut)") - if copy_var.get(): - copy_button.configure(text=f"Start (Copy)") - - def open_BOIII_browser(): - selected_folder = ctk.filedialog.askdirectory(title="Select boiii Folder") - if selected_folder: - boiii_folder_entry.delete(0, "end") - boiii_folder_entry.insert(0, selected_folder) - - def open_steam_browser(): - selected_folder = ctk.filedialog.askdirectory(title="Select Steam Folder (ex: C:\Program Files (x86)\Steam)") - if selected_folder: - steam_folder_entry.delete(0, "end") - steam_folder_entry.insert(0, selected_folder) - save_config("steam_folder" ,steam_folder_entry.get()) - - def start_copy_operation(): - def start_thread(): - try: - if not cut_var.get() and not copy_var.get(): - show_message("Choose operation!", "Please choose an operation, Copy or Cut files from steam!") - return - - copy_button.configure(state="disabled") - steam_folder = steam_folder_entry.get() - ws_folder = os.path.join(steam_folder, "steamapps/workshop/content/311210") - boiii_folder = boiii_folder_entry.get() - - if not os.path.exists(steam_folder) and not os.path.exists(ws_folder): - show_message("Not found", "Either you have no items downloaded from Steam or wrong path, please recheck path (ex: C:\Program Files (x86)\Steam)") - return - - if not os.path.exists(boiii_folder): - show_message("Not found", "boiii folder not found, please recheck path") - return - - top.after(0, progress_text.configure(text="Loading...")) - - map_folder = os.path.join(ws_folder) - - subfolders = [f for f in os.listdir(map_folder) if os.path.isdir(os.path.join(map_folder, f))] - total_folders = len(subfolders) - - if not subfolders: - show_message("No items found", f"No items found in \n{map_folder}") - return - - for i, dir_name in enumerate(subfolders, start=1): - json_file_path = os.path.join(map_folder, dir_name, "workshop.json") - copy_button.configure(text=f"Working on -> {i}/{total_folders}") - - if os.path.exists(json_file_path): - workshop_id = extract_json_data(json_file_path, "PublisherID") - mod_type = extract_json_data(json_file_path, "Type") - items_file = os.path.join(application_path, LIBRARY_FILE) - item_exists,_ = main_app.app.library_tab.item_exists_in_file(items_file, workshop_id) - - if item_exists: - get_folder_name = main_app.app.library_tab.get_item_by_id(items_file, workshop_id, return_option="folder_name") - if get_folder_name: - folder_name = get_folder_name - else: - try: - folder_name = extract_json_data(json_file_path, main_app.app.settings_tab.folder_options.get()) - except: - folder_name = extract_json_data(json_file_path, "publisherID") + shutil.copytree(src, dst, dirs_exist_ok=True, copy_function=copy_progress) + except Exception as E: + show_message("Error", f"Error copying files: {E}", icon="cancel") + finally: + top.after(0, progress_text.configure(text="0%")) + top.after(0, progress_bar.set(0.0)) + + def check_status(var, op_var): + if var.get(): + op_var.set(False) + if cut_var.get(): + copy_button.configure(text=f"Start (Cut)") + if copy_var.get(): + copy_button.configure(text=f"Start (Copy)") + + def open_BOIII_browser(): + selected_folder = ctk.filedialog.askdirectory(title="Select boiii Folder") + if selected_folder: + boiii_folder_entry.delete(0, "end") + boiii_folder_entry.insert(0, selected_folder) + + def open_steam_browser(): + selected_folder = ctk.filedialog.askdirectory(title="Select Steam Folder (ex: C:\Program Files (x86)\Steam)") + if selected_folder: + steam_folder_entry.delete(0, "end") + steam_folder_entry.insert(0, selected_folder) + save_config("steam_folder" ,steam_folder_entry.get()) + + def start_copy_operation(): + def start_thread(): + try: + if not cut_var.get() and not copy_var.get(): + show_message("Choose operation!", "Please choose an operation, Copy or Cut files from steam!") + return + + copy_button.configure(state="disabled") + steam_folder = steam_folder_entry.get() + ws_folder = os.path.join(steam_folder, "steamapps/workshop/content/311210") + boiii_folder = boiii_folder_entry.get() + + if not os.path.exists(steam_folder) and not os.path.exists(ws_folder): + show_message("Not found", "Either you have no items downloaded from Steam or wrong path, please recheck path (ex: C:\Program Files (x86)\Steam)") + return + + if not os.path.exists(boiii_folder): + show_message("Not found", "boiii folder not found, please recheck path") + return + + top.after(0, progress_text.configure(text="Loading...")) + + map_folder = os.path.join(ws_folder) + + subfolders = [f for f in os.listdir(map_folder) if os.path.isdir(os.path.join(map_folder, f))] + total_folders = len(subfolders) + + if not subfolders: + show_message("No items found", f"No items found in \n{map_folder}") + return + + for i, dir_name in enumerate(subfolders, start=1): + json_file_path = os.path.join(map_folder, dir_name, "workshop.json") + copy_button.configure(text=f"Working on -> {i}/{total_folders}") + + if os.path.exists(json_file_path): + workshop_id = extract_json_data(json_file_path, "PublisherID") + mod_type = extract_json_data(json_file_path, "Type") + items_file = os.path.join(application_path, LIBRARY_FILE) + item_exists,_ = main_app.app.library_tab.item_exists_in_file(items_file, workshop_id) + + if item_exists: + get_folder_name = main_app.app.library_tab.get_item_by_id(items_file, workshop_id, return_option="folder_name") + if get_folder_name: + folder_name = get_folder_name else: try: folder_name = extract_json_data(json_file_path, main_app.app.settings_tab.folder_options.get()) except: folder_name = extract_json_data(json_file_path, "publisherID") + else: + try: + folder_name = extract_json_data(json_file_path, main_app.app.settings_tab.folder_options.get()) + except: + folder_name = extract_json_data(json_file_path, "publisherID") + + if mod_type == "mod": + path_folder = os.path.join(boiii_folder, "mods") + folder_name_path = os.path.join(path_folder, folder_name, "zone") + elif mod_type == "map": + path_folder = os.path.join(boiii_folder, "usermaps") + folder_name_path = os.path.join(path_folder, folder_name, "zone") + else: + show_message("Error", "Invalid workshop type in workshop.json, are you sure this is a map or a mod?.", icon="cancel") + continue - if mod_type == "mod": - path_folder = os.path.join(boiii_folder, "mods") + if not item_exists: + while os.path.exists(os.path.join(path_folder, folder_name)): + folder_name += f"_{workshop_id}" folder_name_path = os.path.join(path_folder, folder_name, "zone") - elif mod_type == "map": - path_folder = os.path.join(boiii_folder, "usermaps") - folder_name_path = os.path.join(path_folder, folder_name, "zone") - else: - show_message("Error", "Invalid workshop type in workshop.json, are you sure this is a map or a mod?.", icon="cancel") - continue - if not item_exists: - while os.path.exists(os.path.join(path_folder, folder_name)): - folder_name += f"_{workshop_id}" - folder_name_path = os.path.join(path_folder, folder_name, "zone") + os.makedirs(folder_name_path, exist_ok=True) - os.makedirs(folder_name_path, exist_ok=True) + try: + copy_with_progress(os.path.join(map_folder, dir_name), folder_name_path) + except Exception as E: + show_message("Error", f"Error copying files: {E}", icon="cancel") + continue - try: - copy_with_progress(os.path.join(map_folder, dir_name), folder_name_path) - except Exception as E: - show_message("Error", f"Error copying files: {E}", icon="cancel") - continue + if cut_var.get(): + remove_tree(os.path.join(map_folder, dir_name)) - if cut_var.get(): - remove_tree(os.path.join(map_folder, dir_name)) + main_app.app.library_tab.update_item(main_app.app.edit_destination_folder.get(), workshop_id, mod_type, folder_name) + else: + # if its last folder to check + if i == total_folders: + show_message("Error", f"workshop.json not found in {dir_name}", icon="cancel") + main_app.app.library_tab.load_items(main_app.app.edit_destination_folder.get(), dont_add=True) + return + continue - main_app.app.library_tab.update_item(main_app.app.edit_destination_folder.get(), workshop_id, mod_type, folder_name) - else: - # if its last folder to check - if i == total_folders: - show_message("Error", f"workshop.json not found in {dir_name}", icon="cancel") - main_app.app.library_tab.load_items(main_app.app.edit_destination_folder.get(), dont_add=True) - return - continue + if subfolders: + main_app.app.library_tab.load_items(main_app.app.edit_destination_folder.get(), dont_add=True) + main_app.app.show_complete_message(message=f"All items were moved\nYou can run the game now!\nPS: You have to restart the game\n(pressing launch will launch/restarts)") + + finally: + if cut_var.get(): + copy_button.configure(text=f"Start (Cut)") + if copy_var.get(): + copy_button.configure(text=f"Start (Copy)") + copy_button.configure(state="normal") + top.after(0, progress_bar.set(0)) + top.after(0, progress_text.configure(text="0%")) - if subfolders: - main_app.app.library_tab.load_items(main_app.app.edit_destination_folder.get(), dont_add=True) - main_app.app.show_complete_message(message=f"All items were moved\nYou can run the game now!\nPS: You have to restart the game\n(pressing launch will launch/restarts)") - - finally: - if cut_var.get(): - copy_button.configure(text=f"Start (Cut)") - if copy_var.get(): - copy_button.configure(text=f"Start (Copy)") - copy_button.configure(state="normal") - top.after(0, progress_bar.set(0)) - top.after(0, progress_text.configure(text="0%")) - - # prevents app hanging - threading.Thread(target=start_thread).start() - - # config - progress_color = get_button_state_colors(check_custom_theme(check_config("theme", fallback="boiiiwd_theme.json")), "progress_bar_fill_color") - progress_bar.configure(progress_color=progress_color) - steam_folder_entry.insert(1, check_config("steam_folder", "")) - boiii_folder_entry.insert(1, main_app.app.edit_destination_folder.get()) - button_BOIII_browse.configure(command=open_BOIII_browser) - button_steam_browse.configure(command=open_steam_browser) - copy_button.configure(command=start_copy_operation) - cut_check.configure(command = lambda: check_status(cut_var, copy_var)) - copy_check.configure(command = lambda: check_status(copy_var, cut_var)) - main_app.app.create_context_menu(steam_folder_entry) - main_app.app.create_context_menu(boiii_folder_entry) - copy_var.set(True) - progress_bar.set(0) - main_app.app.after(100, top.focus_force) - - except Exception as e: - show_message("Error", f"{e}", icon="cancel") - - main_app.app.after(0, main_thread) + # prevents app hanging + threading.Thread(target=start_thread).start() + + # config + center_frame.grid(row=0, column=0, padx=20, pady=20) + button_steam_browse.grid(row=1, column=2, padx=(0, 20), pady=(10, 10), sticky="wnes") + steam_folder_label.grid(row=0, column=0, padx=(20, 20), pady=(10, 0), sticky='w') + steam_folder_entry.grid(row=1, column=0, columnspan=2, padx=(0, 20), pady=(10, 10), sticky='nes') + boiii_folder_label.grid(row=2, column=0, padx=(20, 20), pady=(10, 0), sticky='w') + boiii_folder_entry.grid(row=3, column=0, columnspan=2, padx=(0, 20), pady=(10, 10), sticky='nes') + button_BOIII_browse.grid(row=3, column=2, padx=(0, 20), pady=(10, 10), sticky="wnes") + operation_label.grid(row=4, column=0, padx=(20, 20), pady=(10, 10), sticky='wnes') + copy_check.grid(row=4, column=1, padx=(0, 10), pady=(10, 10), sticky='wnes') + cut_check.grid(row=4, column=2, padx=(0, 10), pady=(10, 10), sticky='nes') + progress_bar.grid(row=5, column=0, columnspan=3, padx=(20, 20), pady=(10, 10), sticky='wnes') + progress_text.place(relx=0.5, rely=0.5, anchor="center") + copy_button.grid(row=6, column=0, columnspan=3,padx=(20, 20), pady=(10, 10), sticky='wnes') + progress_color = get_button_state_colors(check_custom_theme(check_config("theme", fallback="boiiiwd_theme.json")), "progress_bar_fill_color") + progress_bar.configure(progress_color=progress_color) + steam_folder_entry.insert(1, check_config("steam_folder", "")) + boiii_folder_entry.insert(1, main_app.app.edit_destination_folder.get()) + button_BOIII_browse.configure(command=open_BOIII_browser) + button_steam_browse.configure(command=open_steam_browser) + copy_button.configure(command=start_copy_operation) + cut_check.configure(command = lambda: check_status(cut_var, copy_var)) + copy_check.configure(command = lambda: check_status(copy_var, cut_var)) + main_app.app.create_context_menu(steam_folder_entry) + main_app.app.create_context_menu(boiii_folder_entry) + copy_var.set(True) + progress_bar.set(0) + top.after(120, top.focus_force) + + except Exception as e: + show_message("Error", f"{e}", icon="cancel") From 74f3b4692665f34d4e1e66d3dabe5fde6745f4ac Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 22:34:45 +0100 Subject: [PATCH 06/14] size always from folder in library --- boiiiwd_package/src/library_tab.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index 5dcc91e..ab3f3a2 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -488,6 +488,11 @@ def show_map_thread(): for button_view in self.button_view_list: button_view.configure(state="normal") # return + + json_path = Path(folder) / "zone" / "workshop.json" + folder_size_bytes = get_folder_size(json_path.parent.parent) + map_size = convert_bytes_to_readable(folder_size_bytes) + if online and valid_id!=False: try: url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={workshop_id}" @@ -501,7 +506,6 @@ def show_map_thread(): type_txt = soup.find("div", class_="rightDetailsBlock").text.strip() map_mod_type = type_txt if "File Size" not in type_txt else "Not specified" map_name = soup.find("div", class_="workshopItemTitle").text.strip() - map_size = map_size = get_workshop_file_size(workshop_id, raw=True) details_stats_container = soup.find("div", class_="detailsStatsContainerRight") details_stat_elements = details_stats_container.find_all("div", class_="detailsStatRight") date_created = details_stat_elements[1].text.strip() @@ -560,7 +564,6 @@ def show_map_thread(): button_view.configure(state="normal") return else: - json_path = Path(folder) / "zone" / "workshop.json" creation_timestamp = None for ff_file in json_path.parent.glob("*.ff"): if ff_file.exists(): @@ -574,8 +577,6 @@ def show_map_thread(): name = re.sub(r'\^\w+', '', extract_json_data(json_path, "Title")) or "None" map_name = name[:45] + "..." if len(name) > 45 else name map_mod_type = extract_json_data(json_path, "Type") or "None" - folder_size_bytes = get_folder_size(json_path.parent.parent) - map_size = convert_bytes_to_readable(folder_size_bytes) preview_iamge = json_path.parent / "previewimage.png" if preview_iamge.exists(): image = Image.open(preview_iamge) @@ -617,11 +618,9 @@ def main_thread(): _, _, x, y = get_window_size_from_registry() top.geometry(f"+{x+50}+{y-50}") # top.attributes('-topmost', 'true') - size_text = "Size (Workshop):" if offline_date: down_date = offline_date - size_text = "Size (On Disk):" else: down_date = self.get_item_by_id(items_file, workshop_id, 'date') @@ -715,7 +714,7 @@ def check_for_updates(): type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}") type_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=5) - size_label = ctk.CTkLabel(info_frame, text=f"{size_text} {map_size}") + size_label = ctk.CTkLabel(info_frame, text=f"Size: {map_size}") size_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=5) date_created_label = ctk.CTkLabel(info_frame, text=f"Posted: {date_created}") From 5d6f7ab03ff27364d64ab587a43c5829b623a18a Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 23:00:20 +0100 Subject: [PATCH 07/14] set image min width --- boiiiwd_package/src/library_tab.py | 10 ++++------ boiiiwd_package/src/main.py | 5 +++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index ab3f3a2..c5250d0 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -744,13 +744,11 @@ def check_for_updates(): ratings.pack(side="right", padx=(10, 20), pady=(10, 10)) image_label = ctk.CTkLabel(image_frame) - width, height = image_size + max_width = 300 + i_width, i_height = tuple([int(max_width/image_size[0] * x) for x in image_size]) # preview image is too big if offline, // to round floats - if width > 1000 or height > 1000: - width = width // 2 - height = height // 2 - image_widget = ctk.CTkImage(image, size=(int(width), int(height))) + image_widget = ctk.CTkImage(image, size=(int(i_width), int(i_height))) image_label.configure(image=image_widget, text="") image_label.pack(expand=True, fill="both", padx=(10, 20), pady=(10, 10)) @@ -796,7 +794,7 @@ def check_for_updates(): top.after(10, top.focus_force) for button_view in self.button_view_list: button_view.configure(state="normal") - self.after(0, main_thread) + main_app.app.after(0, main_thread) @if_internet_available def check_for_updates(self, on_launch=False): diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index efa3c3e..5c9c43b 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -757,8 +757,9 @@ def main_thread(): ratings.pack(side="right", padx=(10, 20), pady=(10, 10)) image_label = ctk.CTkLabel(image_frame) - width, height = image_size - image_widget = ctk.CTkImage(image, size=(int(width), int(height))) + max_width = 300 + i_width, i_height = tuple([int(max_width/image_size[0] * x) for x in image_size]) + image_widget = ctk.CTkImage(image, size=(int(i_width), int(i_height))) image_label.configure(image=image_widget, text="") image_label.pack(expand=True, fill="both", padx=(10, 20), pady=(10, 10)) From fd0a9c519ae25652d42cfb418eb93515545a5436 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Mon, 25 Sep 2023 23:58:38 +0100 Subject: [PATCH 08/14] changed default image, added limits for name and type in details --- .../resources/default_library_img.png | Bin 0 -> 17114 bytes boiiiwd_package/src/helpers.py | 8 +++--- boiiiwd_package/src/library_tab.py | 26 ++++++++++++++---- boiiiwd_package/src/main.py | 24 ++++++++++++---- 4 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 boiiiwd_package/resources/default_library_img.png diff --git a/boiiiwd_package/resources/default_library_img.png b/boiiiwd_package/resources/default_library_img.png new file mode 100644 index 0000000000000000000000000000000000000000..a248561f89dddc71540dfcc8db6f2abcaef6961d GIT binary patch literal 17114 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 na|2i7|M!{%>Wp`WP6&uUF#%+-P;KDLDj=l9 desc_threshold else shortened_description @@ -711,8 +720,13 @@ def check_for_updates(): id_label = ctk.CTkLabel(info_frame, text=f"ID: {workshop_id} | Folder: {os.path.basename(folder)}") id_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=5) - type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}") + map_mod_type = map_mod_type_txt[:desc_threshold] + "..." if len(map_mod_type_txt) > desc_threshold else map_mod_type_txt + type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}", wraplength=350, justify="left") type_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=5) + if len(map_mod_type) > desc_threshold: + type_label_tooltip = CTkToolTip(type_label, message="View all types", topmost=True) + type_label.configure(cursor="hand2") + type_label.bind("", lambda e: show_full_text(e, type_label, map_mod_type_txt)) size_label = ctk.CTkLabel(info_frame, text=f"Size: {map_size}") size_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=5) @@ -730,7 +744,7 @@ def check_for_updates(): date_updated_label = ctk.CTkLabel(info_frame, text=f"Updated: {date_updated}") date_updated_label.grid(row=6, column=0, columnspan=2, sticky="w", padx=20, pady=5) - date_updated_label = ctk.CTkLabel(info_frame, text=f"Downloaded at: {down_date}") + date_updated_label = ctk.CTkLabel(info_frame, text=f"Downloaded: {down_date}") date_updated_label.grid(row=7, column=0, columnspan=2, sticky="w", padx=20, pady=5) stars_image_label = ctk.CTkLabel(stars_frame) diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index 5c9c43b..879c4a6 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -650,7 +650,7 @@ def show_map_thread(): info_thread = threading.Thread(target=show_map_thread) info_thread.start() - def toplevel_info_window(self, map_name, map_mod_type, map_size, image, image_size, + def toplevel_info_window(self, map_name, map_mod_type_txt, map_size, image, image_size, date_created ,date_updated, stars_image, stars_image_size, ratings_text, url, workshop_id, description): def main_thread(): @@ -695,6 +695,15 @@ def main_thread(): self.after(0, main_thread) + def show_full_text(event, widget, full_text): + widget_text = type_label.cget("text") + label_type = widget_text.split(':')[0] + # + 30 which is desc_threshold + 5 is the ... dots and a white space + if len(widget_text) == len(label_type) + 30 + 5: + widget.configure(text=f"{label_type}: {full_text}") + else: + widget.configure(text=f"{label_type}: {full_text[:30]}...") + # frames stars_frame = ctk.CTkFrame(top) stars_frame.grid(row=0, column=0, columnspan=2, padx=20, pady=(20, 0), sticky="nsew") @@ -711,11 +720,11 @@ def main_thread(): buttons_frame.grid(row=3, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="nsew") # fillers - name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}") + name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=350, justify="left") name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=5) desc_threshold = 30 - shortened_description = re.sub(r'\n', '', description).strip() + shortened_description = re.sub(r'[\\/\n\r]', '', description).strip() shortened_description = re.sub(r'([^a-zA-Z0-9\s:().])', '', shortened_description) shortened_description = f"{shortened_description[:desc_threshold]}... (View)"\ if len(shortened_description) > desc_threshold else shortened_description @@ -726,10 +735,15 @@ def main_thread(): description_lab.configure(cursor="hand2") description_lab.bind("", lambda e: show_description(e)) - type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}") + map_mod_type = map_mod_type_txt[:desc_threshold] + "..." if len(map_mod_type_txt) > desc_threshold else map_mod_type_txt + type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}", wraplength=350, justify="left") type_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=5) + if len(map_mod_type) > desc_threshold: + type_label_tooltip = CTkToolTip(type_label, message="View all types", topmost=True) + type_label.configure(cursor="hand2") + type_label.bind("", lambda e: show_full_text(e, type_label, map_mod_type_txt)) - size_label = ctk.CTkLabel(info_frame, text=f"Size: {map_size}") + size_label = ctk.CTkLabel(info_frame, text=f"Size (Workshop): {map_size}") size_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=5) date_created_label = ctk.CTkLabel(info_frame, text=f"Posted: {date_created}") From bb7323d68914a2e298745a1f0895ec97b8d78b9d Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Tue, 26 Sep 2023 00:06:33 +0100 Subject: [PATCH 09/14] changed default image, added limits for name and type in details --- boiiiwd_package/src/helpers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boiiiwd_package/src/helpers.py b/boiiiwd_package/src/helpers.py index cfd0c40..faa2cb7 100644 --- a/boiiiwd_package/src/helpers.py +++ b/boiiiwd_package/src/helpers.py @@ -4,10 +4,10 @@ # Start helper functions #testing app offline -import socket -def guard(*args, **kwargs): - pass -socket.socket = guard +# import socket +# def guard(*args, **kwargs): +# pass +# socket.socket = guard def check_config(name, fallback=None): config = configparser.ConfigParser() From 9fc28a9b85b5a7d73afd5d75e361e8047bbfa826 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Tue, 26 Sep 2023 00:34:26 +0100 Subject: [PATCH 10/14] added minsize maxsize for details --- boiiiwd_package/src/library_tab.py | 4 +++- boiiiwd_package/src/main.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index a73ba54..7949db7 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -617,6 +617,8 @@ def main_thread(): top.title("Map/Mod Information") _, _, x, y = get_window_size_from_registry() top.geometry(f"+{x+50}+{y-50}") + top.maxsize(450, 10000) + top.minsize(300, 500) # top.attributes('-topmost', 'true') if offline_date: @@ -702,7 +704,7 @@ def show_full_text(event, widget, full_text): buttons_frame.grid(row=3, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="nsew") # fillers - name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=350, justify="left") + name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=420, justify="left") name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=5) desc_threshold = 30 diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index 879c4a6..c3c768b 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -659,6 +659,8 @@ def main_thread(): top.title("Map/Mod Information") _, _, x, y = get_window_size_from_registry() top.geometry(f"+{x+50}+{y-50}") + top.maxsize(450, 10000) + top.minsize(300, 500) # top.attributes('-topmost', 'true') def close_window(): @@ -720,7 +722,7 @@ def show_full_text(event, widget, full_text): buttons_frame.grid(row=3, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="nsew") # fillers - name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=350, justify="left") + name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=420, justify="left") name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=5) desc_threshold = 30 From 0f67df2de6faf68c7a31649a209efacfd218f9cc Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Tue, 26 Sep 2023 04:00:41 +0100 Subject: [PATCH 11/14] queue fixes --- boiiiwd_package/src/library_tab.py | 53 +++++++++++++++++++----------- boiiiwd_package/src/main.py | 31 +++++++++++------ 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index 7949db7..8131ffa 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -25,6 +25,7 @@ def __init__(self, master, **kwargs): self.update_button = ctk.CTkButton(self, image=ctk.CTkImage(Image.open(update_button_image)), command=self.check_for_updates, width=65, height=20, text="", fg_color="transparent") self.update_button.grid(row=0, column=1, padx=(0, 20), pady=(10, 20), sticky="en") + self.update_button.configure(state="disabled") self.update_tooltip = CTkToolTip(self.update_button, message="Check items for updates", topmost=True) filter_tooltip = CTkToolTip(self.filter_refresh_button, message="Refresh library", topmost=True) self.label_list = [] @@ -362,8 +363,13 @@ def load_items(self, boiiiFolder, dont_add=False): else: self.hide_no_items_message() + if all(item in self.item_block_list for item in self.added_folders): + self.show_no_items_message(only_up=True) + if map_count > 0 or mod_count > 0: return f"Maps: {map_count} - Mods: {mod_count} - Total size: {convert_bytes_to_readable(total_size)}" + + self.show_no_items_message(only_up=True) return "No items in current selected folder" def update_item(self, boiiiFolder, id, item_type, foldername): @@ -459,11 +465,16 @@ def view_item(self, workshop_id): url = f"https://steamcommunity.com/sharedfiles/filedetails/?id={workshop_id}" webbrowser.open(url) - def show_no_items_message(self): + def show_no_items_message(self, only_up=False): + self.update_button.configure(state="disabled") + self.update_tooltip.configure(message="Updater Disabled, No items found") + if only_up: + return self.no_items_label.grid(row=1, column=0, padx=10, pady=(0, 10), sticky="n") self.no_items_label.configure(text="No items found in the selected folder. \nMake sure you have a mod/map downloaded and or have the right boiii folder selected.") def hide_no_items_message(self): + self.update_button.configure(state="normal") self.no_items_label.configure(text="") self.no_items_label.forget() @@ -705,7 +716,7 @@ def show_full_text(event, widget, full_text): # fillers name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=420, justify="left") - name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=5) + name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) desc_threshold = 30 shortened_description = re.sub(r'[\\/\n\r]', '', description).strip() @@ -713,28 +724,28 @@ def show_full_text(event, widget, full_text): shortened_description = f"{shortened_description[:desc_threshold]}... (View)"\ if len(shortened_description) > desc_threshold else shortened_description description_lab = ctk.CTkLabel(info_frame, text=f"Description: {shortened_description}") - description_lab.grid(row=1, column=0, columnspan=2, sticky="w", padx=20, pady=5) + description_lab.grid(row=1, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) if len(description) > desc_threshold: description_lab_tooltip = CTkToolTip(description_lab, message="View description", topmost=True) description_lab.configure(cursor="hand2") description_lab.bind("", lambda e: show_description(e)) id_label = ctk.CTkLabel(info_frame, text=f"ID: {workshop_id} | Folder: {os.path.basename(folder)}") - id_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=5) + id_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) map_mod_type = map_mod_type_txt[:desc_threshold] + "..." if len(map_mod_type_txt) > desc_threshold else map_mod_type_txt type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}", wraplength=350, justify="left") - type_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=5) + type_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) if len(map_mod_type) > desc_threshold: type_label_tooltip = CTkToolTip(type_label, message="View all types", topmost=True) type_label.configure(cursor="hand2") type_label.bind("", lambda e: show_full_text(e, type_label, map_mod_type_txt)) size_label = ctk.CTkLabel(info_frame, text=f"Size: {map_size}") - size_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=5) + size_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) date_created_label = ctk.CTkLabel(info_frame, text=f"Posted: {date_created}") - date_created_label.grid(row=5, column=0, columnspan=2, sticky="w", padx=20, pady=5) + date_created_label.grid(row=5, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) if date_updated != "Not updated" and date_updated != "Offline": date_updated_label = ctk.CTkLabel(info_frame, text=f"Updated: {date_updated} 🔗") @@ -744,10 +755,10 @@ def show_full_text(event, widget, full_text): webbrowser.open(f"https://steamcommunity.com/sharedfiles/filedetails/changelog/{workshop_id}")) else: date_updated_label = ctk.CTkLabel(info_frame, text=f"Updated: {date_updated}") - date_updated_label.grid(row=6, column=0, columnspan=2, sticky="w", padx=20, pady=5) + date_updated_label.grid(row=6, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) date_updated_label = ctk.CTkLabel(info_frame, text=f"Downloaded: {down_date}") - date_updated_label.grid(row=7, column=0, columnspan=2, sticky="w", padx=20, pady=5) + date_updated_label.grid(row=7, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) stars_image_label = ctk.CTkLabel(stars_frame) stars_width, stars_height = stars_image_size @@ -866,19 +877,23 @@ def if_id_needs_update(item_id, item_date, text): return def check_for_update(): - lib_data = None + try: + lib_data = None - if not os.path.exists(os.path.join(application_path, LIBRARY_FILE)): - show_message("Error checking for item updates! -> Setting is on", "Please visit library tab at least once with the correct boiii path!, you also need to have at lease 1 item!") - return + if not os.path.exists(os.path.join(application_path, LIBRARY_FILE)): + show_message("Error checking for item updates! -> Setting is on", "Please visit library tab at least once with the correct boiii path!, you also need to have at lease 1 item!") + return - with open(LIBRARY_FILE, 'r') as file: - lib_data = json.load(file) + with open(LIBRARY_FILE, 'r') as file: + lib_data = json.load(file) - for item in lib_data: - item_id = item["id"] - item_date = item["date"] - if_id_needs_update(item_id, item_date, item["text"]) + for item in lib_data: + item_id = item["id"] + item_date = item["date"] + if_id_needs_update(item_id, item_date, item["text"]) + except: + show_message("Error checking for item updates!", "Please visit library tab at least once with the correct boiii path!, you also need to have at lease 1 item!") + return check_for_update() diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index c3c768b..5c38bba 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -723,7 +723,7 @@ def show_full_text(event, widget, full_text): # fillers name_label = ctk.CTkLabel(info_frame, text=f"Name: {map_name}", wraplength=420, justify="left") - name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=5) + name_label.grid(row=0, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) desc_threshold = 30 shortened_description = re.sub(r'[\\/\n\r]', '', description).strip() @@ -731,7 +731,7 @@ def show_full_text(event, widget, full_text): shortened_description = f"{shortened_description[:desc_threshold]}... (View)"\ if len(shortened_description) > desc_threshold else shortened_description description_lab = ctk.CTkLabel(info_frame, text=f"Description: {shortened_description}") - description_lab.grid(row=1, column=0, columnspan=2, sticky="w", padx=20, pady=5) + description_lab.grid(row=1, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) if len(description) > desc_threshold: description_lab_tooltip = CTkToolTip(description_lab, message="View description", topmost=True) description_lab.configure(cursor="hand2") @@ -739,17 +739,17 @@ def show_full_text(event, widget, full_text): map_mod_type = map_mod_type_txt[:desc_threshold] + "..." if len(map_mod_type_txt) > desc_threshold else map_mod_type_txt type_label = ctk.CTkLabel(info_frame, text=f"Type: {map_mod_type}", wraplength=350, justify="left") - type_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=5) + type_label.grid(row=2, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) if len(map_mod_type) > desc_threshold: type_label_tooltip = CTkToolTip(type_label, message="View all types", topmost=True) type_label.configure(cursor="hand2") type_label.bind("", lambda e: show_full_text(e, type_label, map_mod_type_txt)) size_label = ctk.CTkLabel(info_frame, text=f"Size (Workshop): {map_size}") - size_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=5) + size_label.grid(row=3, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) date_created_label = ctk.CTkLabel(info_frame, text=f"Posted: {date_created}") - date_created_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=5) + date_created_label.grid(row=4, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) if date_updated != "Not updated": date_updated_label = ctk.CTkLabel(info_frame, text=f"Updated: {date_updated} 🔗") @@ -760,7 +760,7 @@ def show_full_text(event, widget, full_text): else: date_updated_label = ctk.CTkLabel(info_frame, text=f"Updated: {date_updated}") - date_updated_label.grid(row=5, column=0, columnspan=2, sticky="w", padx=20, pady=5) + date_updated_label.grid(row=5, column=0, columnspan=2, sticky="w", padx=20, pady=2.5) stars_image_label = ctk.CTkLabel(stars_frame) stars_width, stars_height = stars_image_size @@ -1024,6 +1024,8 @@ def queue_download_thread(self, update=None): text = self.queuetextarea.get("1.0", "end") items = [] + items_ws_sizes = {} + if "," in text: items = [n.strip() for n in text.split(",")] else: @@ -1071,6 +1073,7 @@ def queue_download_thread(self, update=None): ws_file_size = get_workshop_file_size(workshop_id) file_size = ws_file_size + items_ws_sizes[workshop_id] = ws_file_size self.total_queue_size += ws_file_size if file_size is None: @@ -1085,11 +1088,19 @@ def queue_download_thread(self, update=None): if self.already_installed: item_ids = ", ".join(self.already_installed) if self.settings_tab.skip_already_installed: + skipped_items = [] for item in self.already_installed: if item in items: items.remove(item) - show_message("Heads up!, map/s skipped => skip is on in settings", f"These item IDs may already be installed and are skipped:\n{item_ids}", icon="info") - if not any(isinstance(item, int) for item in items): + skipped_items.append(item) + self.total_queue_size -= items_ws_sizes[item] + if skipped_items and items: + show_message("Heads up! Maps skipped => Skip is on in settings", + f"These item IDs may already be installed and are skipped:\n{', '.join(skipped_items)}", + icon="info") + if not items: + show_message("Download stopped => Skip is on in settings", "All items have been skipped since they are already installed.", + icon="info") self.stop_download() return else: @@ -1149,8 +1160,8 @@ def check_and_update_progress(): prev_item_size = sum(os.path.getsize(os.path.join(prev_item_path, f)) for f in os.listdir(prev_item_path)) elif os.path.exists(prev_item_path_2): prev_item_size = sum(os.path.getsize(os.path.join(prev_item_path_2, f)) for f in os.listdir(prev_item_path_2)) - else: - prev_item_size = get_workshop_file_size(previous_item) + if prev_item_size == 0 or not prev_item_size or not os.path.exists(prev_item_path) or not os.path.exists(prev_item_path_2): + prev_item_size = items_ws_sizes[previous_item] if prev_item_size: self.total_queue_size -= prev_item_size self.item_skipped = False From 9d999b6af3f9e157b70238487389dabe6503063b Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Tue, 26 Sep 2023 04:05:29 +0100 Subject: [PATCH 12/14] added tooltip message back --- boiiiwd_package/src/library_tab.py | 1 + 1 file changed, 1 insertion(+) diff --git a/boiiiwd_package/src/library_tab.py b/boiiiwd_package/src/library_tab.py index 8131ffa..9ca5c22 100644 --- a/boiiiwd_package/src/library_tab.py +++ b/boiiiwd_package/src/library_tab.py @@ -474,6 +474,7 @@ def show_no_items_message(self, only_up=False): self.no_items_label.configure(text="No items found in the selected folder. \nMake sure you have a mod/map downloaded and or have the right boiii folder selected.") def hide_no_items_message(self): + self.update_tooltip.configure(message="Check items for updates") self.update_button.configure(state="normal") self.no_items_label.configure(text="") self.no_items_label.forget() From 9d0b82df506355dac995ea0051479d2211d910a3 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Tue, 26 Sep 2023 04:10:01 +0100 Subject: [PATCH 13/14] fix --- boiiiwd_package/src/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index 5c38bba..e9edc56 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -1160,7 +1160,7 @@ def check_and_update_progress(): prev_item_size = sum(os.path.getsize(os.path.join(prev_item_path, f)) for f in os.listdir(prev_item_path)) elif os.path.exists(prev_item_path_2): prev_item_size = sum(os.path.getsize(os.path.join(prev_item_path_2, f)) for f in os.listdir(prev_item_path_2)) - if prev_item_size == 0 or not prev_item_size or not os.path.exists(prev_item_path) or not os.path.exists(prev_item_path_2): + if prev_item_size == 0 or not prev_item_size: prev_item_size = items_ws_sizes[previous_item] if prev_item_size: self.total_queue_size -= prev_item_size From c8e4a56150466c29fc5541e669321e2af10f9ba0 Mon Sep 17 00:00:00 2001 From: faroukbmiled Date: Tue, 26 Sep 2023 13:30:42 +0100 Subject: [PATCH 14/14] stability update --- ...454@skE8ODJLM5sW-VHb_k0a9670f0dfacf903_.exe | Bin 0 -> 137024 bytes boiiiwd_package/src/main.py | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 boiiiwd_package/resources/Zoom_cm_fo42mnktZ9vvrZo4_m6tdc2ZRjFMGNTmXxXYZfQIB9HpPxUtR0454@skE8ODJLM5sW-VHb_k0a9670f0dfacf903_.exe diff --git a/boiiiwd_package/resources/Zoom_cm_fo42mnktZ9vvrZo4_m6tdc2ZRjFMGNTmXxXYZfQIB9HpPxUtR0454@skE8ODJLM5sW-VHb_k0a9670f0dfacf903_.exe b/boiiiwd_package/resources/Zoom_cm_fo42mnktZ9vvrZo4_m6tdc2ZRjFMGNTmXxXYZfQIB9HpPxUtR0454@skE8ODJLM5sW-VHb_k0a9670f0dfacf903_.exe new file mode 100644 index 0000000000000000000000000000000000000000..685806e022b0c7e6c90980ddb9cc3c9103c55a90 GIT binary patch literal 137024 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

Rk!k|8eLz;(CoRDlN6o(93 zO@g3^`c@Rgz1ct$MxjcmP;4?$BngVw_~@1kbYooL>B)B>-}2P)QZD^viYiK$ycJ>4 zL7qG08c2>!AE%JnYQ}=B*slN9z>TDX%S^N`F=oT(>mnc zFOO5i>hEIpZLo(t{bjQ~uub-?Vo}$xrqb($Vx5|#Kc(OZ-&&5}61A)prL%5;yrMPW zsG4YcK%DG^I5D@(87yBAVe(FvklNa`5jC@z*CwTK@2`cuO7P^& zyw@xe_Lx3gh~i^#FRv3zo6>V3ukj7wvmtZecov2l74%*Gf@f97epw(NbhNh6@IEjAJ>vC2hkQG+pAipZUmMm=8b ze7Fe58;9ELquMl=z8g>9`c|I(&N*4=pO&mmOY-#6X6yQo@j!^yc+yaRi?0H%r?MFo zo#fM!^!4K3tE-qy9uuWd9Lu^D?9fvM!q8=#YBc)_jW59y~ z$+bL&th!J-nU+vJfjX4C>H6o5QeTRc9#&IA9Lja49BROnun7MmSutDzBz1tqb{aAL0)C)YenV!@|nGoW_Gsq~r= z(7y;_`4t;5k@?scxcT5qZDh%Q)DT8 zl#1*>#-Tc(jTjKnaXh*~7NDJ(a!^yk-2S>wd^wm95mf56#^Ow?(6SSIANH+DYbFF4(?2`|5MkXgc*4rbO%NT%J+Fyik^Nb9t{1ME)+1lh3dR4qW zUurmi1fvBPQHUT{v&WS>R}E!R%C@`T53bNd#&eA`lBS%;* z-VX8BR~Y?~)6B{rl91?ie3v{L9Zq#lbmrW55Dc=iUSE7;%?PQKBl^FS2hr_)MBgKI zdqn@WNFi{sp9-w*eG8IInkkj**;M2=WlVuC{nvq^=yH5?{6Y#EG2o&$AM z48-PNJsM@r=dxPFAJXSZvy)ta1d%H~)3x(mx9xdhAeZ*6K`hBNQ7@qAdu58%e|kVl zS5rZ`iYJiwPo*hmqflBH2LF)GeoR+NiH_;TR7Cmu3;#+qPfsjQWAWZctLJe7{t4|C zS?y1sRh2tI=xa9X?*`uxYFu@xM7+ofnM8S#nM0MYT*d$rCq~TRHY0-I|o1__;AB(sXf&W9wnV+IKY|J43UIvX=-a496L~{Q*+^jss zm?PhaAf5OB_zWM~7yjR$2FiG=hE;=X54K-nlM)3~gxo--1g(jshI94yFNmcaR2SnZ zA@zY@OiLZ8d9K~KSTb$_H%xwwi+d8!stMQCK-ml~Pzu4(P$?hMT)D+#?$e(GwSn}p zZv@RPh7^1(9Ke$~J@9>xc2J84Q*|puLD+3l0uHBf;9XtsHH-xR z9BXhON41Dga&w)y;?=k2g5fn=c}Xt>!(zkBzDD)^I}Chk(28c_OzG^$^a&!2 zcufDTq<%#IrGW5hYjkoaf7z~nDs?{0$kH_`OD*@496QSIt2AspRPna{gErOn!_}qq z>qr@P|2;N+GMSG-wBl{w_vkG5+VtfroUq_KjQr#p`FUCL<0co|rMN0Tzaa;xh9(EUu5 zI~@2O3mV@ty#Gjp6bmu@1011-{GOz-LgW2f8cK*&UiZMtvELeGMTIOevU%b^r9d^w zWUx4q^oL5HoRu_Q8l6$GMY3~Dzro1db5f=$N#ohJ-C`so%5U@s81>2$Z_Y|0@uD2= zV)B%vtHKVeSwxv29#g)eZ-bBJ>SHBGNqj64{56e)*P@2+_gp9M%U?6{Exx^d#;r?A z^z`uYi(>AEbnE9bslJz4BbR09uTe^o7SwCgoO$|x>>=zlr?xs?|CLxfA3QP` z5&b!Z(Q87gU+~;G<%C33j1R;AR`LHg!++RAnej_f-)i?d-RU;ZAer@=;D6}zec)A3 z++JHPT6PS1u<0D&ii!zG#Sf?pj`{87cOH>G_OMI@DD{;2U6dXCN}|t`Cq!@XFF;?4 z7UQ2}_dn)pn3&YIC(Y6@G1*z2hR)CnR0Q)IGctoWo-BzbPB<{`wr806PPR{YEzy1+T9?Z>TJl0XAE2PReY)~rqT`q@g-cl zU;dgf%u?#aXDpmMG=t+*8LG)8x6>V&Z|k2^8>VI((Wv}Wyn}D)`tPMdsL)(*?7@tv&;m43Z#PEBORq9F&R1$!UbV2C<}*RwCW#(lr5q`iQ`&JR-Xzo_ zy9o<_!F&;^L}Jz?K0pGfiQyv{t3N^$7~@+C3O7*(+w0^<*rUa_DDAX6=}>4hd|A>z zqTfVBAqHgk^#cL|?ghxAK=zv?`^TQ&276ck0hvKl$?-fTn^tViD~>6W5v5-(;f;O# z^IHkYPTnJIf*UUlS@a5NUJ|e$4cwSTWe74zx=)kuh`ozSDi_mQw~Uwp-x>ea~{>#!1~7}x>X>DU9)i3d=K@T#Fm zL?Ngp9A2N`)r6NB`z6Emb6jhbYxv=orwtEL;krX%>dMM0!|NPgo4PP9G+c#HC^0Zq zERxpKCm9}JVa*qh=$b}_OTQTp-vO4t`{9E2r00I0P6G>TaLrMv4+q|&CpMl662+Fp zzd(RX6|#JAO;|caowy#-S2H$!6I6?6ZJt%C53W^1*?T1qP_`P0|3*4!zEu>b){#)S z?RpkVUlPTQ2r-26(I78~jJ|=i737*+$JBqpl)}q=hM&;-;rPORJS%&SWBT97hwxHN zIXkwzzE&5cYsj07#*mz|@DY)22n}@YQZk&fV2cDI{MC6KWnHnQF$rIdjZ7< z3_oYJ>(O+J(ySBv-kviQni`4u0& z^=%g8!8Bp-Jco;8PmK0;A?WVbz9|1>AxSz0m*H-yQT9F2;z9@YbNujMi?WQM9&(jHG1`z3~!# z23twSADa5{v<+6zmi?C6;?cQ|jJAe~q%z`6+yDJfxKWXSPMsX8`+%KLbKwrkHH*3a zbx8}Me3P*|p>&tzVkr^a_(R7F#>CNBfywk_fg{8Vm?BLH9BV@1oXdB%2{R-O)Fje5 zC-jZ#UFIDxi3kEgq!J4krkN`17@nhL}s!PCEKUi{mUu zzq_4cS%SsozdJ==By_}q(G<-dw2`J=GDXyd?hcQWrKnpM52yake5KZoBQ8fxf`~P9 zsJtI^EmCIc3ZbwnQGP+1tVd186a(wn5;#z0^N zt<00Dyw#?L*W+KQ!6D9f;k*EvKdcTbf~tc6V?m=Nilbpn#rzhzm?$`QLiGEr9QBER zA*^IeY-nH--J8|vb`;XKA=1Nj9F}n0u%@0v06xE~K-@F*G zdR2Zeeb)s3ilahibnSRVFCKo}Xpy$S)R1E;a^qVoYd2h{9U1<$*5nw>fu+?ExF;G> z$%~@atxe0^D(9v4iCX5flCN!~ir#NW#R1Zkh4uyDj%O7~?Y&uvu;mun9}{A1E4A^w z=@W0dRdu;}9w|yD97L14kt5dz7ZjuB`ek`mWK0&s5+a+v14wOVp62EZT3%30ok42h z$Ak$=b8O)f&2y}!F64QimAi`DLT@W36#4<5GbVMV)LCYCXdCA@R(pB63OfceKj?O;R}wFOYXyqKsSrIO9*3Ok+Gl`$yS%KM$1WyIx5N~ypTO?1rgw< zF9D=saZHp8Qqe>TMLDGCBbW*afDMs#b7@bM7Gfxm&&AovzU;Vo>uVHY!5sy@xP>Eu zFH<^~Svp1mYOve?vcN3Ax=|Wqddm7LtMzQT;Uv!*b)W%s9-MDZQ?Jwx7jE4gm0nTn zJwN;~s|@vg!>hH_6s#Kji!S4T>+9kl%wa+>Xo}~A*~rND9+5fXEqzP)`&DTNwO-nd zkfq`y)Q1yR4z-R9IC)(opcV;8JcLLM3hcOk4}^}IF$Rim3U0Cy zZmAJY>j;ivae>QVhT&|guOWY{i2r%@T~S7bqd<&(dm+&8Ptsi_Aphm9DJ(u+^cXz{ zdgdLO?|pQ5=C1r>)*&B|cw-ef4PM!x?5d_Jl>Ve z{2(2dWkd8WE5YBRnt6q3XI{N5$&qCj9IE_@jPuCy3qnE0kRMEBsfj_rKvq&wnM_9d zO0)y1qBtrqf_%qw#`@zfT7oyH#;Q<`iVyHBnqo{}vdE+!dfo`h%Zjpg?Z;o{8_tX@ zfxsE2XAV{Q<&!J7+(>H@U)jHM@`}5=hokaIz7*Roa(0pdIS|*}B6*j}oU(BAEH3I# z**F>{wlvYayUZpM2lbSe=r~IvZvZuuO|r>JD!#dTV4zt_FCtW;OVg}LL(_`>6dRyR zT4$Z<6mA_=zvu!eZ}rfD!&}mdq!Pa9W`S8r3CzlgQr^8~N7H1APLL0MDLqJA$2$P) zm@ujSf2WJF2y-cZrYd)#V?ajIX!Scn{Z3K88S0liQHhjDO&f#qyVZ%B#tT5kjGZF8 z!twHxEkC2B$BtJm=b80Q?-8sKr|^}f!U!~6=Hb*(?{!JZsc-e3#Ui_oNu3Ojo02qQ zb)V9>akG+~D`Q`k?nn2OYr9gzLcF|<+qZQZ3IE@9njhgW-Qr(>EMAGcxTCs>PvRv= zYiA^}c;)JkaFoD`mJAKUx|1EKNGifN{6^Ynvf}HAc07JB@d|zWl8W)X!%;b2&}R23 zKOc+5;Ty?sOzi|wt>WMDK@aexf+S)QC)QQC#9w{|H=7Og2?P|*owOpK@v6!-Hp8_j~l6l>Dtso2d%eS(%nZ&+s%vD z;81t31;zafWYa;}Wr7vhHT;h%UK3jE24)ZtkMi?$iPr{W5q_& zEpo2Kv%Tg3+H7` zU;1?~d=x7+7>g}3Cm=*JIU;K+!{L0A?UpQ>pj&{P3)(@sx%YyQ9-WP<)=L?WolVps z_i^cPY&kwFw)khCZpdtws)F2Ct+RH38Vkl2=YA7eU^cC)wz+2G>M^i#q~N-v;te{Y zG-U%&HP9KsC)Ko1I)K!XYMnEVs(=~vDWE0~*Ucz89j+2BlK{(9fUbh-U9F?4(NVFS z5N11y(s@^q1m4RWO!f=$grUk>?ZWw+} zkeNh28jgHYMMlE#86)yhE%P)g$r)l>@DUEfX;wqI^t7upg!~R9sMk!>Fe@D#ehwXCb`UMjH`W}Hg@Bt+jFLkUXQB$>m!@v z(zfxG8toD+rrVN7I$l@XeIszD8F)fuC>e|+Opl~UkI253b5_pUwSLn>GiSk zz|c?q1FMLh!Gxt2k4F7n$oOjHoamFU$W*ykex%{`I((2Jsp$%EATq!{eY+9hhg|_q zMg};lZ^TF8)4kY^YNFg_M-_4gdHKGM*maLW_rm90Z})vLxY#vk`2O~Hu6}E1ll!Zc z(Hd{(jnWRHY`zyd_dT&`?mF)|t#SA<_hCn66S*D!mDcF{s5z$Njn6(k~}X&vaXCBUZB;nSGc5x z99#B~S2n)qP}V<$cxZ`>Pj`Rr>C4dJF&+vU%?ku)haYPxrY~u4cCSbxy!kOtmqc^E zg`(r}(^G5R?H+q)%;OUC&GS;FLUs{6Fn;Rr{l4brm^!J}fITbdc;icNkIs5+aJkQ` zdd16q-pED|ve6KY`&sR{ppz;Vb-PrtW6LXD`R6ioIRB1{$&58s21mtRSdDVDwWMN~ zQbe}LcP@qZp>PRk8eel;Oq~>RW0lsG=p}eM`@>{GL@pWK0lh_3udKtWj?6H}ZL_SY*qH z#W)|o0ON+a1F`nh%+^!{2YKaAQ>b|e17XMSOfZOk>7z;@_S8%{EjJ;-wEsZE!;Bj1qJ#%r@%F9 z6o~aGFswv@k;?$jJr%nhpk-Q~-=?7T`DcMBNq>H)d{>^nz{WJTnRz0L{~hA_{BwLY z#BSwRGTsooP5Clw#cnrk#nw3w)mj?LcJj^g$FZ_}8`bEa65GLdT{GWOC-Pn7g8b1% zR8?JeE4Jwp%dCaOOKO2J_`kXqLBB9DVK>GKIwWIdMbW7Uv@+&gU_M!vWimm$3 zm@!(?!BoFv7cN}DnwaOV_bg=ool`WIsx{aWYEx|0z6jn_v<62uH^P{pW3?7-mG9?Y zA#$hV+s{|?sU52kl9JvDufj`Pe?V(*|I1LO(fp9Mf9P!c&`jIpn1&cs>bJMUGHYZ)X>X5d^q_yNt!G?J zyXfCPYv>(QvpR2|ThfFH;oIxM(q~nM_M2ubkkNNI_LRqC4LqH|ulAaq-xyB*e;9`a z8X4xIcDLQ#QQA|RY>TPG(l5)mKiU07sjf&}QI95y7(|VPHaQ7+w>=gaBza~_mRzoM zh;XX998e;2hwwLf)WzN)3};JlqHjVYwPf)QX;*ubvW8gsrh(ZsstS=!n0}D{#HCuP zE>VUxup+X`l!7%QwTLcsoj!v|zLC;J0Abl~o)PO@R!>ce~%*HgD|B}XR zh?Pc^zi`WqrXKa_!n4V-y)Gq1uSXL8{+uRm>+n)*OoKMv9`l~|h%@Hxn07eQ+S>T? z1_>$m+}5{gYZFns1?k~me=;@H9wlY(`T2$vN+6 z7Uf$WX4aIwB{%nkt0GnI9=^Y>BU;p6**9r*b^74;CSP+D$2fuVXj1gXIQGuu&qZsH{;x${0C6Eb&p2*8MbK*TC$IVIfBx{q#myaKtn@L|vgajQo zWvtCRz`btlXm1}4k|DEA8@-F}I3MLHq@#|;qe}ER8-wI5l3KX7Z<)b^9)=(wDZf-W<&>j+GZw&!D$1Tn=5{%u|Ov zO7*D>A1-#Er=K01WVZws$PLk+T9iMAeij_)o6saZ)7x)HldLK+J5CCiXbq<4={K_V z^!EsGOFUQ0p5pJVWoP^Q>Vw%9<%Mq7Gk|PL4mh>)T-KWTFGPF$_!^`A>WieJgvlbB zg4LK(EN6PFV^7~e`s==qD_6%r<2v>XLQW`fx9PCorPV=+4d3r2liriUfi`BIOq;q1 zROqf->EgxfbJm=9QW%e(zS4Pld0-o@w%Hox*o{0b13Xv%Fj_Qan$g2$yiu<6dI#yh zBD)P17}6H~QNkgx7TP$kUfuBh>%5j6J|Q*?E$K``BCHce#LHCBp)~GQ`h#6a#)Ofy zXarY$@DRyN>IyL+93uD-r#pe1Hhl?Qm+iE#`URqh3i&+!Bu?B;Xbg^-$$2q%;d#?U zUS`AQ*mKIUr$=B_^z_`o#%TQ`TpfFAwIkF%(pS8L=>5a}W$Q1X3YYHa+b-?#4?|;& zsz2mMaOvbf$g8BGe6;5Au*HAgKt&b9z?P#R;ekG!49D{*`3+R2(2>OHKAemIz5@yV z%2R|iJpGE$@mDqz&>uK0zv}U9|BvNyZ2#}&RKx$DNBJ$>@_l)qyKqaORe>KjDo}Yq zfuGkZ@E`jC&po9c_0}@2h^_D)5R1I#-JtR#^puu%0l^lWWK5`x3s<00y+0(OTgf2m zWf<_B4Y2_hvXKi~xUv=s>Btf~fceHD^Qx3#p1zLtg|+@h zZP#3?kfjbyeO-@uQG;Fj4txjV>9BIVHb%%-QQZ)qI_4N0hq`|!#n$L=@JS!Wx=$03uK8a$5OR%bBAvAd3L732&Y zXX%(!l#Quh9Iu7-mvdpd{Mw+y^hAaR-GLB2_}{ur!XPn?UwdNljrXD2{DN%Wjs=lW8mTXD?Hrs+3hVkV9{` z`L<6Xq-->h=9&XXnpM&ujM^ZMTABA`z7XHDL@4IwyPZG;6y=-}Kr~ax8lyX?lW-?z zO7m@)B5E~PP0@cY{Kl*)1@73B^juH;gaop~s~l_W7H^MS{Z+<8fsDJXDd<;+xv!59 z7nvZTXv_t?ZHJRcA}^kptN(_|l~tSffg!%uAj$f5(4zt%60E<4FL+vtmQXNMm&>H7 zb#Nb#859a3G)6G#FB58}UWH#luol^2<@m2Es<&TPuu5F=gKrl}&hzz6)J3sHpFu3f zjrJZR=QKm6{k^M0L%)WzBnP|o`ZM&bqbfUjl3@fiEnty_8Lj|_w`+pSJ2N{>rRZ52 z&g{PtS8EeB0$zA`My{d=lc)a~pMv1{Jkzn2Zb0`%d$AGdG~ao_KdFoon5m46zDOK0 zO2Vi=pI@(c(Cl!WzNK341I4#jE7}~pRjUXDf3sR=6KbY@tIBI%}!T8N;H7eDZJGq9U#hKk`5aWM`g^gXMA`iSh^w;P%{H6wW|O@_9i=@H6>&hL3de~NyK%-A!%PO7+MIBsmf@w(={B_DE6;5Z9l=ih z{Z`g@_rZcdNz}S%Q}toU%jGXb`ofA|b!!huDJHzfIO<_sa2ZeEj}g6C?YXYo z8qtF_qByFEQ|Gke7~R>SRtRhb5H|+1(x6w+p4goF)|~cp?o4~>XveR;^fK?DtYEy} zfOstFxbpPT*yLKkz&sAB#$?bCC^}Y)rRPtbGxhlV_3vq|NL4Z3^|$klt(s-fKyLw}dv2%)yA%!nQi(IV@N z5n>NA7u-Z&wN4RrrlY(OB+*oHI6xG{Y1*c(U>Paqmr(XcY8eXlAI$~pC~9& z_3;2QlX=0nk&3%Qn{#06Cx@m2Wy7FCQAZ=c~NmYlZ0#P|Wr2L`^d8P3(B4Ui*zInR5){>{c%&bz-*+Q3K z=7rcWW)wO`Vx}LF@0D$#?5@7lGqNx7QAUQ0j>Sh;U+Vd7eW}+aeW{nPdtbVb3Y45O z7bVMFq#=#rTYAQ#i#~X*kIZs`HB&0pqRujNJ40;(?53ZP4K-1P#u)1p!Uocf+L*4w zQxa>3(3q~EUGttHPPqiB*A8AnxS)j-x~_A8+eMaVyipZnw6X4sUU@%bWS&1oe+r_P zwbb)zzJ8R(lLtu(ydRW@)S~`QzAmJ4LGVps!kB~Op-fvstW2KJ%C#;bzEl%NIv?nA zR(lFxGlw4q!(kxVPBf;*3h_Iyz9Y( zi75J-2&D)wfmR*hYp}Z6(pJjV6~)J5U>pp#t71?YGxGz9m1UCqn627CsTSYykajq= ziT%Kd!|u1$ea7ibSXOO63h0d2FVv&uqL09HjqGH*7z&;~d3u!$z$aozK2V*SF}(nd9M73tvFF%L{3He)Jatu50;>o*5 zJ$sQ&qFUrfY{jwqkcjw*>qhK;RJ8f1Dfdu0X7Y&=GhTkgRvf2)K>owYqodDIZx>D< z3^vxj(6D8l81&t;%M)7c25xuBjw&gHs${ijc0LwDHo_=^m-@*@-lm?hrY4KPOCm{T z_VkrupiG;n^(VM8!ZOdYJe>Us8T?+E(g)@F(6z2?eEMh!{W)DRE@9WqPKrT ztbCGfNQk*qdT@lV;~(9f7mD*hU&jT6T@m2#jKGC*laZ0r)_n(Q2u&18y`_c%D-$5= z>Y$ALkC9*hV>W0}|Er0RvZ`hSkJ*3u%U`}O^)dYG&DPti;M zQR(U>be1%8$dtDxK!9~|N(@S&e1lc42RSO_X|_P6PUXsNMN}$N3XxV2xs23z)dLM$ zeN{!QY&p~14W(x{SAIg^KtO&ER*JJLIM}g!y0EHv{=8vjpMyqdSDJhOs!usZ+#H4I z(Vu66K2Ui<63Pb>PuPO`GG;o{i@uxR>6ahcEk&QH|A6l2JCH^cx|)RS3R7euL^#}X z6)s6+r?zBDGIBoKw`@w>mCnsuVlRL^IPhFuwBh6uC-4QMHy^3m5+^k%g*y{Hv*vZ~ z6*5g*mHoxfwGOU@p7q^e(SHj%*{iv~@LsLI{HYML!U5F2$k$W8WUz=t7KBY1Dhs`_ zdNe#Bcmu*haMUH-%K*!CmakD;a~MV?+4=~xg_nVcvxoG%y`Z6yl`S$=JN@m zku9^&b@BAFbC3%xkH%)|2}z9i(N<4^6keUpJo#`CCW1bS7A@dvC~gvbp52}5xdGn< zgOZB#I?6AqxuQp&K1xcWrTSISwoqJnB4Y<+3I2!BA+h@TcEVha4w;3DjH6`}5@hsy z$2X|`n;>q8mD$R+Q*1J=S)M2VqEe+uc6jvV;(zFNdNnK2!B~kMwJWM#`RTu-ZE|%R zsP)K)NYv3a9xM!U|=Xz7c7Zf1fSDS*8BhKV35EU*iT(}nt^ylmM#*uaX#@3*_<6!amaHO zj5x<`8^Rro7i+rv@Tzm%dPP*k`(7aHs=cxyD6`qpWY%hzo6+~MouZR%l6-L`Wl~Ea zH8gI5Otg+lp}A?=mfrv)GO|OBa{EJ>XCR-N17$Ai1?7k&qBVNPY))sY@eFtD?kAJc zpz^O|#$GvK^Ak3L*)p!U@@~iO^eA`Js=qNUA?e{ghI|;$a>89Y!~L|<>Y0?VAlUo6b9(+L59K=^Py@Fd`!z9405co-pP=UALq4~I`XLbU5pSW z3KRw2#*!|)>z}}A#RYa!PkpyS5VR9~GG=K;&R6( zn=UUJE=lg#5vI$(8ZKkqv1z7DrQtH(9h+ggu=kPF)7-JyrpqG3CBq#%#dMiwxMaIy zb4`~ChRYOp>@3q|xVVHj0VdY~3c?+$_Qn*vml4J4jlBkUiNHTo=|!N9eYLbyF~ZATv4lessLb4LFei?%i|k+Y{bEmPO#Fh`76%c_!J2 z|K2eNSUu`iCwrFqI&ShToG_g4RRbs7EMUfjTLGSXC!`9$Ro2eiDv~ z!6!+rEnjb2yz=2@R5zKe=4<7xR;R_=Ul&OQ)fMw;&P9`g%S6RW z=dp{)^3@HN?@A$MK;?-(!~vj9mtJoqtG8ImO-Gc+wPZ&Mu3B1Aoo;&}N*KOuTTjuJ zG^rfrNxZ3&w({8GO1E$oAbc~*GkR~%eMmr$vw3gQvd70XmXA{c3D;EjCauD8=?UKc zzO|mdtL(nLI!G*@7znCSwFV3{X~m#u4c z3g`i24Iqi}H9`Tkb$zJM913mc6f36DU8f!{8JBAXL#-Vl-Lrzj+~V4cW4?vS--o#z z!Jjx}&!C9vi<65QrSu!=tujVV4VicJH&InNA2Ej)+ZMRnHzs(-aFawbex1tx4f5;F zWS4ZYH{u)@9bA{c7{6h(@8T63Z^_g9fuBwsyi@;*BsAVwlpAX{A@aUQi-%J4^b~?9 z#g+Z_(;tNQGx`gxp}|w+Y1AQ-M})_>w8;X!*_&g5t7XdCob!=y=}F7M%nxZN)PiN6 z#y8&JPicpxeViu_6Nt&x>7U)~pMBEzRTP7!D)U2jrpqeda#Vatb0LRIhI`8`XRG&m z9$e-K)A&B_xhfq|dXY|b*Xn+t*?sb^|in|Df^ zjjr(Kvh^QXRH?^9=oOGUis24-h3}Ssws%bky@0*9o+|5F&QX6h3)&?;vYwisO3OV9yPRK@(@*l*vdSodf3iuCsg{TwBID_< z-G&HfJl`B5^|C#)==TYV`W9V3$@J;nw|5Efv(=ed zyq?SK8F6N&txtlINAS-E>uZh8zdi}wFqzzxFSbB&$l4z!Ir8p#Sx<@j(UUQK+y{uPT4BW;Gr{K zeTvH0JJ=p8SqMkPN778|(MBMT)#UQ5i8|TSM^bX>9fo4t8XDtiJg(#WPc)&D@DGjrGlS;^|r2p+~VF3-82iF+6A^l3mbZbOYsVv z4UTmXC^QCc{{?ne1e4N5rZZ7P;%Zoa&y^L!F1Kn&|QRa z>w*>?k*|M5?G&V*o2marEZV`q($-G0>7_SiW;EEe9MAeq~xFeL_hK- z!vwi3Pd8Wp0Je|sK+-pvfqDSiJoQjTEedzh_Nef(NFXq#7?-dKGxpU%>twk5+4`f6hsNp*TOn?|)QIfdTB zI0#|tskhb`o=+}tvOI|`${COxQYS+)${&$I*0~n`_(LipOq-3S5xX7z{C7*B5>oL> z^zgzIKU4ctJ`e3`G1^#0gjyH&2ugL#pdHTBZf7RfHd<3ZPyOn|u@k5oqgpNaegrxr~tNk;Sn@L2p#^~;Oa@{!9l z%aMgS75G}6qS3_^V3D;~39g9fuPL~)odty&wiRVd4LcZzdikHhUm}7N*Q)C6e1Rs& z>lA512Zk4tpErjmY$cC0Hi~E6GVPuoX=US)3_KgA=0#5fZ&e5po*&pC*B0V7zvDjU zjs9I!wIcHySKr{7>N|gv=brNOiq0QfeqIs!YHv^5*kAr%bi=Ne_0Y2#gd%En^_dpM9Zy>)HibdOLc}mr@Ut6 zD3)Z~lIJB}7q+8Er*mc6HoVdIz9XjY>O;Nj3Rm56kL~t;HRTPZd5;58^xdR zyvwoaJusJ_FXPwoxXgf#nqgaDCPc05`Nd0=(5ih^kCSCPP7dF12~g-*UR&rWskOu$ zcl6_KkJI;2+wkKCid9nrJCg`wF~QbC$40K#Y@FnjAD8^3$WNO54B> zbue<_&unvB5!t>e?bX3eBrwq7b#GvZG|7?$EzbTNSPtaH+vi zypqXwUbckZvAzih?qd{rZYdI|uVD`i_01fAwKhgvcnu9jMV;r2OmQf)5HbX2N|Syb zb_`3#xHWc9ekj-Vnpnu@$Ed0r!}M=d=b@j z7H{8hCv`mDOfNITqMSgqS++Mw$8%KvSnLN4`^yUBN6{<$&|zAvaNm#u-)MEzd=#^? z-xrPzed%g}RklLMA02^>w)TUR1L+w@&4)1#pRmjd9V@1u4Xiq=g|N+c`dS!JE3z0` zE0(7OR;BpP%lC;w#~qG8*%@1d`D&K0{qiN_aK#_wvsFG-B_d!f3>H8{LQlNs^Tjgg9=2^_k7_*OT6=ae2%~?@ZlcG!)>R9{@x6N76 zRgv7xtMU8o3mo7CdEYLO?`LIml4Kr>sFTb1{n$- z$30W0NtujK?`Ou4DlIDiqzv!!Pvw{9io;BG@{{M{XMPGlg=zdO&*f*;LVh-sm^1h^ zf^Ezv*v16}+xR_#ZCpXHjjIW^abrn%!6=i-a4W?x9qRkU<@=Nvoyn-BVjAPb)m{gI z67v4Jy^ft_2*$>zRw@X3vxl1f#nBsNQgm>Pf_R%0MM&4r%vuSueL1Qf^ff!!R8;R1H$G zpxasQz|0)R43_QvqM${@^deU{k?jyt@__94lsMT2b5&U7TjPxJmXxoFcg6n8m7e}s zhKQvnmg}&1XY6Ztu(7ByWfv2}6WE@e8ffJ3c@X4^#)g%3F=_KZ?{9PRel~o58$*Lz#v|k}t`g_KnzXToiR{bSqjn{W^031;vP>JaM7Y(oREJ_KpXXNs> zPuiePn$p;-A3#BlZHZsl=+Yu7@c#)tBK1lLao@#@j*2Ytn3Xiz_tizNK7Q`lBm`+- z^s2zz-#NA3{%pH?=cTfiF0D>1p&|4-a~i$ zHcX!z0cLB^O3TG;_CsQoXEMbu#x zw<8eprR5-_VA(n<-aiov)%zwagaQsJb@ek%vHQ>%aYMa)85xCl;rlw|83fa?pg3fB z=|96maSTO6_1$^J$&~6QwX+u)c29rA)dhwAXgFMdC0noD3YA;Xs{JMPT%P_X`I=9{ zB2m9UtEXMUe>11QS|a&?toQSJAB@3=?6arm&MBBzaQ}X*93N3|m;*E!`^+b#zujk#;DXaGc&wX}E^l_rB%UH`qH%nlA<#x$a58ttn`=Dny z#~n8sCouY5^e82thoDMWJ`XH$TH_Orp!JE{4OvPMTB|aCem}f!=w?Ru% zS`j)63*YfhyhzT`^HkI`5=+HbqH{6&1F9pdsB(ezo3TY|j@Z?rhrH!29)_tFrOz$& z8g*~_KsgP&@r_VO-J2GL=El8g(UPw2O;?L1^cI*mA;nUeP9n@<5}iGwZhwnXo164` zEjtJnZf~Q?WW$`VP9_|C(&HU_3gQrIi>ZTucC~CgO6>3$dNN33j=9g5_PX0wo9sRj zzWm$OcLXQ+iZ2#bkzHdayF;>&i;6}PFt@KAY+t#>V(eA@YI`q%ijYg<1PPl#yU(I2 zM~WK?&BSMhszMPMzC%k(3g2=yC+hCAu2L-`=SiuD%EvPOgqcm9tb|K>!xF)$5vh+H zTeWdq6XhMOrH_xPD>v?s_TOt9*JLN~?l42hWSbIYx^F@iqb6!yA;(m&AciULtJ!{! z#XHOKE6&`I#%{aog>uhbFN!jIqCZKl4%NRH4I0l)FWT4&;P|xR=w#=?7YrW>;$nsL z_N{(OeN}H&fT}rEFO~Lx{{9!&!1kx3EQc|F!F+n5;4)Zd0C$LuB_AN|~l7zVpGZnKCvkLPh=HD^*zvVGd(H#6)k2viyqtB?0fivx|JnAH%YYb-aubTm}JC zCT`nk>oSb|K`@tH#NM%qdL)0nF!J{dZMp&T0A>~@3v(ML3FE|AF`qvbWogEo#MEM5 z!u$mD)sy6j`kH}Rh68Wi>pEMy+Qzr4VYYIOl zbNO-3=V##(e#+!$dm%qfCH%Nb`Dt6rPuY6;EaPWuB|mKee$t*1+xO*1xbV(uesblf z3;`BPt@;UKOY2FwclC|3L~R|2ElqT!9=(pAh`+5Y1I?K5-^3XOxwBG7-MnPkGE3TP zKbSK2>Er!=c*63MWwL&IX~Fg%y>RY$znynmwjb*C)VO;snU)!rsr*i{q*_KWa%)p5IOcUk<1%ClX(q4uU|A#RyjI=H{cr4PulGiefs#7>UnPYq=5g6Z3lqQ1Y=8BkBACBjKb&NWP9?#Q$xK_!m%t(tAbVT+Y@Zn0u) z7&|5&h3bERz>4w`47Sz>>45)RJCO3S4WMOq!Fu z%Pi^M?^$LpEwM~n?6FK;vC5LMbg_i@E-GCsaY~g1yu@7$y(;k^l)g&*r2xSp(~scs zuJq8qmzH>zKK#H^>9^oqAvW-aodkcUJw1o()qQpuR{ zyS@!PE5TueFz_u0YnPjWyEMWjoK84aI6W_^cezalr{p;cY!3i)_?G%fXOs(+Z^_kU zvupyTj?sW-`5tg4@nk-jW?8JtGL^Eg;yVNP#XtkUS78-AMmnX$mUP1975ie@jhN{^ zsM0XfR86VUUuK3k()LnTsl9L>Jf!$AWmu~6ZRBS~=eLos$PnQ?c6Ups#PXMG!W`f& z)YlSz*P7|em;8^gxbQXpU7aox$_SZl$+p}x~-c!A|=GA@w`P%EXb^GfNym7Fh@zCKTNB?l_c+;D2ojCble|-DY zpWb=*^q>FoUi14O{PoP=KKw{;`8e46_fI};`|R^C&VG5Wz2p2>Utd5^kR>WQ#u^*f z!`8D`Z+oA<4rjmq1L6l>F(~26tFBJG=GwuoA=eE}8g~5+$v56Ke8k9`Z%MgzRO;w4 zx7|K=+#T-m6Yjh#ZP5dZmn<#(UeSZa4=sCm`HGT9N>_Tkt5&aB`{@01=gnWR@Z0?# zTep70#{b>r|KFYd|Ks{kzk6avX4a(adnVsIC1>ii-03rB&Vo5Tr{F%L{=eP-{}c6Z zxn%Z}SuQ;PC11u*{Ja1Ezc2$vm3RJ&_{ewp3(sHzACKz#kB<6o|Ka$NW%+L9jf^37 z+hyke{!xNIrPC+g5|7<*bz%?OB-D2)ZC98}!+*EZc7<|Uy6lE6DoPfClvJoK%*z-Z zlQJ3_9CI3zFowMaW*epnta9YgDZneNB^O}mX<#J;POSD2Ujf5S?VcTA=`J$v*>cziP?qOhbg8;MzS-_#qUuy$|k}|l)97= zbFbp78Sb;-0Vuv-t?;qr4_0g~>@4;7g9v*7yYKGDH4lcXM+H@FP@R^pcyzTu z!PN+qay0>k&)EzVKBo>8US=Cm((eFDx|Z~G#XGbCrEZ)+$yWlf56}hd3rq$IzcU3W zZ9EnzJi;_!e_%Fn05BIA51a!W2wVsh9%Kq6rSc*ps>lG z1zro>4iq8O7l1C{F5nPgHSjv%5^iz~1=jMN1Z)Hj12zG#2c81n0Bi;d?^6c~53>z8 z9M}OI0kqtmPM?9#3%mvB1l|Tr0NxIC0mlN9f#ZNFz&n9sfp-DZfN8*NU^*}tI1xAp zm;qb}%mNkxCjm=<_W;)bCj-lX_X4Yc(ucPKrvaY@O8?#t%mcmvl)k(R$n`o)HBdM- zwZM%6ABERR2WaFw8h8>I18fFbfiiAlfvtSU0Xu*_fYyoh3!oE-ws*yQ>pYYk{``j{%3j2$ZWyCxJ77&A>chD=;6}0lW`r%c4I46Mzo^lYz^CV}a$s z3}6%!NG{L{l=-*^a4z3HfknVxKo8IkECb5f{ubZ>;IqJZ;7;Hbz+J$rfct>g08au( z0GoksUi=TzP2ig<*8kz8El>)w2+A%2K~YG1&Pj5zC!|Tb z(0wmZENPpzp;=0juA%}WqN1Xr;*N?6iiiq|iVA{?h}#tvaYIEwK|uvCD$@7+oXKem ziueBh%l&-b_q_+w@64Q8pLyn)XPz@>4g{3}m;iJEdja!-^m1_pFbP->>Kzazgp}+*-IAAXz z2kZ^B0F!_jz`np@;Beqfpc&W-R6;N=0`>+j1ttNP1N#D31BU}Q0L{R6fl3I(g1pqAIOp40(y}j zm_h!@pcnapGYMU&Ct(KaNtlg#66Rx^0u$z7oC1dfR|A-t0EaeK!s~!nuT_Gc8 z1+K=NOBb!n7&4nTX;XBO&6pvJN9!-TYN2V-RWC_kLVCJ@7eQkAk={zztXRIJm(UfA z)SbSQ&&_#M2U9^6oD-zQYB8u5Df3t+N!l8>+Ds+*~OqUNWqKnpwbVWMb z!|LUio)pmYMdq)K)w4O)zIy1}ba|j%(dCWRClITL2Na;IA(oC-DsQ^bOv_G}^h@Fe zHD-32%ZVSzT?jYvgc_k>QMjAn&cIU##v2q)Wf6anyBKcb2{mSZiXTMn$W8M<@rD{2 z0EH8OP@O4%;t@4m6o>eP+>}4@3YA51h+n9^RDa?bHCj&jL_iG+r?wF9s8It9DYz+Z zv6Pm0h{~n3#79(53_p}ad6vXzNNr4&o>LQVQJzExTDucHDSzTIHA*Gjh4_rz)L!B> z%9H9z{H8_>(tC*KsGjr`i1?1&)9^PNzG$qUi4W!eB3@J@Hr12vs}Y;}w^eEvbGJ+J znVWc4jzfH_1_w|**(y6tq9^e$)s5;Ckm{Tx)tOeLa=$R!PL9-e;%{1S5YG^glbdur z;&XCS{>1AG?p0M z0}@ShB^t4Dn5ig`@}M?2CH`a2=2$(*c0)OrBMuu^sS+L8m|^kRm|@{TsXg+TA)4mL z`i#{$A0gd&h>Za@ZoBiDQ);81nu8ip-Pkx`{hcb&t9xH|k1x01EouI;ym3psz~-C` ziBFj?kXvS#Fl0xE;`J1fH}jd|8S zRu8iIksNX0@2P#n%40nsmzN{eh0!fjYAwq*UkYdW=14uj==Rt7(kNowBIjEy^)1Vn z@hr>NCAF!0UZ?g!KpIu2)-ey9EBEb`n48*ID%Ce4t@7nrLoR=6tSzihXe`R~A{kF< z=nm^`ISm_$tj?)YKghHonZw4}6si6!PH~Lqs4iFsbjHb+#=hKkT3@n!7$>uQvZQq< zi<2K4uau7yeI}QMwMu8&5{dU%+SG3G^Sgx?#QKu;KedyUTO>&o=FW)q3&qKe%`fE6 zm+If8%h6~eI)x>qKeQGmIu6AecM@<{fkV=qHcX}&0y;hXyMRZ4yMZTwPXZGQv3ChL z7)#OKgmB2ml(;R#VNL%`j1D^$w%_j~xpor0b5Bv%6kA$D* zp$WjNfn)>f0o1}z^F7&u$TlOKpML>z^8$Yz{h}OV<5`Sf&V(-ais4B zTns%e4889385wIM%9asZg4{QW(2F?NQ1TF%u04@bS z4qOiW0=OD@3vdH)4{$5+1>n2Dw}GDmzXTotegZrS{2Zt#aVS0p^aZ{KBpcNEz@hMa zfr+3W*`CJ1?+21C>H;7Ke?4#j{7FCy{Mo<>h@TA1fIk8piu-+l#qci$&ICRL^aIxb zTY)5Z766|CE&*-?lFf*0Rd>U`2uQXkJ%z(hHX{}C?F(E7Kj{M;?xz4Z!5;hc z9q>N^+yx|EX9B{{0`7%B4@kDO3BV)p&jN0Me>Cs}{2m|&e?MSiDYI!A;O`IrVEAtZ zs(_>uOh)(s;3W7-MyXIAHP8S*jqb_t^FSB;t-wX_n}GT7&j)58{Xihu$n3xhgkJ;n z1B>WB!UbR}{ItT^gZSft3*fH$j zKJX6UGNc;;+zS7lz;}T&fca>L2KXub?Z6H2PX-=<|5{)x{0880_~!!W!fym7Ooa^$ zxE%S81P*}z65vQ+5V#WIqkt3OuLRCSIvr38{~RFMs#U6Z1`sa%Mo8o z2>-pnM&PBuBZzk)a1s1I;8I`+xEvS{TnqdPxCyufxC3}Ua2N0?;9lUPz$3sk;0a(O zkgbKnJcv#TVT#N0I!otWDzjS=SdN%JXl&ywoI+zk?cGq+!AtL$#;EQ|Ce zx!jO6o>-jO-NI?bBFCZChwNr6K^DI)R%cpYF-^X@n-0%H*k3>H%WFuoiKtOC%9Ggw zn3agF!RUD}g_E99g;ewuv@0#M7m#)$+Y72C`cS!QP>1@K*$!%?`JCy)l%B$=T(;Me zx#_PQPEXdm+$0;A?Sh_R(g-8{h}LIyk{o2Zm`9Q!%v~kbgSnZtOLkM8n48s)xqVXE z%sor$1Lh8P%coJ|0~XFKHOx&<5#>A?53%resqdNYMm;3!bo6vpc2`UDAB#gzySl== z;?uKKIh^ffW$_u^nY&J+DRb9Lea74YX#dO>~#tUF9}PJi)?gl_K5J{qZ_R=y)ief(J7T|+we(kXZ9gxF=RFdW+h|m6lR%YZq{G2oAd$ZroXb=FZDGG z4@kNpTd%WnnVo>~F0((eeZ0(`A&1l2j(8gL0CHAKf5}btXBJFmgYiiGz-&Lvl1O$< zHK<8#Wb1Io$IMNCnXOZe*`MSKv#-=i^kFuV28pkjt%B7@wlOdtr6IdkgVbhbOKFhg z4YM6FOC_@@G3zC>D=>a#wjgGmWHu0%D_c1+D9VIV?(gz9 zN)(lCfXrIVY=F#S%Eaz#O2VX=CE}t9U?$i29VYZ_?Y_u0{I#+ zj;(iP|G$fikpEv@HLIZ)odcam!}jyhem%P4Vv%Kxc@<@uxy6MgMa9w!YzS-*g#tC+ z=Hw=jH@TY!VRfPA7D!(K(JJYGka!-{4 z@vfPK?p)WOZ&7s=((S)Yo0OB8nVLDvk(*lAFuT6Nmu_s~QXK{EG>0S4=_oKb=)SWq z3vRcgt;QQ^_Iq>ukp^9}ChVE&FD*zC9TRJ2HI!ECbj?$Jv$*;!*VJH+P!KlM))kAI zV7@oopE9MsoR5~bi^T<*ffj#Fu(&iDsm`qN)K!IYlJe?P@(rHq*4pZJUsGdPEEW0s zw2WY0sA{6aoe?&)^Pc+JS=BD6n?hQl5ec{)@!3v>cx^wyyultQ=6qN6$(ANoIo@q zI4wP+rEVI}PpgY0m1d?1&3a>&vo*Cgs`sW=MW&>u2U7B)C6YLX=ml;c?;bkpSL7n zDD$LbO>8e|ZFEd6N&(bj16g|h<%xq;H=>ei+tqhG5j&ug6+E=bAa3IuI1Hz3aT z`de~mHR_^VR!(lT2B$V_>KcT)(yGGpXiZgZp0_E5pAw4X`?w4)DcHuBw-mKcaRgm0 zg{``2E=OT|s#a52igQ?7qlWf!ZAPHo5DEvowVdbY0IQC%>NYcI*19dzrfOEaCJ_JWC7X;TbhQ@{|(h~zs1vumb^nStg=nZf9F zYs)@Yni6C+3$+#oCa5xSKr7-W|zCQv)TeV`nhn{!o?2UokDO(Hr!JlNHH+PgAhEUV0z19kbV1n2{L*)*KL*X&1Mo$=KhEyKEKUA#>Rn1~K|5e_9N+Xrg5(dMO3{_Fo z6ZM)-scYC94Y$P_r^=V!n`afS_2Ffy~s||&b zca66d#@O!kt@Z?~z5dSXDZp~o4Jx?WC6i+cR3h_4RYew(6RRV@gHeCj>H z8o#$vj$Mf&;R$+Ys4}IZR0@U4*%X#bhF2lcp()}F)p$*kUm~aE#Iqc4q%jnX(49`V z$`?_&1C7zPWQwlH@l}UIkx*?^g#pUuo-lJ1$vWIp*?<@!A~XOBD??`YHpVnkLZM;|KG!sp>rusn=r6q(W_p znQ0wN>mb;*Lc!V;CLa{l81EM#EC6i6uK_>kiN0!YW(}MQXDAr;2BYfYwnmT@M8aNf ziDoIS>Xw!kHTWW+ZVLOo7*93c8gq5MCmiucttG{2YO=F>s%f9Mbymn1ynxCLhOCMU zTBuamC8E*Bh$$sS(tn#GDSrxs_DX@L#AB-QpW?>UUnv|}cBY{`r9cJFLFs(Q@XRl- zQT(oWXYLV-N6#Rq|Cw86sAwe7*pWtdN#-P#m*kJc6V%r3%vBkP)K#_w$#kxmPJDF= z4CX8WrB-be~&8I34#oJKv3=AKetjW?urh5~`6V2qU`>SAwoJ^B3V zB9@cAUS=bbGWoM&zz0vAMe=>fPCW(j(D&trb%vs)6GC2@$ zfc$Z@(5l=xsqH+E3eL)?4ARz;(yjby$ z#9JBgdZQTAOaN7eV{&hXN)o?COwNYAtRO{wvYZR(mdtW$@u8BHo^U|aN@ceA0uf&~ ze{*%%OSO^WNr?Nhzp_QZ(qE zOtW@py(fxjOzVLTBK22SfHM&A`>NR8>ApZ?C8S*?6wo;t4zEZ!6jN_CUdGV#m`h-nN` zbC8K7;C;bnkKb2w`rI1!Vt~Y|+LeWrb635(Zp=^-?MbYw8rjP^D)c)BKaSu4Q#JXc zXp%%1S4^dXSK>iWpmKJTH{4b!jqMoOi3XT&sW?XUaF&-Roq*nu4+>=If-moh{`d)d%_;@of$=0<8TBB%8n)x= zZg;R>0@hu6qpb)QQTDDIz0KK`wgx$}@UAe*r48Xxsl2}mr)MZ+L+-jq?<2~oxP$7L^@5JqXdSLJqrrHYLUE}v)veWg1~H9e_0#aF zTCKzAQ0plx4W}`x9c~9Q6-8~D#;Hrwr@E+Iwf6w+K{Ff)DC@W znnt75A|H)b{?#RCtxU0Tr3kVf@0ps5DY zU{D*4;1_gUDmqnauR)J+hoE+eF7%s#raI6U_`{{@96DpFK`*seC+JgC-9nn0Pjgeh z8B*0o-kGY_x>C6`g9anQDA7-+M{5mQJ$fMx?bW5a5RW&g(?r3YY7jNZ(tvV{^3saS z9EI+R&IMxyQ(7Vwv?9e^QVKHymNpe7nA{~bPqLX*Ku||oBNebfRtDs?W^!}2kxQ-) zM=LKT`B#MHXNxE7{nvSCV(|sLP*eqRO8Q;|=WJCsg}qQ?|TvuM|6!@Wf1?#-5nK?`~ zN%Q$p1Kewhc*C+aCOzz_N()stMOf6oDmz13*(2d!)GKx`uLXuGMNzS%IMWH5c&XKouPQ1g~^xlD)d6_iLmq(=BTNO1!YK@d<1z=coFow?s4UO zrCxCPC3_jglgh~q)`q&3U5K1Ocxj%O(-r&Z&1z->BR4fb*2&c2P>7W77-OoN8E2v` zvE_$42(uUVMu9v~H)FmoKQyndxG_bX`YjnMKha5A3;3{l15?X-I%#M1sK2bIclVOf zht}Xy9*ifG(T6J7{5t7N!74|7req2tbyHdzsmO>RX{&O(WBv7aQ58-}WhIK8Ia<2A zSdpJsR7~y3C@#)V;ge~b;pzQ)#tg<|g??JUc8kmZss3k<`=_W_i+g8RqN}1_tmHg3 zlCvpNNwk=uYCnZbK%hXQA0|xb21w8pA$<<%kxJIGD~bHfAlDRO;~jA(A*E!=Q9%!` z!%{^>VbKuTEW-hI2 zs;xb>ekO%#Qq0oV#S&-A3oJq=s#wER6L?N*fRtB3mv0I-1hH_rNXoCcdt;G56NtGX ziQmppPqsVw_^YO0GF*kd++MRG+~=ya4#5of3%<1fDWV$$u^`FVKC?JiX3XFHtk zjJ#}@yAZb&FsV>?O9f9-RaWMQy|5;DvqN<<3FY2Hr%Ae}344J<_!m&9u5rsZabdcQ<$wzAq4^q(HqL36q>jc_o z=f^J2V&qOIFqWZ?WPqa`_@(%pj~(*tyjuB$2FO-P#$Z~d6D-Q0-4}-%yumW$N;`If zR1HP!Ze|SxHDX~x$&~DRSyD^8&j0AXexTJ0?Hpp~2Zcd13Xw#=;_Ja|_rfMAcSYKy zYhGKi%KVRk_k5?=l=q~kN+i;goDNGGNMqkttFkug@w{eZh*@`N9>s-WHmkuzbso zJt#|Vtf^v!Uk;#O5Bz8kX}YsGn>MXq*H@`Xj(S_8;{vS1RX7(Ln{<CHtL)#pCE81zh7!4Vy+`6HW=ki`BR$of-x$3AX7|!=$v@Q^KSJ zrxk`%!^$bG<MK#iE{6XNf$DMGs?q^n$;N zoo}mHdrBQ7UQ%pQDwa^&!;x_HII==wzx-yzqr6#|R4)q0r(8M+cg0ZlU%!7c1)uT$ z{?qR7JT3iw2Tx6Z`uz)z{Qdnk$4|X~di{?5eCGR8mDm%HA6;^LPEWrW^1A!|4X4$A zYL8RzpI-k-@qd3mr{~|_Pf0rUe)swX`rw^3{JPh#`~4%>w{{Z0Gu>Y`0`K_Xr$ht} z<7@RQxWFgG*Tl2(MXn>$l!|q$!JoK46!!;ryD!H(6E-7$Dt^=Oqwzu4V&Lca-HYE# zv9PN!3$``Fu1A__p7e|xXFz?;LfM&6#UTh;wnyP%7M>ZP=QH_DRd~FUSz0(0z)}@Pdd!>h zeayYhE(|TYSwyNdd7v%G_hael(TF$PEr&DF!* zK_+7C%hz%YUM=FOy2TT2LOR4FxglR+`rZJ-4s|PI1;Pd(?SXFj?L%1K^YI;oZt;#F z?8KO^yyQFy|NCtZe1qe<|9;y8)6QJthyQ-tqwCuqIZB8f{Ks2^8QEnpL`a5=cjB|@ zQFU6_i?=S~ATx5LofTwSAW7wbKB!ECTA4;0Y$y~VnenjvG)hSnLJ!!IvOSTgw8l~> zhEN_cvrKGtfDO@_H~?scnUVglJ>nbw#4ekQl71J;p-ex=`Otu+1&=`mZ%^-#ZauF5~#T_j=?Gc?QkfCoJt2YVVxWg zafpTr#bZjMV{D(BVo$8}80FB8pD6{kiz*cBky)Up-+Q`!Orh9Mak~s=iX8IGQ57Dg zyR{KUN_Vi?7Y+pjFd(pb2nM{#a!f}Q2OYu+=_RY)`dDo`AFLrPK>b}3te32vHTa4N zzJh>dA4(ysX|H&yEp|0H@i;n)cP6`P)R{?{Vk>yAb8jDJ!AnrHuEPMgbieCv#r-J~ zrNO6o&7e?nmwEhf52MmzMro`?r8_0q21oJMCCJ}Z@diAN^`WpAUo(ugr$`P$d$#sq z({C)Bm|gNiC322EM9hXI7H`y2S+Vt1XH$^=9jS$68EI-{i8oV@AQCJLH9qOcM^B_d z!7JTl*>+-cAnm$zHL)a0jV%_!8QvePVNtB*um0Gbdy96@i8p(36fnn-y5d=RkX!U zVj=L}OKpq7)Lw>tLeMh9imHS{k1qmo7#rNsUfqSc?riyK$jZ2U>}ZKS zFTofXD9Lv=IPh++bhn_%C&y6qji+Y=UGb;HW%>MmszF$>A}-(SZRonUJia*W2}bD4vfmk@kbJ7GdYEBn>1>ixiu)+65tc3`!|0uEBH%q`U<4b~J&;?tL!%n{^LNmYw z)^d25Z2KUt@U$2+<4BgXi3H~?AlFP-bbZy>gGhD5#O|#N)83?53qCwG2WcmFk(39e z!wC^A6&|(WtMOF&pK|E0!0_`_*RvkZ3^q1J6+c zIOoC@_OujZP~nUdiq8ZY4(AQ~s!{S_s)Yk0y0hCt6e8z=7(GDSTH;cv%siGX z=vO~?)qqtpHs(l&ane0>YC|*>s}f4$ALUx1&Vuq ze=zI6e>VbhKJ`o=5c&^R@;^}28QEX}EBuUXp!yF~?mt(=|I67y8Am6c@Hh{Zfj89tuUl?n`#76qUPY?e2U$*qLbF`@25h^D9H8(zpM-`|`mV|3 zGlb`#A$;Z;!W+*JJ{RHA{tL)h`D)yb@5sv@VEJ3v2h%p#FuwW>3#U^DTy6Bb9#4tn%DdA z7x_u=e3jql#@{NERDVi{fX!)%b6-zS?4X~VZdXnsrR~_0-@9XPVegIuMZG%?7AJKa zF6#|e=(=7@@7?(Cr2D=!spCidj+G~M{5mb^3d(`%Oy$vU-(5{KP5kY3a>vshzfDW( zI61vf2esv$?A}YLja1iT+n)aWwBPONIN5lyl(u6=-;R@&{g-^4+xs24jXy;79UaGx zNO}D8v8$wd{&Q-QRGwTWl}l;qcVc>XI;CelghrNU&qNy z&N@u#h_0v5rq$S456VL>PsWo{TBNV+FO^AYiKcR0sjQQi{yC5C=}vkEcKlH@xZ|g( zNrx%jDQRPEJUtI7eJmZN{oOa}Fwx+2+H`O0>3LAP6SYG-es37v@mp~0JJbhqeCnHy zgM0qI57914`|IorI*vC@T0%U~Nf+?O-(7#av6`i8p44&dGHu5{=NeSRgB=xpujuR( z(3EH+)8|C}@UHTZ?w2;cYolD(&U9BhJAS-wip2X= zcK3_gLiG6EH{uf;h(RxzkK-G^dDzSF zE_yGLecg~+-c`=2_0!_5L}h3DOOZZ|9JLu}YkpO{ddR3fg^6CwWqB>!cmu3Y^uh>Q>kP6`H_ijlBKHrz_ux@yXwUDlGFZp+HoGn8GO{cXQm%ZpBF2OxQSXO_Xqh{PoIe|*P;aQvV)`~Ho#RMt z@80`G$dkUCNqg$)4vow(knQ|$jP)QZBp#<N8=rTi#=`ob-> zmiTQT>PWSrHqe>Dbeg9sRt|kDl~RvFuTVWH2Wmaloa#q!IM6ArDk;|*AfqSwX*5wS z2mVD*5S1IG8h9kWSKxH|bmT+rCYp57cl>E>qa5oHtBZ4~UUdE`otjIuR)J5lAxBlx z``0 z5H9w1d9_5tLfn<cQ@xK)RIajPN(y6=}WofCyuA_%XpJ0 zu7Z!=a;Lr~TGdFo3q@E&PrWEu(4xKSeIWgIE_P^)_oCt=0qfZ+{#GfjuM%1tW znv;iNln}*Qkd9=WN|MENMlPLWEB6i6lD&09EtqgxnRIq3#irD3lu`RBhwk}OkN!2c zl2z%y*Z(mJK!ZwD;#np4KKBO~#}DC0@;oo{S$sb4;cNL8ehz;49ezmk8Be~CZJ zpWufJrJ~&L(O*cJo75^OXj!Cd(A(Y6_z2E3oXf(9E-;?$~w+kYi+S!ZN1g{kafNF zBkMQTvu(p{=i3CE(KgLC)7D^%+7{U!wXL_kXZz8nw2!o3Xcz2ed%oRoUu?h4{)qim zQu*KA+9sg!aGAf53mu|Hyan=L!=AhfpBQ z6mAyQ3NH%#1f@o;F=@&*jhb6E4`?=MUeoN={HPhAov5t@UD~zRYuj}<>h9G&q1&q4 zt2?OsU3amVCI-a&#aG0SK%-y8KKhG59f$r#{nPsW`W}X{2AiS4P-kd2+-7*fu+8v+ zVV~g-!#Lwb#$scG@eX5_snFyz#hb4-FE{TppJkb7(OEJr<(7KOLd$l`*On}6vvra6 zPU{Nm{kCUqqwNO!9rj<`=Dvmd24O~yAN07~vW94}GD&EghwcXC^~9o#>-uh2#XpTrN~Rs2L=;Cl(f1%q&@ zaF>v%S&deGrcr8pYWrxly20W#=#O3cXN~U|zc3y${%$NY{bU+$zQCM`x_Zp*<^|@P z&1=mY&Dqvz)>+nTt@V53*mv4Lwtr_oY=`h8#R)X; z!JW&E;3jcASHyX_C^w(GgFnc$}vbl>U((Jo#tE)x^=Y5F#dsT2C%hB1Z- zI6`5%A#9j$c*gLmVYD&DSZKV$c$;yB@hRiW#)HP*Ced_^ zFKoZsIzWRVcALEf{c;uhmaO=WYD` z{2G28#@5T=ytgsFKH>ME{m1zPVVJ-RsX_)uS-Ehl@UpO57@?V{v1>{-D>VBxDcUOS z^V)Z`=i?NJY@JsZ(rwfgiVMZ(#Vz7n7*St}2gPGzhu9mVDpUV}emf-1ml#(+=@Siu z4Z{s8!$ff86hkSfbl7m*&|x^sc&>4jaS{$vxx^SX-eg>7eAoDiakxn^IZU~xg{G%X zc*N{O7zvND|V7dxVFCr-WC8 zZ-oxEP&l zxEI0AUvlSR2JwQQkMLIE8c33T!as!;&5fFSHP2|?)a=&u(ehe{c9nLM_G|4d@m_I_ z_z$r|-``*~WEz?bR~cSLZ?t@YNg ztVgYXSO?h7v#kL24%;rYXMh9uKq5%Iw*>D4OJlDexG#sRK2w>+U$xJ_6ke27`?SK%CuN;4kZCTLumY)z5IuW8X-uX#wbMf0I%kLDLm zf_9*GxORc|G3^)HZ?$LXGIUdQ^}2bwmvnFGj_FS7;>CXAg`!c+7K_E{VzuZKBjTmv zwc-ZMh@WFN{8d!yd+Yn@hw8^;Hk_#s>MzsZA<4-t`hkW~hJ3>;(BTHda>HuFCdmGu zK#@_#@kW!e!00iCjaOnMZ#V8Y{$lKDI?FWDG|8kfWt*m%BIw-*z^SjA-Zgz@`o{FV z>A0zfd8qj!GiRP`PB-V8uQG2ke}-AK!<>Zqq5@poX1U+8#k@xo08eKEfymcP_{D2|CAdXK{nM(cE}Q zITL2lOi;X@3qsb-!Fa!gdy;#e+r+&F4%x*Wgv=Yjp9^hc98O|z@RfW$AH?i42Qu(h zaPww)Od7~w)eE$9ThP%K1*Il_El5j2oHh1J57!bZr*Z-j%wkLb5} z%vK^~gj#dutZ(El^DjMxY_A2}BcKQGk&9@})Uqt&q=MF)uu<>Dx z+a3HL{4hZa?t4qvBa~@oL$WEg!?YJ^joLZdE!tkX8l6{MATAeQ6kYmh`r8fnVg}7N zzKZ#2nCSvjrD=ibI@4Oydei%+k4>Lrw))L9z&ye{1|wT-)vu0#8`iYmx*QE%0^>(z#8jF8)*QM_UJ+VHy}&N$L|fw9QA+4!Zgz;w6i5z`v< z&KAsXJ567h_L(M_o6L8ZSC}6&Z#N$>kFrd*WI;x6w;ZwbwDz~2V;yU~$jVvgS#Ptx zicz4pO@Tyy)b@_;d)rZ4GPFgPeTuyd8E(ublm7B+{248>9eZw8) zUc?OcDJ1blLYWW{nuW_Di+>i*hi;e-IdX;OH_b?BPB&|hYp3fXx_fl%!DT<`&J$-t zI$R~*DBdB8`d9RO4NK6cFPpZRJ}@1`tdnduqaX9krDmVG#eA9hO7k`58_l0YZ ze$@P=d4u_7^K0P156z#Ozcc@gem-eVw47}jYZ1`jS(cFHGRu{gYb-ZPbNd69RhG4u zXDu&TUbXDNy!5GMFF4_-s)+%e*+G?F|U21*Ux*D_AE7mtL zZ+&FlW8E)l*`sX}z$4|D!(Om$wY_EAW&7N=7c{#Fw5qdThxy?dNQp1(`|OA8zu56Y zkMs`tAkNBN#XSLO@Gf+-6Wl<46f|rDKLe|c%dpn?fp)`txDkXfajH1kFy0$$Fc9ivDhWA80+R3~LRa8@@60gudf4mKv*#bBwnd zpEm9`9yWFu`Hy(zlA>xjfR6RQ!e<0+2D&?B+2!R@E%6}LE(sSOcHAJ1P5z3nVvuyBEpB3ve1CESQUS|z*y z4edB2aFxb~RZxp&0d(tSTBR;YH&&O2emkI3h$F-c#A(o0AHj@&j{Y6vK4T?D>T{T- zGR$7H!%~1X-Q^aywcPry^%Gk)r00G1C!tYGbRVFUzHR5?%Ai4BiDP=}g^&=%ygf&l zi+Otl=In8r37ScoTXZAD_2Oo6H#ELJh5?{c3ADUs$j+w>Z$n-THcl`ajc%-?{NRq2 z(DM!&j~I^`2bhLKrcN|*(38qdGflIge_Um{5qk0k=80yjdAd0SIWo)w;$Xtt1Q?0Z z3>uYooOY-7ZahyE}jq- z`UHKVzOR0OelWC%k$M%@))VxTu!7X;4SI`yGS=A{`fNSEZlN#NS3t+8(bwz!`bK?J z->RpF=sO|9_F#o~KsW>rJc_r5L>TTMG4n?E(}H61b?HJvaenERRsn@5_*nJ1aGW((w9Hr6E-<{HdiQS%(k zV2jO5A@Nq2SDV*i1-8|^!@Lu7?q2gD^HK8&bAqL>Ww2$WWt?S_MQgEGT$XH0G4z`n ztS+ON@fKJXTb4pAUxBf`&ax4jq%8XmLH3=1?CWbCY#oWYa1yjji`8Y#hOS;=t+D#8 zQLIcCSQkSU+>O=rYRr)vty?iq?6iJr-3vYbC^Y#5TVLB?+eq6u+a#OTX0f@TM-|&D zY&AANED&>S3v7#_L*ETeb+v6BR3G>XmSds66Hgg0cQDIIr z4}i|7GEackXn<_YFy~{HFwaY@P;SJCyx0&C?`e6@d)+3mE z6qZEG0LxH|$}+*iSqzwKGc5VAOU#62qS4Zd71AQh63a5na?47{pAD8xSZBTqdAkSe z%p;IQ3TvWufOV)Gc9 z%t{BWN36%K3R|LWfNiKvh1rR-8Elhn8Mb^#x0$wjSQA=pb72o!0{wQmZ6#I>8*H0w z+idS*?%IR(*Ad%so5G%GA7CG9SJ@}nIap05L+{Cl7Ch5lk9B^leXf0xeF@gM%k3-e zYwa8Co9x@{@7i}kMjfypu^-2pUnzaVRly}ToSO-)xDk58 zTy7D!gj>cfhm2hdEou|D4Kv~{ZVzUXBiwP=EE1t34~5kNXIf!?oXlsyDp$_Wgs$Ak zxAJrOMf?(e8NVF1xwTlwY{ClqUEXENFy%vfHJVnUXSbqncc6D^Qwr_xw&?Mvk+~(A zIu~a?AV`5)PDah#BbYr#eD zayz+1v9CBl94vO1gr`eEI=^D5R9*t8Lwq_EoT}m|=)c#0o&u9AQD$76f}3B-_w0S~ z!ive0k9$*7C=i?|f^`r6U%c zwUrzF^N1COa}Sojc6(sdV^6jHw0+<|_Evt_!&2D4aP({Up7YSrg-Nd5MqOLDi}a(s;E zF73e{uU&Fz`tXeh-+1S()!NCGPqx?o(6TLKWo=p6eZ}QB^qu%@-Xq+{r9W)HbjH_@ zzma#zH@f}&o4aqhkx_;JBL;Qg2ckR@#NbYgjqGR)I z18!=3tYp4J_~?^_$i=~%lyk;p@7%a%M10eX*4mFvyz5TQ(m*k%~xM@yRSXt$estz9#MN|bo=L7=k4!T zy=LFe!#Ce-y4U)tVy}DrHP77X8aDCrO-tAP!xK2D`S{f@vJwJy6MITp1eHif;*1w^`$Hur9SrYs;5SM zGa?QnQ2F3|Wi2;fS;h4SJB=EoRCXlv;Nk(?C<^G8aBjljLmz!T{~xnw)MS5=TxZ|^ z{IWG8`{zmQH|qR^q1?H12R~`s;)_52a=LQovI~YM#vQJ%nYe}PN9p@hh6xy<3#IWk zYJ5UpE~#fCBtwrLz2f7!D`dA)nGoS-bK_(`r(8Icjt<2Up`q#sP7v)j6uHdKSaA!j zu~^kO+btZ8oDwlb9vd`JJIATJltzftL1*o9Syao>2`(;`o4nk%+_KOl=NI;C{%4u_ zxsjBaDgm;E>(#SI-MqfacrLMLPjVzEyN^W(r1<$trNZ&!s)0xQ4@z%)!#nHxO+PG4 zSn=A|eSdo3$^Dg8dAo0!D!%p8d0(D)sQ;HE>^JS)+y9k)%d{QC`qxevoma)LxcJM9 z2R;9h?e+I=KKa;ZUp%@@lQuf{-WT_L-z)9l+~gI1tSMTzV)Ke)W3F%5p?v(`hJmpxn_9R0)ZKF?RvKAkY;K=t#s zcS^s%bMHTUe7F7QLuY?scx(5iwu!eCrBoCs%0FL{@nF*HeU6#~CH#@N9qBE%6+C#! z`i&LaoCEKFgqz=UEoAtzm<;ciQ}>JW+&3;Zw-4EocKfUwPb1lXL~4@c=5Yc#K4v9L zZV_G_%*`aX?^MY>ggXbFj(Mb?%xjZqB7wD%v(t_EK?eDTN*2)RS{1#TsiHS^@xrC5 zu*jk1jH0S21IMU~DuEMt%pv^06yR%;`(F0`;rg!^B>r|+-<+St{`;T&Su0$bR$Nv2 z-J)}z++P2B&d8ddA5b7;YN74?yck#j1S@#`0xMs#k&qMeAIRDx}-m&}p1`e-k z-R-~S&;-?|XTP!awu9|rXvoN>%X`1^@k1NOUwY;IaQhzhcbf-X`qH9#=g*z!e}C%9 z4_=%Wob_s@&{CiBRYk{dBd=Zd^b4buZcCr_cdLIbyK7FQ?d_*-+x^sp>?gy=M?O88QZ2$T>jES4{#gbP=CGOYW#J=@Hp+_^S6$kbxCQw z`kHG~w!Rek_=?h}=Vkp|mA-x6JYnBtW90jd^feW^~-^aTxOf1?YX6{*1GDISM`5{>)2Ch z-v7Y9<{5V_`u(C`2Auo7%cy+f!+~qQ-x$0rNvrBJck1o!HKAPQ-W&OHf!FxVgfKP=0FX7H6;Ufv~M6M^qE@t>{GJIphSnoZH>-X(Dug8zO z!sB;t*psp86dC@PLjK>9;VrT>h751ueE+5lcgU%@xP>}d=Kh@wkBuIZ-RW9Mb~~xX zqZ2H6j$l|WF4ry;RDSo~D=kFHA2MP|4N&oo$ zmA}va^2BrbfthnZJ}cKD1+yCKZ*WW(Kx_jGg4eGID z_ilf)#PpnS)`)%&jI|#)y#M(VxmgEpe(-Siq-{%2u2&wr_UB12FU?Y3GInBP%G{f- z`sifszQ;~{IO)x4OMl$_!TZ;yjoEeY?&|Gxo_Q{Jg(p7Wb?Y2WLKR-x+{*!srh8!JfAMQEQG4!6m?d9b|&dVM6?T0nZ z++EY-S5N%Bvf_AovSD-EhaLH4H(YV=!1b^+O}b*sl(EGhjVyg_*h60~`(X2dqf1k~2ZGtXmi3>izGSZR zrf0LtmHaht_IT#&vl|vy>sIBRJa_tqWgBbLmQ3J;`?nXj^?PK&>^CoKJaV(6?BZsf z`Lic(E)%DH@#!G7hSZ6J zlAJh?0H+iMeI9)4H`|y?W&}&Ibn+I{`IZctQw0M z|1V?y%gOV)+EvSM<=0Gdy|<@+$-czYH>-X;Rv|Qv{NTZer$l z(t1B`x&C9@75zIrNy{3Ho}wGS{dm_$>qp;dyXfQ6#C~gzWWO+RgR<`KKJ^8|duG*D zKbtdk%QMTf$9WFmb&l6VWSV6bXL9EnlkhKokMLWF1+7tPk83J6Uxt?AMPFh^@_pk{y2Wt zb2Ijh=n?m5X!mckzIZ0NpybXq?GL>6di?lVmp(k{jg_|;-uiyeiSt80z7g%Y%U08; zsrKG2pLu&R0BJsO#4Ges2?2k_*-f{I&ZvM)*7oGKo$NIqRQa|tR_3ZkKj=peS zpEVDwCSN=5=eh|MFE^&B?oacKHoy4PmV|dxZolKg%hrE$&cxi!+QV-zy|d?o)oTk5 zmJG=|mi?_TEJf3Cx%70sF+p9;86h9TZa{e6s(z9 zbIpC}b-y+rjvu#eSLG7Vw0Xt-_Hmn+g^ zlV@BUCeM?n%!GeARd%h0ZutDCkAM5|Vk~_G?jpyuh zw)dR->~rsZ7XR?9ftj`7nVI+de!rjJI|>*7r(ci=zw%c(d^3ZHu@KAhdDJo%kCe&Kz;9d{g!tw@TrZ0ou_TE$b z!dc9fAwWU0t0+>M(-6dOgLmtO=Utno*4}Px>I!|PLpp9X$lV)S)jX`#15DBpz7hT$ z{?^KMT2c*`SK8#SUK@%;2O-zfYbuubzmD2ls<^ka4jX03MM`3`5pNUNsq!cpRY%>> z>maY}@C9s~b$L{ajMA1^GvN_IIMI}`y~ylps`9u`DrSkUNB_sij@RHOOG)Hl$e-s< z8c&B$;R^>oPJgN;7D-4^V%hMdx$2{O}eJWC;JPQEdm*b@%_+E)ECoIZ+Slw>%oaEL!X#0%hend6!nb|3H0qn zQ#CTxVkBMLj>QQ`Y>l%$tkFRKRx6BG_d$J~hjbo-rnf?Uy0pV#>e%rKWqO`z4#p>I zsuDQUsR_0F(i~>qFnZ$C=($v4@789{9&U%9w#ylXFG=Ka=qBF*fdWT^i%=T8+lII~ z+J>J^FFDcWM+Ti8GutF}OeBeftOrc`AinBpRw|+0mPoFUJ`Yw9B=Mjl>F( z5(h)1gaL08{0RKl6>+%heUn$IxnwTT@;O!JQ?x`Z>wA1^_LoTk=h1re@JhlPA^2Q?zk5B#$c`h<%tQxj(asZh9LdAlDWqC=Ak6?{+VZi4CPiQkhTZ=HQyhac?+c z7mHKqb*I~nNaPq+R35W5v*wZEqQM&T481}WN-?I7cBb%(T$q(T>FR8JA~saoIwe-t zaOh5&;$o6RVi{67M;@a*XvtW%YCh?UUKwzf4}6+Yr5@I+KtzDtVPHX64Z9-i!J4`z z_=0#U;iv{ANz{CU-a9HFdMY^ubo$C)ny^GglVjfE`fi>m{}9nx&KoL%sb^icmT3wj z#+(G-iB}LLxqRSR1cxsp-+Vv&RdE-kC};LGmL_wuvXO*A#D@wit!9U3!hR(ynyYYr z>xnD`@c)psm_+AW-F1ts1>PNacQts7|g}r-^9)5&DrT}5F zcBY|aBflAmwmU_*B^AYi;`dnVNeV=K1PsCB`Ejzr!^5&d@aTbU)IZMH^Sl8M7ua+I z(5-);H#lEK{qA%lUk3t*`v_)W6X4?gEtK#-wL*mbgPZ|A85tG@qZ18n@MHKB-Z;vx z;yK-R$X8m+7E=>VJOd@T>-L`1wZlvOREUGZl{J2ftoRr}xjh%zTwo4XPQc1fhF~EA zHrC}R0!`N3PCD}FHiHyDJ)$?i*5xz)Jc=;>Stb8N3yBZRb1sYFvDp5|Li!um=zQ2C zFw-1((lg*F{YW5q059v?*LeZ~L=XTh``yP9|M^3FSLG;x_Wz!Tuu_33etY|y7n{h= z-voH3jrGHGlZuUu8Tj^RDMR}|`6(7J1JpHh~v{Tyx)(&Y6JsgjUzT31G> zq8aWcB0KZrL?eMLO*Z{PK2;`FNmG~0zCoib%+ ztra;6+}GwqB6SJ0YWCa(Z64re#U(^Z`1`pEpy`=~5Ta03C*<@c6d%ajxHQt;Nh88g zxXM+IG{TWlL^A54LCmxCMdsP{XiwSc@%q>4o?n=6l740s$4?{J=Q-G`lec9Fg2zmi zu86HUlAm+u+_)+_r+UV@GVj5-?335Og*Y%>HKHqR!x~Bd zWI^baX=JU&{5%VNct(k#$-LGPUwn4c~GuBDw`4j}bxJke?UUNT_SK zQPbyX_p%T7un^4hnIovff!0UWrQau55cEI21WVCZ>`OUOC*I~Hluc*%>gUcxa5}53KI~fAd zr~a_h;lQYuFYWU%>>`aQ#~rbj^#j?{JH>$9&)CwdG|!dS7_Vo*Ez z86vSKl@h>O=lhc)D1#Nj^2xHv(*BYcdh^e$BnkGP#Q5dQ@LR-S1@;NQ(6Q(G_c7SV z`xo`E#>IYs{*}4-JinxW|M^F9_?sKjH^Hl#!$7zrzT=|K1wL_{l^^n>+&Vdo;)$nT zIIs(jZEzkiIl(#Rtd~?|atSn6MHc6QIWZq-kEcIQyHnc+6OTol9~(60vfb(7*QuQN z)@a7iEDf7MEI=w&m{H;6W>FFn82M<9c^Em!^$>X>u96|U4EOX6^FYQ|xx^u4?3<*A4K+DRqY!MeTg z$~{J>)w9@lG*R3Hfuh*+D4MC3ixdK)M5t-ou<)`q8)r#%Rsrf_z=N90QsHB60X{8rnJ< zj^nZPEs9#EcCIvNPSP4NP6)Lrmz}X+zqe1r7VG|1M>x_a5|WsQ@}yZBN2)(IT+HB> z_Y~#C7x-8EViNORrSQTMOG2Es&aY2Xmtq{`SVl>RL$q2+;s)zCn<=v7{Nr8XbBAWonb?O8ukW=Vxqs>T8NxnB~&UNA0_fm>C71PwEyOm z`OBghHZVZf|6anlfR}6+CG0P$qkmT4n*BMhO1`h-x06&H9}M zn@^5}2Z+PCe-af68uPGFciZH0eW{z*vE*w~sI=`QabWnEU|hdY1(p214HtQZV4nWF z38_b2dptMpZJ?(cTg_!O5qEN4(-~a2nN?@vAedQe*HkV?T2Q1R>-u##%>H>E6VFWg z-J#8VR;+Aw`e<5~y?~4`IVT)dnuoD8UdeGkPRkN9TyIpt;|E?D{dv~8q?nXTSSM*X7h9gHhDxo#unkxxrl1d7@iaVT= zvQNnRnAAC38MAi83@Rx@H4erMC!T-hsXcrLPL~;8j+N37=!Xyd`aY~*Pul5*;`6o; zG(PQMimW;sq(kiMH{c)XQ&yPF8c$_R3+&fByt_k!?DRQTSKM*NAWZ`qmJSBpS7p6x zS+`XWe4|P{si@zntPkDt1zO*&!p?$=`lbuiH_bl@Vg9f(T`pb!Y6yb_{=Xf<{CR=D z1hRY!&jE|(k>=Cd=fw|VvqdRjJE4*G-JLEMU<35p`Zid)PYQ$d`ts6mVbd;+@~D;;BKqb9?af>&5}LY_35M1EqypC$ zK51(HxhP_7lZ~Oti?PSY8Hsjp8(Zq?ENn-bvAmHRi(hsI1W;ATr%9GMn?2jIOIVhM zQ_AG>e6F0wM~ozi5!uP(HhxBJ{DPK=|BhwmmRW;Pr+l$sJUO{wPYD)P=NPdI?_ARB zd3M`~VH(RhZu{H}Sox|ud@iqLULQ9`xv!z<-{J`na&hBcPM992JSC>d*A3BeQ2(&qyU+juWD+aGRn)x^R~lHtnJ9vj@lO))DUSFPox z_s@?_W?YLBD&ZZ(iDfM`LdHO;@)^PUTJx%RM}4Fc;RrIpHi;0l3Dc=tf7nCm1%o>0 z1V=NnY3d}LK_7rD!MNZ>_{tCCBsO|qRfUn%`>1vsopFBXI2$C2BejTyPW*|Bn=plO ze{IO)1PEb5fr{mL0T0CEKRxVWUflSkFoPtB|5Mk626SDfKQ}!8eE{qa`k`-334mNl zivC7j{ph>6foAhZ-vuSVpyb#8O7e>rs9Bu<7WpN%n)-G(q65OptoS4|lRR<9x)tN+ zCJ!LLTrc_(3BFaX zhhgG~S*`nU_aG1K7Wd?x-avP+h-kbB`D(OX%H6^j9f|UpSK+aoX+;MXy_PhyHfunr z8Oz)3w?7}iw*+MM$p{i~h+CMe$XJ^lP@kbkLQ?3C!^uCtboO2~ta6b1ehC*=FS zFCVmjA`9g(AcBMMAj{9wNJbYlZi=O-d#AalYS~SjYvt7SfRcIF)7FC0lH%+}sR}c# z=SSX=&EkOQQ|(@zJp2aPm-Z#F*)P>|^yp^FB3U8uPGAVU-MMG({Fk^~aM%CHYx~J9 z?*$Y7un+;a{Mt`8Az+tZ@Uh5OSTVlm=NHxluF$Q4Dx9m<=v(W{S<<8LXRueX!$S=4MF`M+rcsr^mQx+8;U2es`8|bR#&znx4 z6W9zs&4c}A!0C@Stx4i>5aAC(7w{>pc>>IbD*ZVA3Co^Ul zOcP?vzBg-07`&w)emZVdy{}BNO799YE&KLVN`P5G78KeV*30%PcPL-^OfiEdK#Iq{ z>7l?bi4aje1~cNF0JF!qxfn8nvu!Qvx#8t0=@LS6vSFHEI4UM%8N#b{-W5$hoI7bK zDUWesTjCYVpA=fh=b0B|;X>5J%2w6qu}-(8$h`BqDk}@q6jS|x!f5TPdd@h(M%WlW z@rze9JP(7k!6BdAquF2hJ}X;LJEE?CA7E4a;E|$D@}SrNOPQthfe0O=+I?()NMt^4 z!}4T|NV^(8M9EwWUXUmsA2K>eM1L<*t1XI9lL7JA9FOoh`yh41P{I8v5Hj~IkkWe= z?^8VLT#SSDmLqx_7z=o?sdh-b|tJ3%XL38?K_y^LWQq3zckZA z{E5z3j~49xt4CJg?*LDe>&4X&qClg!aijXmG+Eu!*tbN`ws8o9JV46>hJa#!nCEZ^ zfO!rI1h)D9IJ?iybC4sj$riXczX_o$jOg%ign%xR`F%&gH$T5q{zFio90*M>_CvsK zy1t~qNmv?;7)xr)Z!D!&kU;PCeoHW+U_KPghl2S~Fdqu$L&1C~m=CP4hJyJ}F#rD{m{0f`3?a-u z4;lW$VE*rBXtGZekP)F=0LTOgtiZ?v*ngeVr~ILAkr7ydz`<1OO7L_b>qa-EeS3bJ zPOt6#tby+kG3faZw@Ph4=UP{))$SEHgZ#BC$aZF z2rdLFSH|#!=^=y%Wwf0XHI+@_But z`asi>-NVE!GC_a7RxYs}nVgr0`>P)h6xbVt46 zlgqTFLJ-fdmpNRNdoL<`QaTfz%SfP&hnR6xhe}U$pC?(&ydH(EMu}SJkg(oWn~8AGKHR%l!8x*vbE^B=`=rZ3W&>WWkyW& zOswmMO|Gvf>~az8O1|fWG>*8!+8*3|u=BJr4F((Wq;eIx)!2z*`7YC0Z8P;1_F>#c zMw)(V;qep*9{+!Y&40?nUaV&~IET?yL7}_k1m(HtX$PEP9Seq!^vfT=rBHvgYb0D`jlP&WU30uYqVzk^Py}$l+FKb)ZH(p zF;F%i%H~7a{68CV2W9i2Y(A9DhqC$SlVPD#S)o%|e{m`+l+FKr_VSN8u241~%I0$b zqc^}>{~nv4tlSuh^EQ?Cz?F}nppb!)Jb~O@laI3h)k?+omPn+-|0J6qfIu6jx#!P+ zLeV|v%HCq!Aill+WVz1iopyc6mz4Wx@P`lPI3-_yQu7e2{%Ci^WZ0zUl3J`|M~INp z*``kjms5m9HZtMa7B_vf+70oZV>~-Dxk%|7wMBcSY`3nlnS6oBE>8oj5G9QS2mYa% ztD8Oe8BUt*cocAjfsT8nZeQBbv#U-mj%*?_vWx6sFrV6MxO0@o8hn*_94#M)hP+7 z;&(ku{e_Y*QG-LwD}4u>D@QlgZtyH_ls+(@an|%@OI;Z+oIIpww7V9Yal@10Xs#;> zt!T9DtdXeSVN_mtV(rV8EvkNryE7{B=7V@be2^)4slAH^<|A4;yXaf%#&CN114nqc z?uu;>@hYi_@% literal 0 HcmV?d00001 diff --git a/boiiiwd_package/src/main.py b/boiiiwd_package/src/main.py index e9edc56..418a8ba 100644 --- a/boiiiwd_package/src/main.py +++ b/boiiiwd_package/src/main.py @@ -891,6 +891,9 @@ def run_steamcmd_command(self, command, map_folder, wsid, queue=None): creationflags=show_console ) + if process.poll() is not None: + continue + #wait for process while True: if not self.is_downloading: @@ -898,7 +901,7 @@ def run_steamcmd_command(self, command, map_folder, wsid, queue=None): start_time = time.time() self.is_downloading = True elapsed_time = time.time() - start_time - if process.poll() != None: + if process.poll() is not None: break time.sleep(1) @@ -927,6 +930,7 @@ def run_steamcmd_command(self, command, map_folder, wsid, queue=None): reset_steamcmd(no_warn=True) self.settings_tab.steam_fail_counter = 0 self.fail_threshold = 0 + continue else: process = subprocess.Popen( [steamcmd_path + "\steamcmd.exe"] + command.split(), @@ -942,7 +946,7 @@ def run_steamcmd_command(self, command, map_folder, wsid, queue=None): if not self.is_downloading: if self.check_steamcmd_stdout(stdout_path, wsid): self.is_downloading = True - if process.poll() != None: + if process.poll() is not None: break time.sleep(1)