|
| 1 | +import tkinter as tk |
| 2 | +from tkinter import messagebox |
| 3 | +import subprocess |
| 4 | +import socket |
| 5 | +import threading |
| 6 | +import time |
| 7 | +import os |
| 8 | + |
| 9 | + |
| 10 | +def install_ess(): |
| 11 | + # Show password prompt in a clean window |
| 12 | + ess_prompt = tk.Toplevel(root) |
| 13 | + ess_prompt.title("Authentication Required") |
| 14 | + ess_prompt.geometry("400x100") |
| 15 | + ess_prompt.resizable(False, False) |
| 16 | + |
| 17 | + tk.Label( |
| 18 | + ess_prompt, |
| 19 | + text="Please enter your password in the terminal to install essential tools.", |
| 20 | + font=("Arial", 10), wraplength=380, justify="center" |
| 21 | + ).pack(pady=20) |
| 22 | + |
| 23 | + # Auto-close the password prompt after 8 seconds |
| 24 | + ess_prompt.after(8000, ess_prompt.destroy) |
| 25 | + |
| 26 | + # Run installation in background |
| 27 | + def run_ess_install(): |
| 28 | + try: |
| 29 | + subprocess.run( |
| 30 | + ["sudo", "apt", "install", "-y", "git", "vi", "vim", "sudo", "curl", "wget"], |
| 31 | + check=True, |
| 32 | + stdout=subprocess.DEVNULL, |
| 33 | + stderr=subprocess.DEVNULL |
| 34 | + ) |
| 35 | + messagebox.showinfo("Success", "✅ Essential tools installed successfully.") |
| 36 | + except subprocess.CalledProcessError: |
| 37 | + messagebox.showerror("Installation Failed", "❌ Failed to install essential tools.") |
| 38 | + |
| 39 | + threading.Thread(target=run_ess_install).start() |
| 40 | + |
| 41 | + |
| 42 | + |
| 43 | +# Predefined .deb app URLs |
| 44 | +PRESET_DEB_APPS = { |
| 45 | + "Brave Browser (.deb)": "https://brave.com/latest.deb", |
| 46 | + "VS Code (.deb)": "https://update.code.visualstudio.com/latest/linux-deb-x64/stable" |
| 47 | +} |
| 48 | + |
| 49 | +# Check for internet connection |
| 50 | +def is_connected(): |
| 51 | + try: |
| 52 | + socket.create_connection(("1.1.1.1", 53), timeout=3) |
| 53 | + return True |
| 54 | + except OSError: |
| 55 | + return False |
| 56 | + |
| 57 | +# Password prompt |
| 58 | +def show_password_prompt(): |
| 59 | + prompt = tk.Toplevel() |
| 60 | + prompt.title("Authentication Required") |
| 61 | + prompt.geometry("350x100") |
| 62 | + prompt.resizable(False, False) |
| 63 | + tk.Label(prompt, text="Please enter your password in the terminal...", font=("Arial", 11)).pack(pady=20) |
| 64 | + prompt.after(8000, prompt.destroy) |
| 65 | + |
| 66 | +# Log window |
| 67 | +def show_install_log(): |
| 68 | + log_window = tk.Toplevel() |
| 69 | + log_window.title("Installation Progress") |
| 70 | + log_window.geometry("500x350") |
| 71 | + log_window.resizable(False, False) |
| 72 | + log_text = tk.Text(log_window, wrap=tk.WORD, font=("Arial", 11)) |
| 73 | + log_text.pack(expand=True, fill='both') |
| 74 | + return log_text |
| 75 | + |
| 76 | +def install_deb(app_name): |
| 77 | + |
| 78 | + |
| 79 | + """ |
| 80 | + Download and install a .deb package for the given app_name in the background. |
| 81 | + Output is suppressed. |
| 82 | + """ |
| 83 | + url = PRESET_DEB_APPS.get(app_name) |
| 84 | + if not url: |
| 85 | + return # Unknown app |
| 86 | + |
| 87 | + deb_filename = f"{app_name.lower().replace(' ', '_').replace('(.deb)', '').replace('.', '')}.deb" |
| 88 | + try: |
| 89 | + # Download .deb file silently |
| 90 | + subprocess.run( |
| 91 | + ["wget", "-q", "-O", deb_filename, url], |
| 92 | + check=True, |
| 93 | + stdout=subprocess.DEVNULL, |
| 94 | + stderr=subprocess.DEVNULL |
| 95 | + ) |
| 96 | + # Install .deb file silently |
| 97 | + subprocess.run( |
| 98 | + ["sudo", "dpkg", "-i", deb_filename], |
| 99 | + check=True, |
| 100 | + stdout=subprocess.DEVNULL, |
| 101 | + stderr=subprocess.DEVNULL |
| 102 | + ) |
| 103 | + # Fix broken dependencies silently |
| 104 | + subprocess.run( |
| 105 | + ["sudo", "apt", "--fix-broken", "install", "-y"], |
| 106 | + check=True, |
| 107 | + stdout=subprocess.DEVNULL, |
| 108 | + stderr=subprocess.DEVNULL |
| 109 | + ) |
| 110 | + except subprocess.CalledProcessError: |
| 111 | + pass # Optionally handle errors here |
| 112 | + finally: |
| 113 | + if os.path.exists(deb_filename): |
| 114 | + os.remove(deb_filename) |
| 115 | + |
| 116 | +# Install selected apps |
| 117 | +def install_selected(): |
| 118 | + selected_apps = [] |
| 119 | + |
| 120 | + # Collect selected apt apps |
| 121 | + for name in apt_apps: |
| 122 | + if vars_check[name].get(): |
| 123 | + selected_apps.append((name, 'apt', apt_apps[name])) |
| 124 | + |
| 125 | + # Collect selected deb apps |
| 126 | + for name in deb_apps: |
| 127 | + if vars_check[name].get(): |
| 128 | + selected_apps.append((name, 'deb', deb_apps[name])) |
| 129 | + |
| 130 | + if not selected_apps: |
| 131 | + messagebox.showinfo("No Selection", "Please select at least one app to install.") |
| 132 | + return |
| 133 | + |
| 134 | + show_password_prompt() |
| 135 | + log_output = show_install_log() |
| 136 | + |
| 137 | + def run_installation(): |
| 138 | + for name, app_type, cmd in selected_apps: |
| 139 | + try: |
| 140 | + log_output.insert(tk.END, f"📦 Starting {name}...\n") |
| 141 | + log_output.see(tk.END) |
| 142 | + |
| 143 | + if app_type == 'apt': |
| 144 | + log_output.insert(tk.END, f"Installing {name} using APT...\n") |
| 145 | + subprocess.run(["sudo", "apt", "install", "-y", cmd], check=True) |
| 146 | + log_output.insert(tk.END, f"✅ {name} installed successfully.\n\n") |
| 147 | + elif app_type == 'deb': |
| 148 | + # Use silent installer for presets |
| 149 | + if name in PRESET_DEB_APPS: |
| 150 | + log_output.insert(tk.END, f"Downloading and installing {name} (.deb) in background...\n") |
| 151 | + # Run silently in this thread (so install log is not blocked) |
| 152 | + install_deb(name) |
| 153 | + log_output.insert(tk.END, f"✅ {name} installed successfully (silent mode).\n\n") |
| 154 | + else: |
| 155 | + log_output.insert(tk.END, f"Downloading and installing {name} (.deb)...\n") |
| 156 | + subprocess.run(cmd, shell=True, check=True) |
| 157 | + log_output.insert(tk.END, f"✅ {name} installed successfully.\n\n") |
| 158 | + except subprocess.CalledProcessError: |
| 159 | + log_output.insert(tk.END, f"❌ Failed to install {name}\n\n") |
| 160 | + log_output.see(tk.END) |
| 161 | + |
| 162 | + log_output.insert(tk.END, "🎉 All installations attempted.\n") |
| 163 | + log_output.see(tk.END) |
| 164 | + |
| 165 | + threading.Thread(target=run_installation).start() |
| 166 | + |
| 167 | +# Launch GUI if internet is available |
| 168 | +if is_connected(): |
| 169 | + # APT apps |
| 170 | + apt_apps = { |
| 171 | + "FireFox":"firefox-esr", |
| 172 | + "Google Chromium":"chromium", |
| 173 | + "mpv":'mpv', |
| 174 | + "VLC Media Player": "vlc", |
| 175 | + "GIMP": "gimp", |
| 176 | + "Telegram":"" |
| 177 | + |
| 178 | + } |
| 179 | + |
| 180 | + # .DEB apps (add more with proper wget & dpkg commands) |
| 181 | + deb_apps = { |
| 182 | + "Brave Browser (.deb)": "wget -O brave.deb https://brave.com/latest.deb && sudo dpkg -i brave.deb && sudo apt --fix-broken install -y && rm brave.deb", |
| 183 | + "VS Code (.deb)": "wget -O vscode.deb https://update.code.visualstudio.com/latest/linux-deb-x64/stable && sudo dpkg -i vscode.deb && sudo apt --fix-broken install -y && rm vscode.deb", |
| 184 | + "AnyDesk (.deb)": "wget -O anydesk.deb https://download.anydesk.com/linux/anydesk_6.2.1-1_amd64.deb && sudo dpkg -i anydesk.deb && sudo apt --fix-broken install -y && rm anydesk.deb" |
| 185 | + } |
| 186 | + |
| 187 | + root = tk.Tk() |
| 188 | + root.title("Setbian - Smart Debian App Installer") |
| 189 | + root.geometry("500x600") |
| 190 | + root.resizable(False, False) |
| 191 | + |
| 192 | + tk.Label(root, text="📦 Select Apps to Install:", font=("Arial", 14, "bold")).pack(pady=10) |
| 193 | + vars_check = {} |
| 194 | + |
| 195 | + # APT checkboxes |
| 196 | + for app in apt_apps: |
| 197 | + var = tk.BooleanVar() |
| 198 | + chk = tk.Checkbutton(root, text=app, variable=var, font=("Arial", 12)) |
| 199 | + chk.pack(anchor='w', padx=40) |
| 200 | + vars_check[app] = var |
| 201 | + |
| 202 | + tk.Label(root, text="📂 Install more apps", font=("Arial", 14, "bold"), fg="blue").pack(pady=10) |
| 203 | + |
| 204 | + # DEB checkboxes |
| 205 | + for app in deb_apps: |
| 206 | + var = tk.BooleanVar() |
| 207 | + chk = tk.Checkbutton(root, text=app, variable=var, font=("Arial", 12)) |
| 208 | + chk.pack(anchor='w', padx=40) |
| 209 | + vars_check[app] = var |
| 210 | + install_ess() |
| 211 | + # Install button |
| 212 | + install_btn = tk.Button( |
| 213 | + root, text="Install Selected", font=("Arial", 12, "bold"), |
| 214 | + bg="#4CAF50", fg="white", command=install_selected |
| 215 | + ) |
| 216 | + install_btn.pack(pady=20) |
| 217 | + |
| 218 | + root.mainloop() |
| 219 | + |
| 220 | +else: |
| 221 | + root = tk.Tk() |
| 222 | + root.withdraw() |
| 223 | + messagebox.showerror( |
| 224 | + "No Internet Connection", |
| 225 | + "Please connect to the internet and relaunch Setbian.\n\nThank you!" |
| 226 | + ) |
0 commit comments