Skip to content

Commit 96c3cbc

Browse files
committed
fix winget issues (better link, corruption checks and progress bar support)
1 parent 71aa3bb commit 96c3cbc

1 file changed

Lines changed: 117 additions & 83 deletions

File tree

mtkclient_installer.py

Lines changed: 117 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from PyQt6 import QtCore, QtWidgets, QtGui
2121

22-
VERSION = "V1.0.4"
22+
VERSION = "V1.1.0"
2323
LOGFILE = Path(os.getenv("TEMP") or tempfile.gettempdir()) / f"mtkclient_installer_{VERSION}.log"
2424
TIMEOUT_WINGET = 60 * 30
2525
TIMEOUT_VS = 60 * 60
@@ -458,28 +458,19 @@ def step_ensure_winget(self) -> Tuple[bool, str]:
458458
self.log_summary.emit("winget not found. Installing required dependencies...\n")
459459
temp_dir = Path(os.getenv("TEMP") or tempfile.gettempdir())
460460

461+
461462
self.log_summary.emit("Step 1/3: Installing VCLibs dependency...\n")
462463
vclibs_url = "https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx"
463464
vclibs = temp_dir / "Microsoft.VCLibs.x64.14.00.Desktop.appx"
464465

465-
vclibs_url_q = _ps_single_quote_escape(vclibs_url)
466-
vclibs_q = _ps_single_quote_escape(str(vclibs))
467-
468466
def attempt_vclibs_download():
469-
ps_script = f"Invoke-WebRequest -Uri '{vclibs_url_q}' -OutFile '{vclibs_q}' -UseBasicParsing"
470-
encoded_ps = _ps_encode_command(ps_script)
471-
res = self._run_proc(
472-
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded_ps],
473-
None, timeout=TIMEOUT_DOWNLOAD
474-
)
475-
if res.exitcode == 0 and vclibs.exists():
476-
return True, "Downloaded"
477-
return False, f"Exit {res.exitcode}"
467+
return self._download_with_progress(vclibs_url, vclibs, timeout=TIMEOUT_DOWNLOAD)
478468

479469
success, msg = _download_with_retry(attempt_vclibs_download, max_attempts=3, base_delay=1.5)
480470

481471
if success:
482472
self.log_summary.emit(f"VCLibs downloaded ({vclibs.stat().st_size / 1024:.0f} KB)\n")
473+
vclibs_q = _ps_single_quote_escape(str(vclibs))
483474
ps_script = f"Add-AppxPackage -Path '{vclibs_q}' -ErrorAction SilentlyContinue"
484475
encoded_ps = _ps_encode_command(ps_script)
485476
res_install = self._run_proc(
@@ -495,30 +486,21 @@ def attempt_vclibs_download():
495486
except:
496487
pass
497488
else:
498-
self.log_summary.emit(f"VCLibs download failed after retries: {msg} (may already be present, continuing)\n")
489+
self.log_summary.emit(f"VCLibs download failed: {msg} (may already be present, continuing)\n")
499490

491+
500492
self.log_summary.emit("Step 2/3: Installing UI.Xaml dependency...\n")
501493
xaml_url = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx"
502494
xaml = temp_dir / "Microsoft.UI.Xaml.2.8.x64.appx"
503495

504-
xaml_url_q = _ps_single_quote_escape(xaml_url)
505-
xaml_q = _ps_single_quote_escape(str(xaml))
506-
507496
def attempt_xaml_download():
508-
ps_script = f"Invoke-WebRequest -Uri '{xaml_url_q}' -OutFile '{xaml_q}' -UseBasicParsing"
509-
encoded_ps = _ps_encode_command(ps_script)
510-
res = self._run_proc(
511-
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded_ps],
512-
None, timeout=TIMEOUT_DOWNLOAD
513-
)
514-
if res.exitcode == 0 and xaml.exists():
515-
return True, "Downloaded"
516-
return False, f"Exit {res.exitcode}"
497+
return self._download_with_progress(xaml_url, xaml, timeout=TIMEOUT_DOWNLOAD)
517498

518499
success, msg = _download_with_retry(attempt_xaml_download, max_attempts=3, base_delay=1.5)
519500

