1919
2020from PyQt6 import QtCore , QtWidgets , QtGui
2121
22- VERSION = "V1.1 .0"
22+ VERSION = "V1.2 .0"
2323LOGFILE = Path (os .getenv ("TEMP" ) or tempfile .gettempdir ()) / f"mtkclient_installer_{ VERSION } .log"
2424TIMEOUT_WINGET = 60 * 30
2525TIMEOUT_VS = 60 * 60
@@ -104,22 +104,6 @@ def resource_path(relative: str) -> str:
104104 return os .path .join (os .path .abspath ("." ), relative )
105105
106106
107- def _choose_download_dest_from_headers (url : str , fallback_name : str , timeout_head : float = 8.0 ) -> str :
108- try :
109- req = urllib .request .Request (url , method = "HEAD" , headers = {"User-Agent" : "mtkclient-installer/1.0" })
110- with urllib .request .urlopen (req , timeout = timeout_head ) as head :
111- cd = head .getheader ("Content-Disposition" )
112- if cd :
113- m = re .search (r'filename\*?=(?:UTF-8\'\')?["\']?([^"\';]+)' , cd )
114- if m :
115- return m .group (1 )
116- except Exception :
117- pass
118-
119- parsed = urllib .parse .urlparse (url )
120- name = Path (parsed .path ).name
121- return name or fallback_name
122-
123107
124108def get_user_runtime_dir (appname : str = "mtkclient" ) -> Path :
125109 base = os .getenv ("LOCALAPPDATA" ) or os .getenv ("APPDATA" ) or str (Path .home ())
@@ -242,7 +226,6 @@ def _setup_steps(self):
242226 ("Install Git & Python 3.1x" , self .step_install_git_python ),
243227 ("Refresh Environment PATH" , self .step_refresh_path ),
244228 ("Install WinFsp and UsbDk" , self .step_install_winfsp_usbdk ),
245- ("Install OpenSSL 1.1.1" , self .step_install_openssl ),
246229 ("Install Visual Studio Build Tools" , self .step_install_build_tools ),
247230 ("Clone mtkclient Repo" , self .step_clone_repo ),
248231 ("Install Python Requirements" , self .step_pip_install_requirements ),
@@ -268,25 +251,36 @@ def _run_proc(self, cmd: List[str], cwd: Optional[str] = None,
268251
269252 def reader (pipe , collector , prefix ):
270253 try :
254+ buffer = ""
271255 while True :
272- line = pipe .readline ()
273- if line :
274- collector .append (line )
275- self .log_debug .emit (prefix + line )
276- m = PCT_RE .search (line )
277- if m :
278- try :
279- pct = int (m .group (1 ))
280- if 0 <= pct <= 100 :
281- self .current_progress_changed .emit (pct )
282- except Exception :
283- pass
284- else :
256+ char = pipe .read (1 )
257+ if not char :
285258 if proc .poll () is not None or self ._stop_requested :
286259 break
287260 if stop_time and time .time () > stop_time :
288261 break
289- time .sleep (0.05 )
262+ time .sleep (0.01 )
263+ continue
264+
265+ buffer += char
266+
267+ # Process on newline or carriage return
268+ if char in ('\n ' , '\r ' ):
269+ if buffer .strip ():
270+ line = buffer
271+ collector .append (line )
272+ self .log_debug .emit (prefix + line )
273+
274+ # Extract percentage
275+ m = PCT_RE .search (line )
276+ if m :
277+ try :
278+ pct = int (m .group (1 ))
279+ if 0 <= pct <= 100 :
280+ self .current_progress_changed .emit (pct )
281+ except Exception :
282+ pass
283+ buffer = ""
290284 except Exception :
291285 pass
292286
@@ -705,6 +699,7 @@ def get_ver(cmd_list): # version string helper
705699 return False , "winget not available"
706700
707701 # Refresh sources
702+
708703 self .log_summary .emit ("Refreshing winget sources...\n " )
709704 self ._run_proc (["winget" , "source" , "update" ], None , timeout = 120 )
710705
@@ -755,7 +750,12 @@ def get_ver(cmd_list): # version string helper
755750 self .log_summary .emit (f"Verified Python: { new_py_ver } \n " )
756751 else :
757752 # If winget succeeded but we can't find it, it's a PATH refresh issue
758- return False , "Python installed but system PATH is not refreshed. Please reboot and try again."
753+ refresh_environment_path ()
754+ find_python_executable () # last attempt. Won't change the result but may help with debugging
755+ return False , "Python installed but isn't in PATH, please retry step or reboot."
756+
757+
758+
759759
760760 return True , "Git and Python setup successful"
761761
@@ -789,101 +789,7 @@ def step_install_winfsp_usbdk(self) -> Tuple[bool, str]:
789789 self .log_summary .emit (f"Warning: WinFsp install returned { res .exitcode } \n " )
790790 return True , "WinFsp/UsbDk step completed"
791791
792- def step_install_openssl (self ) -> Tuple [bool , str ]:
793- # Install OpenSSL 1.1.1 with multiple fallback methods
794- # Check if already installed
795- candidates = [
796- Path (r"C:\OpenSSL-Win64" ),
797- Path (r"C:\Program Files\OpenSSL-Win64" ),
798- Path (os .environ .get ("ProgramFiles" , "" )) / "OpenSSL-Win64" ,
799- ]
800- for c in candidates :
801- if c .exists ():
802- return True , f"OpenSSL found at { c } "
803792
804- # Method 1: Try winget with known package IDs
805- if shutil .which ("winget" ):
806- self .log_summary .emit ("Installing OpenSSL via winget...\n " )
807- for pkg_id in ["ShiningLight.OpenSSL" , "ShiningLight.OpenSSL.Light" ]:
808- res = self ._run_proc (
809- ["winget" , "install" , "--id" , pkg_id , "-e" ,
810- "--accept-package-agreements" , "--accept-source-agreements" ],
811- None , timeout = TIMEOUT_WINGET
812- )
813- if res .exitcode in [0 , 3010 ]:
814- if res .exitcode == 3010 :
815- self ._needs_reboot = True
816- return True , f"OpenSSL installed via winget ({ pkg_id } )"
817-
818- # Method 2: Search winget for OpenSSL packages
819- self .log_debug .emit (timestamped ("[OPENSSL] Searching winget catalog...\n " ))
820- search = self ._run_proc (["winget" , "search" , "openssl" ], None , timeout = 60 )
821- found_ids = set ()
822- for line in (search .stdout or "" ).splitlines ():
823- for token in line .strip ().split ()[:3 ]:
824- if "." in token and "openssl" in token .lower () and len (token ) > 4 :
825- found_ids .add (token )
826-
827- for pkg_id in found_ids :
828- self .log_summary .emit (f"Trying: { pkg_id } \n " )
829- res = self ._run_proc (
830- ["winget" , "install" , "--id" , pkg_id , "-e" ,
831- "--accept-package-agreements" , "--accept-source-agreements" ],
832- None , timeout = TIMEOUT_WINGET
833- )
834- if res .exitcode in [0 , 3010 ]:
835- if res .exitcode == 3010 :
836- self ._needs_reboot = True
837- return True , f"OpenSSL installed via winget ({ pkg_id } )"
838-
839- # Method 3: Direct download fallback
840- self .log_summary .emit ("Attempting direct download...\n " )
841- urls = [
842- "https://slproweb.com/download/Win64OpenSSL-1_1_1u.exe" ,
843- "https://slproweb.com/download/Win64OpenSSL-1_1_1u-x64.exe" ,
844- "https://sourceforge.net/projects/openssl-for-windows/files/latest/download" ,
845- ]
846-
847- tempdir = Path (os .getenv ("TEMP" ) or tempfile .gettempdir ())
848- for url in urls :
849- # Get proper filename from headers and validate
850- filename = _choose_download_dest_from_headers (url , "Win64OpenSSL-installer.exe" )
851- dest = tempdir / filename
852-
853- # Retry wrapper for this specific URL
854- def attempt_download ():
855- return self ._download_with_progress (url , dest , timeout = TIMEOUT_DOWNLOAD )
856-
857- self .log_summary .emit (f"Attempting download from { url } (up to 3 retries)...\n " )
858- success , msg = _download_with_retry (attempt_download , max_attempts = 3 , base_delay = 2.0 )
859-
860- if not success :
861- self .log_summary .emit (f"All download attempts failed for { url } : { msg } \n " )
862- continue
863-
864- # ensure we actually downloaded an .exe/.msi
865- if dest .suffix .lower () not in (".exe" , ".msi" ):
866- self .log_debug .emit (f"[OPENSSL] downloaded file { dest } is not an executable; removing and skipping.\n " )
867- try :
868- dest .unlink ()
869- except Exception :
870- pass
871- continue
872-
873- # Try installation with silent flags
874- for flags in [["/verysilent" , "/sp-" , "/norestart" ], ["/S" ]]:
875- res = self ._run_proc ([str (dest )] + flags , None , timeout = TIMEOUT_DOWNLOAD )
876- try :
877- dest .unlink ()
878- except :
879- pass
880-
881- if res .exitcode in [0 , 3010 ]:
882- if res .exitcode == 3010 :
883- self ._needs_reboot = True
884- return True , "OpenSSL installed via direct download"
885-
886- return False , "All OpenSSL installation methods failed. Please install manually from slproweb.com"
887793
888794 def step_install_build_tools (self ) -> Tuple [bool , str ]:
889795 # check vcvars64.bat presence
@@ -919,7 +825,10 @@ def step_install_build_tools(self) -> Tuple[bool, str]:
919825
920826 if not shutil .which ("winget" ):
921827 return False , "winget not present"
922-
828+
829+ self .log_summary .emit ("Ensuring Winget health...\n " )
830+ subprocess .run (["winget" , "source" , "reset" , "--force" ], capture_output = True )
831+ subprocess .run (["winget" , "source" , "update" ], capture_output = True )
923832 self .log_summary .emit (
924833 "Installing Visual Studio Build Tools (Desktop C++ workload).\n "
925834 )
@@ -1085,21 +994,12 @@ def step_pip_install_requirements(self) -> Tuple[bool, str]:
1085994 None , timeout = 300
1086995 )
1087996
1088- # Configure OpenSSL environment for compilation
1089- env = os .environ .copy ()
1090- for openssl_dir in [r"C:\OpenSSL-Win64" , r"C:\Program Files\OpenSSL-Win64" ]:
1091- if Path (openssl_dir ).exists ():
1092- env ["INCLUDE" ] = env .get ("INCLUDE" , "" ) + ";" + os .path .join (openssl_dir , "include" )
1093- env ["LIB" ] = env .get ("LIB" , "" ) + ";" + os .path .join (openssl_dir , "lib" )
1094- env ["PATH" ] = env .get ("PATH" , "" ) + ";" + os .path .join (openssl_dir , "bin" )
1095- self .log_summary .emit (f"Using OpenSSL from: { openssl_dir } \n " )
1096- break
1097997
1098998 # Install requirements
1099999 self .log_summary .emit ("Installing Python packages (this may take several minutes)...\n " )
11001000 res = self ._run_proc (
11011001 [py_exec , "-m" , "pip" , "install" , "-r" , str (reqs )],
1102- str (reqs .parent ), env = env , timeout = PIP_TIMEOUT
1002+ str (reqs .parent ), env = None , timeout = PIP_TIMEOUT
11031003 )
11041004
11051005 if res .exitcode == 0 :
@@ -1327,7 +1227,7 @@ def show_about(self):
13271227 about_text = f"""
13281228 <h3>MTKClient Windows Installer</h3>
13291229 <p>
1330- <b>Version:</b> { VERSION } (290120261511 )<br>
1230+ <b>Version:</b> { VERSION } (0606220262110 )<br>
13311231 <b>Developer:</b>
13321232 <a href="https://github.com/codefl0w">fl0w</a>
13331233 </p>
@@ -1351,7 +1251,7 @@ def show_about(self):
13511251 </li>
13521252 <li style="margin-top: 8px;">
13531253 <b>Other Components:</b><br>
1354- Git, Python, OpenSSL, Visual Studio Build Tools, WinFsp, and UsbDk
1254+ Git, Python, Visual Studio Build Tools, WinFsp, and UsbDk
13551255 are trademarks and products of their respective owners and are
13561256 distributed under their own licenses.
13571257 </li>
0 commit comments