From 94aaf97a4d207eef98a59011e66d49ae7828f1a6 Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Thu, 17 Apr 2025 12:22:22 +0200 Subject: [PATCH 1/4] wip: self updater --- check_release.py | 31 ++++++++++++++++++++++++ client.py | 2 ++ updater/requirements.txt | 2 ++ updater/updater.py | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 check_release.py create mode 100644 updater/requirements.txt create mode 100644 updater/updater.py diff --git a/check_release.py b/check_release.py new file mode 100644 index 0000000..a5c57c9 --- /dev/null +++ b/check_release.py @@ -0,0 +1,31 @@ +import requests +import os + +REPO = "FAUSheppy/homelab_gamevault" +API_URL = f"https://api.github.com/repos/{REPO}/releases/latest" +VERSION_FILE = ".gamevault_version" + +def get_latest_release(): + response = requests.get(API_URL) + response.raise_for_status() + data = response.json() + version = data['tag_name'] + zip_url = data['zipball_url'] + return version, zip_url + + +def read_local_version(): + if not os.path.exists(VERSION_FILE): + return None + with open(VERSION_FILE, 'r') as f: + return f.read().strip() + +def update_updater(): + pass # TODO + # download updater + # replace updater + +def execute_updater(new_version): + # TODO + # os.system(["updater.exe", new_version]) + pass \ No newline at end of file diff --git a/client.py b/client.py index c5076eb..8651828 100644 --- a/client.py +++ b/client.py @@ -320,6 +320,8 @@ def update_button_positions(event=None): if __name__ == "__main__": + # run updater # + pgw = pgwrapper.ProgressBarWrapper() pgw.new(app) diff --git a/updater/requirements.txt b/updater/requirements.txt new file mode 100644 index 0000000..9252f95 --- /dev/null +++ b/updater/requirements.txt @@ -0,0 +1,2 @@ +tkinter +requests \ No newline at end of file diff --git a/updater/updater.py b/updater/updater.py new file mode 100644 index 0000000..0cdfec0 --- /dev/null +++ b/updater/updater.py @@ -0,0 +1,51 @@ +import os +import requests +import zipfile +import io +import shutil +import tkinter as tk + + +CLIENT_DIR = os.path.join("client") +INTERNAL_DIR = os.path.join(CLIENT_DIR, "_internal") + +def prompt_user(version): + root = tk.Tk() + root.withdraw() # Hide main window + result = tk.messagebox.askyesno("Update Available", f"New version {version} available. Download and install?") + root.destroy() + return result + +def download_and_extract(zip_url): + print("Downloading...") + response = requests.get(zip_url) + response.raise_for_status() + with zipfile.ZipFile(io.BytesIO(response.content)) as z: + temp_dir = "_temp_extracted" + z.extractall(temp_dir) + top_folder = next(os.scandir(temp_dir)).path + + # Replace _internal + source_internal = os.path.join(top_folder, "client", "_internal") + if os.path.exists(INTERNAL_DIR): + shutil.rmtree(INTERNAL_DIR) + shutil.copytree(source_internal, INTERNAL_DIR) + + # Replace client.exe + source_exe = os.path.join(top_folder, "client", "client.exe") + target_exe = os.path.join(CLIENT_DIR, "client.exe") + shutil.copy2(source_exe, target_exe) + + shutil.rmtree(temp_dir) + print("Update complete.") + + +def main(): + + + if prompt_user(): + download_and_extract() + # TODO: run main file again + +if __name__ == '__main__': + main() \ No newline at end of file From 1b11b467235c7d4810c094502e73e0245541a23e Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Sat, 7 Jun 2025 13:02:15 +0200 Subject: [PATCH 2/4] add: error message in case of connection problem --- client.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client.py b/client.py index 8651828..e808466 100644 --- a/client.py +++ b/client.py @@ -11,6 +11,8 @@ import imagetools import webbrowser import statekeeper import infowidget +import requests +import tkinter customtkinter.set_appearance_mode("dark") customtkinter.set_default_color_theme("blue") @@ -387,5 +389,9 @@ if __name__ == "__main__": app.update() # fill and run app # - load_main() # TODO add button to reopen config # TODO add button to purge cache/purge cache window # TODO show game size on remote + try: + load_main() # TODO add button to reopen config # TODO add button to purge cache/purge cache window # TODO show game size on remote + except requests.exceptions.ConnectionError as e: + app.withdraw() + tkinter.messagebox.showerror("There was a connection problem", str(e)) app.mainloop() From 3a99e0195bccd54f0b633b7751983689667e35ad Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Sat, 7 Jun 2025 13:42:18 +0200 Subject: [PATCH 3/4] feat: implement progress for extraction operation --- db.py | 1 + software.py | 18 ++++++++++-------- statekeeper.py | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/db.py b/db.py index 6a4b5c0..69bf313 100644 --- a/db.py +++ b/db.py @@ -15,6 +15,7 @@ class Download(Base): local_path = Column(String) url = Column(String) size = Column(Integer) + count = Column(Integer) # extraction only type = Column(String) finished = Column(Boolean) diff --git a/software.py b/software.py index 19fd53e..71ee9e9 100644 --- a/software.py +++ b/software.py @@ -96,25 +96,27 @@ class Software: os.makedirs(software_path, exist_ok=True) + # beginn progress tracking # with zipfile.ZipFile(cache_src, 'r') as zip_ref: + statekeeper.log_begin_download(local_path=cache_src, path=cache_src, url=None, type="extraction", start_size=len(zip_ref.infolist())) total_count = zip_ref.infolist() count = 0 for member in tqdm.tqdm(total_count, desc='Extracting '): try: zip_ref.extract(member, software_path) count += 1 - #self.progress_bar_wrapper.get_pb().set(count/len(total_count)) - #self.progress_bar_wrapper.get_pb().update_idletasks() - #self.progress_bar_wrapper.set_text( - # text="Extracting: {:.2f}%".format(count/len(total_count)*100)) + + # update progress # + statekeeper.set_extraction_status(cache_src, count) + + except zipfile.error as e: print(e) pass # TODO ??? - #zip_ref.extractall(software_path) - - #self.progress_bar_wrapper.set_text(text="Loading..") - #self.progress_bar_wrapper.update() + + # finish extraction tracking # + statekeeper.log_end_download(cache_src, type="extraction") def install_async(self): diff --git a/statekeeper.py b/statekeeper.py index 143e5b9..7e14dd0 100644 --- a/statekeeper.py +++ b/statekeeper.py @@ -41,27 +41,45 @@ def _download(url, path): raise AssertionError("Non-200 Response for:", url, path, response.status_code, response.text) -def log_begin_download(path, local_path, url): +def log_begin_download(path, local_path, url, type="download", start_size=-1): + if type == "extraction": + print("Extraction path:", path) + else: + print("Download path", path) + session = db.session() - print("Download path", path) - path_exists = session.query(Download).filter(and_(Download.path==path, Download.finished==False)).first() + path_exists = session.query(Download).filter(and_(Download.path==path, Download.finished==False, Download.type==type)).first() + if path_exists and False: # TODO FIX THIS print("DAFUG", path_exists) print("WTF", path_exists.path) raise AssertionError("ERROR: {} is already downloading.".format(path)) else: print("Adding to download log:", path) - session.merge(Download(path=path, size=-1, type="download", local_path=local_path, url=url, finished=False)) + session.merge(Download(path=path, size=start_size, type=type, local_path=local_path, url=url, finished=False, count=1)) session.commit() db.close_session() -def log_end_download(path): +def set_extraction_status(path, count): + + session = db.session() + obj = session.query(Download).filter(and_(Download.path==path, Download.type=="extraction")).first() + if not obj: + print("ERROR: {} is not currently extraction, cannot set status.".format(path)) + else: + obj.count = count + session.merge(obj) + session.commit() + + db.close_session() + +def log_end_download(path, type="download"): print("Downlod end logged", path) session = db.session() - obj = session.query(Download).filter(Download.path==path).first() + obj = session.query(Download).filter(and_(Download.path==path, Download.type==type)).first() if not obj: raise AssertionError("ERROR: {} is not downloading/cannot remove.".format(path)) else: @@ -100,8 +118,16 @@ def get_percent_filled(path): session = db.session() obj = session.query(Download).filter(Download.path==path, Download.finished==False).first() + + if not obj: + return 100 + + if obj.type == "extraction": + return obj.count / obj.size * 100 + if not obj: return 100 # means its finished + size = _bytes_to_mb(os.stat(obj.local_path).st_size) total_size = get_download_size(obj.path) session.close() From 010e19e6b47a6d4c7f5fe6ec67db75fb3d35713f Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Sat, 7 Jun 2025 13:57:05 +0200 Subject: [PATCH 4/4] fix: update todo infowidget theme & zip extraction --- todo.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/todo.txt b/todo.txt index 8a61d06..4966a8b 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,5 @@ # important ## downloaded file hash sum check -## apply custom tkinter look to pg window & move pg window to better relativ start location so it's no longer blocking the back button by default -## zip extraction progress ## fix initial startup pictures not loading ## implement flush download cache button ## fix Call of duty installation chain