520501
if success:
521502
self.log_summary.emit(f"UI.Xaml downloaded ({xaml.stat().st_size / 1024:.0f} KB)\n")
503+
xaml_q = _ps_single_quote_escape(str(xaml))
522504
ps_script = f"Add-AppxPackage -Path '{xaml_q}' -ErrorAction SilentlyContinue"
523505
encoded_ps = _ps_encode_command(ps_script)
524506
res_install = self._run_proc(
@@ -534,90 +516,142 @@ def attempt_xaml_download():
534516
except:
535517
pass
536518
else:
537-
self.log_summary.emit(f"UI.Xaml download failed after retries: {msg} (may already be present, continuing)\n")
519+
self.log_summary.emit(f"UI.Xaml download failed: {msg} (may already be present, continuing)\n")
538520

521+
539522
self.log_summary.emit("Step 3/3: Installing App Installer (winget)...\n")
540523
msix = temp_dir / "Microsoft.DesktopAppInstaller.msixbundle"
541-
url = "https://aka.ms/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
542524

543-
msix_q = _ps_single_quote_escape(str(msix))
544-
url_q = _ps_single_quote_escape(url)
525+
urls = [
526+
"https://aka.ms/getwinget",
527+
"https://github.com/microsoft/winget-cli/releases/download/v1.7.10861/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle",
528+
]
545529

546530
self.log_summary.emit("Cleaning up any conflicting installations...\n")
547531
ps_script = "Get-AppxPackage Microsoft.DesktopAppInstaller | Remove-AppxPackage -ErrorAction SilentlyContinue"
548532
encoded_ps = _ps_encode_command(ps_script)
549-
cleanup = self._run_proc(
533+
self._run_proc(
550534
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded_ps],
551535
None, timeout=30
552536
)
553537

554-
self.log_summary.emit(f"Downloading from {url} (up to 3 retries)...\n")
538+
download_success = False
539+
res2 = None
555540

556-
def attempt_winget_download():
557-
ps_script = f"Invoke-WebRequest -Uri '{url_q}' -OutFile '{msix_q}' -UseBasicParsing"
558-
encoded_ps = _ps_encode_command(ps_script)
559-
res = self._run_proc(
560-
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded_ps],
561-
None, timeout=TIMEOUT_DOWNLOAD
562-
)
563-
if res.exitcode == 0 and msix.exists():
564-
return True, "Downloaded"
565-
return False, f"Exit {res.exitcode}"
566-
567-
success, msg = _download_with_retry(attempt_winget_download, max_attempts=3, base_delay=2.0)
568-
569-
if not success:
570-
self.log_summary.emit(f"Download failed after all retries: {msg}\n")
571-
return False, f"Failed to download winget installer: {msg}"
572-
573-
if not msix.exists():
574-
return False, f"Download reported success but file not found at {msix}"
575-
576-
file_size = msix.stat().st_size / (1024 * 1024)
577-
self.log_summary.emit(f"Downloaded {file_size:.2f} MB\n")
578-
579-
self.log_summary.emit("Installing App Installer package...\n")
580-
581-
for attempt in range(2):
582-
ps_script = f"Add-AppxPackage -Path '{msix_q}' -ErrorAction Stop"
583-
encoded_ps = _ps_encode_command(ps_script)
584-
res2 = self._run_proc(
585-
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded_ps],
586-
None, timeout=TIMEOUT_WINGET
587-
)
541+
for url_idx, url in enumerate(urls):
542+
self.log_summary.emit(f"Download source {url_idx + 1}/{len(urls)}: {url}\n")
588543

589-
self.log_summary.emit(f"Installation attempt {attempt + 1} exit code: {res2.exitcode}\n")
544+
try:
545+
if msix.exists():
546+
msix.unlink()
547+
except:
548+
pass
590549

591-
if res2.exitcode == 0:
592-
break
550+
def attempt_winget_download():
551+
# Use the progress-tracking download method
552+
success, msg = self._download_with_progress(url, msix, timeout=TIMEOUT_DOWNLOAD)
553+
554+
if not success:
555+
return False, msg
556+
557+
# Validate download
558+
if not msix.exists():
559+
return False, "File not found after download"
560+
561+
file_size_mb = msix.stat().st_size / (1024 * 1024)
562+
self.log_summary.emit(f"Downloaded {file_size_mb:.2f} MB\n")
563+
564+
# Safety check
565+
if file_size_mb < 100:
566+
self.log_summary.emit(f"[WARNING] File too small ({file_size_mb:.2f} MB), likely corrupted (should be ~200MB)\n")
567+
try:
568+
msix.unlink()
569+
except:
570+
pass
571+
return False, f"File too small ({file_size_mb:.2f} MB)"
572+
573+
# Validate file format
574+
try:
575+
with open(msix, 'rb') as f:
576+
header = f.read(4)
577+
if header[:2] != b'PK':
578+
self.log_summary.emit("[WARNING] Invalid file format (not a valid bundle)\n")
579+
try:
580+
msix.unlink()
581+
except:
582+
pass
583+
return False, "Invalid file format"
584+
except Exception as e:
585+
return False, f"Cannot validate file: {e}"
586+
587+
return True, "Downloaded and validated"
588+
589+
success, msg = _download_with_retry(attempt_winget_download, max_attempts=3, base_delay=2.0)
590+
591+
if not success:
592+
self.log_summary.emit(f"Source {url_idx + 1} failed: {msg}\n")
593+
continue
594+
595+
# Installation attempts
596+
self.log_summary.emit("Installing App Installer package...\n")
597+
msix_q = _ps_single_quote_escape(str(msix))
593598

