added a bunch of settings, updated progress bar to simulate the download prgoress (not as bad as it sounds)

This commit is contained in:
faroukbmiled 2023-08-16 10:05:59 +01:00
parent baa1ee45b1
commit 1c3cdc7cbf

View File

@ -26,12 +26,14 @@ LATEST_RELEASE_URL = "https://github.com/faroukbmiled/BOIIIWD/releases/latest/do
UPDATER_FOLDER = "update"
CONFIG_FILE_PATH = "config.ini"
# fuck it we ball, ill remove these when i finish with eveything (and replace them with none global bools)
global stopped, steampid, console, clean_on_finish
# fuck it we ball, ill get rid of globals when i finish everything cant be bothered rn
global stopped, steampid, console, clean_on_finish, continuous, estimated_progress
steampid = None
stopped = False
console = False
clean_on_finish = True
continuous = True
estimated_progress = True
ctk.set_appearance_mode("Dark") # Modes: "System" (standard), "Dark", "Light"
ctk.set_default_color_theme("dark-blue") # Themes: "blue" (standard), "green", "dark-blue"
@ -179,12 +181,15 @@ def create_default_config():
with open(CONFIG_FILE_PATH, "w") as config_file:
config.write(config_file)
def run_steamcmd_command(command, self):
def run_steamcmd_command(command, self, map_folder):
global steampid, stopped
steamcmd_path = get_steamcmd_path()
show_console = subprocess.CREATE_NO_WINDOW
if console:
show_console = subprocess.CREATE_NEW_CONSOLE
if continuous:
while not os.path.exists(map_folder) and not stopped:
process = subprocess.Popen(
[steamcmd_path + "\steamcmd.exe"] + command.split(),
stdout=None if console else subprocess.PIPE,
@ -195,7 +200,24 @@ def run_steamcmd_command(command, self):
creationflags=show_console
)
global steampid
steampid = process.pid
if process.poll() is not None:
return process.returncode
process.communicate()
else:
process = subprocess.Popen(
[steamcmd_path + "\steamcmd.exe"] + command.split(),
stdout=None if console else subprocess.PIPE,
stderr=None if console else subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True,
creationflags=show_console
)
steampid = process.pid
if process.poll() is not None:
@ -203,15 +225,15 @@ def run_steamcmd_command(command, self):
process.communicate()
show_message("SteamCMD has terminated", "SteamCMD has been terminated\nTry again if it randomly stopped!")
global stopped
if not os.path.exists(map_folder):
show_message("SteamCMD has terminated", "SteamCMD has been terminated\nAnd failed to download the map/mod, try again or enable continuous download in settings")
stopped = True
self.button_download.configure(state="normal")
self.button_stop.configure(state="disabled")
return process.returncode
def get_steamcmd_path():
config = configparser.ConfigParser()
config.read(CONFIG_FILE_PATH)
@ -237,9 +259,11 @@ def extract_json_data(json_path):
data = json.load(json_file)
return data["Type"], data["FolderName"]
def convert_bytes_to_readable(size_in_bytes):
def convert_bytes_to_readable(size_in_bytes, no_symb=None):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if size_in_bytes < 1024.0:
if no_symb:
return f"{size_in_bytes:.2f}"
return f"{size_in_bytes:.2f} {unit}"
size_in_bytes /= 1024.0
@ -299,6 +323,11 @@ def remove_tree(folder_path, show_error=None):
except Exception as e:
pass
def convert_seconds(seconds):
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
return hours, minutes, seconds
# End helper functions
class UpdateWindow(ctk.CTkToplevel):
@ -434,14 +463,31 @@ class SettingsTab(ctk.CTkFrame):
self.checkbox_show_console_tooltip = CTkToolTip(self.checkbox_show_console, message="Toggle SteamCMD console\nPlease don't close the Console If you want to stop press the Stop button")
self.console_var.set(self.load_settings("console"))
# Show continuous checkbox
self.continuous_var = ctk.BooleanVar()
self.continuous_var.trace_add("write", self.enable_save_button)
self.checkbox_continuous = ctk.CTkSwitch(left_frame, text="Continuous Download", variable=self.continuous_var)
self.checkbox_continuous.grid(row=2, column=1, padx=20, pady=(20, 0), sticky="nw")
self.checkbox_continuous_tooltip = CTkToolTip(self.checkbox_continuous, message="This will make sure that the download restarts and resumes! until it finishes if steamcmd crashes randomly (it will not redownload from the start)")
self.continuous_var.set(self.load_settings("continuous_download"))
# clean on finish checkbox
self.clean_checkbox_var = ctk.BooleanVar()
self.clean_checkbox_var.trace_add("write", self.enable_save_button)
self.clean_checkbox = ctk.CTkSwitch(left_frame, text="Clean on finish", variable=self.clean_checkbox_var)
self.clean_checkbox.grid(row=2, column=1, padx=20, pady=(20, 0), sticky="nw")
self.clean_checkbox.grid(row=3, column=1, padx=20, pady=(20, 0), sticky="nw")
self.clean_checkbox_tooltip = CTkToolTip(self.clean_checkbox, message="Cleans the map that have been downloaded and installed from steamcmd's steamapps folder ,to save space")
self.clean_checkbox_var.set(self.load_settings("clean_on_finish", "on"))
# Show estimated_progress checkbox
self.estimated_progress_var = ctk.BooleanVar()
self.estimated_progress_var.trace_add("write", self.enable_save_button)
self.estimated_progress = ctk.CTkSwitch(left_frame, text="Estimated Progress Bar", variable=self.estimated_progress_var)
self.estimated_progress.grid(row=4, column=1, padx=20, pady=(20, 0), sticky="nw")
self.estimated_progress_var_tooltip = CTkToolTip(self.estimated_progress, message="This will change how to progress bar works by estimating how long the download will take\
\nThis is not accurate ,it's better than with it off which is calculating the downloaded folder size which steamcmd dumps the full size rigth mostly")
self.estimated_progress_var.set(self.load_settings("estimated_progress", "on"))
# Check for updates button n Launch boiii
self.check_for_updates = ctk.CTkButton(right_frame, text="Check for updates", command=self.settings_check_for_updates)
self.check_for_updates.grid(row=1, column=1, padx=20, pady=(20, 0), sticky="n")
@ -466,7 +512,7 @@ class SettingsTab(ctk.CTkFrame):
def save_settings(self):
self.save_button.configure(state='disabled')
global console, clean_on_finish
global console, clean_on_finish, continuous, estimated_progress
if self.check_updates_checkbox.get():
save_config("checkforupdtes", "on")
else:
@ -486,8 +532,22 @@ class SettingsTab(ctk.CTkFrame):
save_config("clean_on_finish", "off")
clean_on_finish = False
if self.checkbox_continuous.get():
save_config("continuous_download", "on")
continuous = True
else:
save_config("continuous_download", "off")
continuous = False
if self.estimated_progress.get():
save_config("estimated_progress", "on")
estimated_progress = True
else:
save_config("estimated_progress", "off")
estimated_progress = False
def load_settings(self, setting, fallback=None):
global console, clean_on_finish
global console, clean_on_finish, continuous, estimated_progress
if setting == "console":
if check_config(setting, fallback) == "on":
console = True
@ -495,6 +555,15 @@ class SettingsTab(ctk.CTkFrame):
else:
console = False
return 0
if setting == "continuous_download":
if check_config(setting, "on") == "on":
continuous = True
return 1
else:
continuous = False
return 0
if setting == "clean_on_finish":
if check_config(setting, fallback) == "on":
clean_on_finish = True
@ -502,6 +571,13 @@ class SettingsTab(ctk.CTkFrame):
else:
clean_on_finish = False
return 0
if setting == "estimated_progress":
if check_config(setting, fallback) == "on":
estimated_progress = True
return 1
else:
estimated_progress = False
return 0
else:
if check_config(setting, fallback) == "on":
return 1
@ -539,6 +615,7 @@ class BOIIIWD(ctk.CTk):
self.title("BOIII Workshop Downloader - Main")
self.geometry(f"{910}x{560}")
self.wm_iconbitmap('ryuk.ico')
self.protocol("WM_DELETE_WINDOW", self.on_closing)
# configure grid layout (4x4)
self.grid_columnconfigure(1, weight=1)
@ -596,6 +673,9 @@ class BOIIIWD(ctk.CTk):
self.label_speed = ctk.CTkLabel(master=self.slider_progressbar_frame, text="Network Speed: 0 KB/s")
self.label_speed.grid(row=1, column=0, padx=20, pady=(0, 10), sticky="w")
self.elapsed_time = ctk.CTkLabel(master=self.slider_progressbar_frame, text="")
self.elapsed_time.grid(row=1, column=1, padx=20, pady=(0, 10), sticky="nsew", columnspan=1) # Set columnspan to 1
self.label_file_size = ctk.CTkLabel(master=self.slider_progressbar_frame, text="File size: 0KB")
self.label_file_size.grid(row=1, column=2, padx=(0, 20), pady=(0, 10), sticky="e")
@ -654,6 +734,7 @@ class BOIIIWD(ctk.CTk):
self.progress_bar.set(0.0)
self.hide_settings_widgets()
self.button_stop.configure(state="disabled")
self.is_downloading = False
# sidebar windows bouttons
self.sidebar_main.configure(command=self.main_button_event, text="Main", fg_color=("#3d3d3d"))
@ -673,17 +754,21 @@ class BOIIIWD(ctk.CTk):
self.deiconify()
try:
global console
if check_config("console") == "on":
console = True
else:
console = False
self.settings_tab.load_settings("clean_on_finish", "on")
self.settings_tab.load_settings("continuous_download", "on")
self.settings_tab.load_settings("console", "off")
self.settings_tab.load_settings("estimated_progress", "on")
except:
pass
if not check_steamcmd():
self.show_warning_message()
def on_closing(self):
save_config("DestinationFolder" ,self.edit_destination_folder.get())
save_config("SteamCMDPath" ,self.edit_steamcmd_path.get())
self.stop_download(on_close=True)
os._exit(0)
def id_chnaged_handler(self, some=None, other=None ,shit=None):
self.after(1, self.label_file_size.configure(text=f"File size: 0KB"))
@ -908,6 +993,15 @@ class BOIIIWD(ctk.CTk):
text.pack()
def download_map(self):
if not self.is_downloading:
self.is_downloading = True
start_down_thread = threading.Thread(target=self.download_thread)
start_down_thread.start()
else:
show_message("Warning", "Already downloading a map.")
def download_thread(self):
try:
global stopped
stopped = False
@ -932,6 +1026,7 @@ class BOIIIWD(ctk.CTk):
workshop_id = self.edit_workshop_id.get().strip()
destination_folder = self.edit_destination_folder.get().strip()
ws_file_size = get_workshop_file_size(workshop_id)
if not workshop_id.isdigit():
try:
@ -944,7 +1039,7 @@ class BOIIIWD(ctk.CTk):
show_message("Warning", "Please enter a valid Workshop ID.", icon="warning")
return
file_size = get_workshop_file_size(workshop_id)
file_size = ws_file_size
if not valid_id(workshop_id):
show_message("Warning", "Please enter a valid Workshop ID.", icon="warning")
@ -969,8 +1064,13 @@ class BOIIIWD(ctk.CTk):
os.makedirs(download_folder)
def check_and_update_progress():
# delay untill steam boots up and starts downloading (ive a better idea ill implement it later)
time.sleep(8)
global stopped
previous_net_speed = 0
est_downloaded_bytes = 0
start_time = time.time()
file_size = ws_file_size
while not stopped:
try:
@ -979,6 +1079,46 @@ class BOIIIWD(ctk.CTk):
current_size = sum(os.path.getsize(os.path.join(map_folder, f)) for f in os.listdir(map_folder))
progress = int(current_size / file_size * 100)
if progress > 100:
progress = int(current_size / current_size * 100)
file_size = current_size
self.after(1, lambda p=progress: self.label_file_size.configure(text=f"Wrong size reported\nActual size: ~{convert_bytes_to_readable(current_size)}"))
if estimated_progress:
time_elapsed = time.time() - start_time
raw_net_speed = psutil.net_io_counters().bytes_recv
current_net_speed_text = raw_net_speed
net_speed_bytes = current_net_speed_text - previous_net_speed
previous_net_speed = current_net_speed_text
current_net_speed = net_speed_bytes
down_cap = 150000000
if current_net_speed >= down_cap:
current_net_speed = 264029
est_downloaded_bytes += current_net_speed
percentage_complete = (est_downloaded_bytes / file_size) * 100
progress = min(percentage_complete / 100, 0.99)
net_speed, speed_unit = convert_speed(net_speed_bytes)
elapsed_hours, elapsed_minutes, elapsed_seconds = convert_seconds(time_elapsed)
print(f"raw_net {raw_net_speed}\ncurrent_net_speed: {current_net_speed}\nest_downloaded_bytes {est_downloaded_bytes}\npercentage_complete {percentage_complete}\nprogress {progress}")
self.after(1, self.progress_bar.set(progress))
self.after(1, lambda v=net_speed: self.label_speed.configure(text=f"Network Speed: {v:.2f} {speed_unit}"))
self.after(1, lambda p=percentage_complete: self.progress_text.configure(text=f"{p:.2f}%"))
self.after(1, lambda h=elapsed_hours, m=elapsed_minutes, s=elapsed_seconds: self.elapsed_time.configure(text=f"Elapsed Time: {int(h):02d}:{int(m):02d}:{int(s):02d}"))
time.sleep(1)
else:
time_elapsed = time.time() - start_time
progress = int(current_size / file_size * 100)
self.after(1, lambda v=progress / 100.0: self.progress_bar.set(v))
current_net_speed = psutil.net_io_counters().bytes_recv
@ -987,13 +1127,15 @@ class BOIIIWD(ctk.CTk):
previous_net_speed = current_net_speed
net_speed, speed_unit = convert_speed(net_speed_bytes)
elapsed_hours, elapsed_minutes, elapsed_seconds = convert_seconds(time_elapsed)
self.after(1, lambda v=net_speed: self.label_speed.configure(text=f"Network Speed: {v:.2f} {speed_unit}"))
self.after(1, lambda p=progress: self.progress_text.configure(text=f"{p}%"))
self.after(1, lambda h=elapsed_hours, m=elapsed_minutes, s=elapsed_seconds: self.elapsed_time.configure(text=f"Elapsed Time: {int(h):02d}:{int(m):02d}:{int(s):02d}"))
time.sleep(1)
command = f"+login anonymous +workshop_download_item 311210 {workshop_id} +quit"
steamcmd_thread = threading.Thread(target=lambda: run_steamcmd_command(command, self))
command = f"+login anonymous +@sSteamCmdForcePlatformBitness 64 +app_info_update 1 +app_info_print 311210 app_update 311210 +workshop_download_item 311210 {workshop_id} validate +quit"
steamcmd_thread = threading.Thread(target=lambda: run_steamcmd_command(command, self, map_folder))
steamcmd_thread.start()
def wait_for_threads():
@ -1053,10 +1195,19 @@ class BOIIIWD(ctk.CTk):
self.button_download.configure(state="disabled")
self.button_stop.configure(state="normal")
def stop_download(self):
finally:
self.stop_download
self.is_downloading = False
def stop_download(self, on_close=None):
global stopped
stopped = True
if on_close:
subprocess.run(['taskkill', '/F', '/IM', 'steamcmd.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
creationflags=subprocess.CREATE_NO_WINDOW)
return
subprocess.run(['taskkill', '/F', '/IM', 'steamcmd.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
creationflags=subprocess.CREATE_NO_WINDOW)
@ -1064,6 +1215,7 @@ class BOIIIWD(ctk.CTk):
self.button_stop.configure(state="disabled")
self.label_speed.configure(text="Network Speed: 0 KB/s")
self.progress_text.configure(text="0%")
self.elapsed_time.configure(text=f"")
self.progress_bar.set(0.0)
if __name__ == "__main__":