Automatically detect and install python on Windows machines

This commit is contained in:
2025-08-10 12:31:13 -05:00
parent 018ddb1a0f
commit 60194da877
2 changed files with 178 additions and 0 deletions

BIN
windows/python.msix Normal file

Binary file not shown.

View File

@@ -10,6 +10,7 @@ import subprocess
import threading import threading
import sys import sys
import os import os
import platform
from pathlib import Path from pathlib import Path
import queue import queue
@@ -35,12 +36,184 @@ class YouTubeArchiverGUI:
self.download_process = None self.download_process = None
self.is_downloading = False self.is_downloading = False
# Check Python installation on Windows first
if not self.check_python_installation():
return # Exit if Python installation failed
self.create_widgets() self.create_widgets()
self.check_dependencies() self.check_dependencies()
# Start checking queue for output updates # Start checking queue for output updates
self.root.after(100, self.check_queue) self.root.after(100, self.check_queue)
def check_python_installation(self):
"""Check if Python is properly installed on Windows."""
if platform.system() != "Windows":
return True # Not Windows, proceed normally
try:
# Try to run python and get version
result = subprocess.run([sys.executable, "--version"],
capture_output=True, text=True, check=True)
python_version = result.stdout.strip()
print(f"Python is available: {python_version}")
return True
except (subprocess.CalledProcessError, FileNotFoundError):
# Python not found, try to install it
return self.install_python_windows()
def install_python_windows(self):
"""Install Python on Windows using the provided MSIX installer."""
installer_path = Path("windows/python.msix")
if not installer_path.exists():
messagebox.showerror(
"Python Installation Error",
f"Python is not installed and the installer was not found at:\n{installer_path.absolute()}\n\n"
"Please install Python manually from python.org or place the installer at the expected location."
)
return False
# Show installation dialog
result = messagebox.askyesno(
"Python Installation Required",
"Python is not installed on this system. Would you like to install it now?\n\n"
f"The installer will be launched from:\n{installer_path.absolute()}"
)
if not result:
messagebox.showinfo(
"Installation Cancelled",
"Python installation cancelled. The application cannot run without Python."
)
return False
try:
# Create a temporary window to show installation progress
install_window = tk.Toplevel(self.root)
install_window.title("Installing Python")
install_window.geometry("400x200")
install_window.resizable(False, False)
install_window.transient(self.root)
install_window.grab_set()
# Center the window
install_window.update_idletasks()
x = (install_window.winfo_screenwidth() // 2) - (install_window.winfo_width() // 2)
y = (install_window.winfo_screenheight() // 2) - (install_window.winfo_height() // 2)
install_window.geometry(f"+{x}+{y}")
# Add progress indicator and message
frame = ttk.Frame(install_window, padding="20")
frame.pack(fill="both", expand=True)
ttk.Label(frame, text="Installing Python...",
font=('Arial', 12, 'bold')).pack(pady=(0, 10))
progress = ttk.Progressbar(frame, mode='indeterminate')
progress.pack(fill="x", pady=(0, 10))
progress.start()
status_label = ttk.Label(frame, text="Launching installer...", wraplength=350)
status_label.pack(pady=(0, 10))
# Update the window
install_window.update()
# Launch the MSIX installer
status_label.config(text="Running Python installer. Please follow the installation prompts.")
install_window.update()
# Use PowerShell to install the MSIX package
powershell_cmd = f'Add-AppxPackage -Path "{installer_path.absolute()}"'
result = subprocess.run([
"powershell", "-Command", powershell_cmd
], capture_output=True, text=True)
progress.stop()
if result.returncode == 0:
status_label.config(text="Installation completed successfully!")
install_window.update()
# Wait a moment and then verify installation
self.root.after(2000, lambda: self.verify_python_installation(install_window))
return True
else:
# Installation failed
error_msg = result.stderr or result.stdout or "Unknown error occurred"
status_label.config(text=f"Installation failed: {error_msg[:100]}...")
install_window.update()
messagebox.showerror(
"Installation Failed",
f"Python installation failed:\n{error_msg}\n\n"
"Please try installing Python manually from python.org"
)
install_window.destroy()
return False
except Exception as e:
messagebox.showerror(
"Installation Error",
f"An error occurred while installing Python:\n{str(e)}\n\n"
"Please try installing Python manually from python.org"
)
if 'install_window' in locals():
install_window.destroy()
return False
def verify_python_installation(self, install_window):
"""Verify that Python was installed successfully."""
try:
# Try to find Python in common locations
possible_python_paths = [
"python",
"python3",
"py",
os.path.expandvars(r"%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe"),
os.path.expandvars(r"%LOCALAPPDATA%\Programs\Python\Python*\python.exe"),
]
python_found = False
for python_path in possible_python_paths:
try:
result = subprocess.run([python_path, "--version"],
capture_output=True, text=True, check=True)
python_version = result.stdout.strip()
print(f"Python found at {python_path}: {python_version}")
python_found = True
break
except (subprocess.CalledProcessError, FileNotFoundError):
continue
install_window.destroy()
if python_found:
messagebox.showinfo(
"Installation Successful",
f"Python has been installed successfully!\n{python_version}\n\n"
"The application will now continue."
)
return True
else:
messagebox.showwarning(
"Installation Verification Failed",
"Python installation completed, but Python could not be found in the expected locations.\n\n"
"You may need to restart the application or your system for the installation to take effect."
)
return False
except Exception as e:
install_window.destroy()
messagebox.showerror(
"Verification Error",
f"Could not verify Python installation:\n{str(e)}"
)
return False
def create_widgets(self): def create_widgets(self):
# Main frame # Main frame
main_frame = ttk.Frame(self.root, padding="10") main_frame = ttk.Frame(self.root, padding="10")
@@ -308,6 +481,11 @@ def main():
app = YouTubeArchiverGUI(root) app = YouTubeArchiverGUI(root)
# Only proceed if Python installation check passed
if not hasattr(app, 'output_queue'):
root.destroy()
return
# Handle window closing # Handle window closing
def on_closing(): def on_closing():
if app.is_downloading: if app.is_downloading: