diff --git a/windows/python.msix b/windows/python.msix new file mode 100644 index 0000000..3904ff2 Binary files /dev/null and b/windows/python.msix differ diff --git a/youtube_archiver_gui.py b/youtube_archiver_gui.py index 010391a..3ef7bad 100644 --- a/youtube_archiver_gui.py +++ b/youtube_archiver_gui.py @@ -10,6 +10,7 @@ import subprocess import threading import sys import os +import platform from pathlib import Path import queue @@ -35,12 +36,184 @@ class YouTubeArchiverGUI: self.download_process = None 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.check_dependencies() # Start checking queue for output updates 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): # Main frame main_frame = ttk.Frame(self.root, padding="10") @@ -308,6 +481,11 @@ def main(): app = YouTubeArchiverGUI(root) + # Only proceed if Python installation check passed + if not hasattr(app, 'output_queue'): + root.destroy() + return + # Handle window closing def on_closing(): if app.is_downloading: