Skip to content

Commit 60dd2c5

Browse files
author
nothing
committed
Renamed file. Added logging and config file. Added prompt if install path isn't found. Cleaned up code
1 parent 991feb6 commit 60dd2c5

2 files changed

Lines changed: 138 additions & 65 deletions

File tree

Keychron_mice_updater.py

Lines changed: 0 additions & 65 deletions
This file was deleted.

updater.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import requests, gdown, zipfile, os, ctypes, tempfile, shutil, subprocess, sys, json, logging
2+
from tkinter import messagebox, filedialog
3+
4+
def terminate():
5+
logger.error("Terminating the program")
6+
sys.exit()
7+
8+
def get_appdata_path():
9+
appdata_path = os.getenv('APPDATA')
10+
folder_path = os.path.join(appdata_path, 'Keychron mice updater')
11+
12+
if not os.path.exists(folder_path):
13+
os.makedirs(folder_path)
14+
15+
return folder_path
16+
17+
def setup_logger():
18+
folder_path = get_appdata_path()
19+
log_file_path = os.path.join(folder_path, 'updater.log')
20+
21+
logging.basicConfig(
22+
filename=log_file_path,
23+
format='%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)d - %(message)s',
24+
level=logging.INFO,
25+
encoding='utf-8'
26+
)
27+
28+
logger = logging.getLogger(__name__)
29+
return logger
30+
31+
logger = setup_logger()
32+
33+
def config_manager():
34+
folder_path = get_appdata_path()
35+
config_file = os.path.join(folder_path, "config.json")
36+
37+
if os.path.exists(config_file):
38+
with open(config_file, "r") as f:
39+
install_path = json.load(f)["install_path"]
40+
if os.path.exists(os.path.join(install_path, 'config.xml')):
41+
return install_path
42+
else:
43+
logger.warning('config.xml not found in install_path')
44+
45+
install_path = get_install_path()
46+
with open(config_file, "w") as f:
47+
json.dump({"install_path": install_path}, f)
48+
logger.info('install_path written to config.json')
49+
50+
return install_path
51+
52+
def get_install_path():
53+
default_path = r"C:\Program Files (x86)\Keychron"
54+
if not os.path.exists(os.path.join(default_path, 'config.xml')):
55+
logger.warning(f"Keychron software is not installed in the default location: {default_path}")
56+
messagebox.showerror("Error", f"Keychron software is not installed in the default location: {default_path}")
57+
messagebox.showinfo("Info", "Please select the Keychron software installation folder")
58+
install_path = filedialog.askdirectory().replace("/", "\\")
59+
logger.info(f"User selected installation folder: {install_path}")
60+
return install_path
61+
return default_path
62+
63+
def get_installed_version(install_path):
64+
try:
65+
with open(install_path + "\\config.xml") as f:
66+
installed_version = f.read().split('<software caption="Keychron" version="')[1].split('"')[0]
67+
logger.info(f"Installed version found: {installed_version}")
68+
return installed_version
69+
except Exception as e:
70+
logger.error(f"Failed to get installed version: {e}")
71+
messagebox.showerror("Error", f"Failed to get installed version: {e}")
72+
terminate()
73+
74+
def get_online_version_and_url():
75+
try:
76+
download_site = requests.get("https://www.keychron.com/pages/learn-more-how-to-use-keychron-mouse-software").text
77+
download_id = download_site.split('drive.google.com/file/d/')[1].split('/')[0].strip()
78+
download_url = f"https://drive.google.com/uc?id={download_id}"
79+
logging.info(f"Download url obtained: {download_url}")
80+
81+
online_version = download_site.splitlines()
82+
for line in online_version:
83+
if "Version" in line and "updated on" in line:
84+
online_version = line.split('Version ')[1].split(' ')[0].strip()
85+
logging.info(f"Online version obtained: {online_version}")
86+
break
87+
88+
return online_version, download_url
89+
except Exception as e:
90+
logging.error(f"Failed to get online version and url: {e}")
91+
messagebox.showerror("Error", f"Failed to get online version and url: {e}")
92+
terminate()
93+
94+
def download_and_extract_file(download_url, tmp_path):
95+
try:
96+
gdown.download(download_url, tmp_path + '\\Keychron.zip', quiet=True)
97+
with zipfile.ZipFile(tmp_path + '\\Keychron.zip', 'r') as zip_ref:
98+
zip_ref.extractall(tmp_path)
99+
except Exception as e:
100+
logging.error(f"Failed to download and extract file: {e}")
101+
messagebox.showerror("Error", f"Failed to download and extract file: {e}")
102+
terminate()
103+
104+
def run_exe(tmp_path):
105+
try:
106+
for root, dirs, files in os.walk(tmp_path):
107+
for file in files:
108+
if file.endswith(".exe"):
109+
process = subprocess.Popen([os.path.join(root, file)], shell=True)
110+
process.wait()
111+
except Exception as e:
112+
logger.error(f"Failed to run exe: {e}")
113+
messagebox.showerror("Error", f"Failed to run exe: {e}")
114+
terminate()
115+
116+
def main():
117+
logger.info("Starting the updater")
118+
install_path = config_manager()
119+
try:
120+
installed_version = get_installed_version(install_path)
121+
online_version, download_url = get_online_version_and_url()
122+
123+
if installed_version != online_version:
124+
MessageBox = ctypes.windll.user32.MessageBoxW
125+
result = MessageBox(None, f'Version {online_version} of the Keychron software is available. Do you want to download it?', 'New version available', 1)
126+
if result == 1:
127+
tmp_path = tempfile.mkdtemp()
128+
download_and_extract_file(download_url, tmp_path)
129+
run_exe(tmp_path)
130+
shutil.rmtree(tmp_path)
131+
except Exception as e:
132+
logging.error(f"An error occurred in main function: {e}")
133+
messagebox.showerror("Error", f"An error occurred in main function: {e}")
134+
terminate()
135+
logger.info("Updater finished")
136+
137+
if __name__ == "__main__":
138+
main()

0 commit comments

Comments
 (0)