594-
if res2.stderr:
595-
self.log_summary.emit(f"Error output: {res2.stderr[:500]}\n")
599+
for attempt in range(2):
600+
ps_script = f"Add-AppxPackage -Path '{msix_q}' -ErrorAction Stop"
601+
encoded_ps = _ps_encode_command(ps_script)
602+
res2 = self._run_proc(
603+
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded_ps],
604+
None, timeout=TIMEOUT_WINGET
605+
)
596606

597-
if attempt == 0:
598-
self.log_summary.emit("First attempt failed, retrying after cleanup...\n")
599-
time.sleep(2)
607+
self.log_summary.emit(f"Installation attempt {attempt + 1} exit code: {res2.exitcode}\n")
608+
609+
if res2.exitcode == 0:
610+
download_success = True
611+
break
612+
613+
if "0x80073CF0" in (res2.stderr or "") or "0x80070570" in (res2.stderr or ""):
614+
self.log_summary.emit("[ERROR] Package corrupted, trying next source...\n")
615+
try:
616+
msix.unlink()
617+
except:
618+
pass
619+
break
620+
621+
if res2.stderr:
622+
self.log_summary.emit(f"Error: {res2.stderr[:500]}\n")
623+
624+
if attempt == 0:
625+
self.log_summary.emit("Retrying after cleanup...\n")
626+
time.sleep(2)
627+
628+
if download_success:
629+
break
600630

601631
try:
602-
os.remove(msix)
632+
if msix.exists():
633+
os.remove(msix)
603634
except:
604635
pass
605-
606-
if res2.exitcode != 0:
607-
if "sideload" in (res2.stderr or "").lower() or "policy" in (res2.stderr or "").lower():
608-
return False, "App sideloading disabled. Enable in Settings > Update & Security > For Developers."
609-
if "0x80073CF3" in (res2.stderr or ""):
610-
return False, "Package conflict detected. Please manually uninstall 'App Installer' from Settings > Apps, then retry."
611-
return False, f"Installation failed (exit {res2.exitcode}). See output above for details."
612-
636+
637+
if not download_success or (res2 and res2.exitcode != 0):
638+
if res2:
639+
if "sideload" in (res2.stderr or "").lower() or "policy" in (res2.stderr or "").lower():
640+
return False, "Enable app sideloading in Settings > Update & Security > For Developers."
641+
if "0x80073CF3" in (res2.stderr or ""):
642+
return False, "Uninstall 'App Installer' from Settings > Apps, then retry."
643+
if "0x80073CF0" in (res2.stderr or "") or "0x80070570" in (res2.stderr or ""):
644+
return False, "All sources corrupted. Check disk health or download manually from https://aka.ms/getwinget"
645+
return False, "All attempts failed. Try manual installation from https://aka.ms/getwinget"
646+
613647
self.log_summary.emit("Waiting for package registration...\n")
614648
time.sleep(3)
615649

616650
refresh_environment_path()
617651

618652
for attempt in range(3):
619653
if shutil.which("winget"):
620-
self.log_summary.emit(f"winget found in PATH\n")
654+
self.log_summary.emit("winget found in PATH\n")
621655
return True, "winget installed successfully"
622656
if attempt < 2:
623657
self.log_summary.emit(f"Waiting for PATH update (attempt {attempt + 1}/3)...\n")
@@ -1293,7 +1327,7 @@ def show_about(self):
12931327
about_text = f"""
12941328
<h3>MTKClient Windows Installer</h3>
12951329
<p>
1296-
<b>Version:</b> {VERSION} (270120262308)<br>
1330+
<b>Version:</b> {VERSION} (290120261511)<br>
12971331
<b>Developer:</b>
12981332
<a href="https://github.com/codefl0w">fl0w</a>
12991333
</p>

0 commit comments

Comments
 (0)