|
1 | 1 | #!/usr/bin/env python3 |
2 | | -import os, subprocess, shutil, json |
| 2 | +import os, subprocess, shutil, json, time |
3 | 3 | from pathlib import Path |
4 | 4 |
|
5 | 5 | CACHE_FLATPAK = f"{Path.home()}/.var/app/io.github.vikdevelop.SaveDesktop/cache/tmp/workspace" |
|
24 | 24 |
|
25 | 25 | if dest_dir: |
26 | 26 | os.chdir(dest_dir) |
27 | | - # Check Flatpak user preferences |
28 | | - with open(f"flatpak-prefs.json") as fl: |
| 27 | + with open("flatpak-prefs.json") as fl: |
29 | 28 | j = json.load(fl) |
30 | 29 |
|
31 | 30 | copy_data = j["copy-data"] |
32 | 31 | install_flatpaks = j["install-flatpaks"] |
33 | 32 | disabled_flatpaks = j["disabled-flatpaks"] |
34 | 33 | keep_flatpaks = j["keep-flatpaks"] |
35 | 34 |
|
36 | | - # Restore Flatpak user data archive |
37 | | - if copy_data: |
38 | | - print("[DEBUG] Copying the Flatpak's user data to ~/.var/app") |
39 | | - if os.path.exists(f"app"): |
40 | | - print("Copying the Flatpak apps' user data to the ~/.var/app directory (backward compatibility mode)") |
41 | | - os.system(f"cp -au ./app/ ~/.var/") |
42 | | - |
43 | | - archive_file = "Flatpak_Apps/flatpak-apps-data.tgz" if os.path.exists("Flatpak_Apps/flatpak-apps-data.tgz") else "flatpak-apps-data.tgz" |
44 | | - |
45 | | - if os.path.exists(archive_file): |
46 | | - tar_cmd = ["tar", "-xzvf", archive_file, "-C", f"{Path.home()}/.var"] |
47 | | - for d_app in disabled_flatpaks: |
48 | | - tar_cmd.extend([f"--exclude={d_app}", f"--exclude=app/{d_app}"]) |
49 | | - subprocess.run(tar_cmd) |
50 | | - |
51 | | - # Load Flatpaks from bash scripts |
| 35 | + # pre-calculation (math only, no actions) |
| 36 | + desired_flatpaks = {} |
52 | 37 | if install_flatpaks: |
53 | | - print("[DEBUG] Scanning the Bash scripts...") |
54 | | - if os.path.exists("Flatpak_Apps"): |
55 | | - installed_flatpaks_files = ['Flatpak_Apps/installed_flatpaks.sh', 'Flatpak_Apps/installed_user_flatpaks.sh'] |
56 | | - else: |
57 | | - installed_flatpaks_files = ['installed_flatpaks.sh', 'installed_user_flatpaks.sh'] |
58 | | - |
59 | | - desired_flatpaks = {} |
60 | | - for file in installed_flatpaks_files: |
| 38 | + files_to_check = ['Flatpak_Apps/installed_flatpaks.sh', 'Flatpak_Apps/installed_user_flatpaks.sh'] if os.path.exists("Flatpak_Apps") else ['installed_flatpaks.sh', 'installed_user_flatpaks.sh'] |
| 39 | + for file in files_to_check: |
61 | 40 | if os.path.exists(file): |
62 | | - print(f"[DEBUG] Reading: {file}") |
63 | 41 | with open(file, 'r') as f: |
64 | | - for line in f: |
| 42 | + for line in f.readlines(): |
65 | 43 | if 'flatpak' in line and 'install' in line: |
66 | 44 | parts = line.split() |
67 | | - |
68 | | - app_id = None |
69 | | - for part in parts: |
70 | | - if "." in part and not part.startswith("-"): |
71 | | - app_id = part |
72 | | - break |
73 | | - |
74 | | - if not app_id: |
75 | | - continue |
76 | | - |
77 | | - if app_id in disabled_flatpaks: |
78 | | - print(f"[SKIP] Disabled app: {app_id}") |
79 | | - continue |
80 | | - |
81 | | - method = "--user" if "--user" in parts else "--system" |
82 | | - desired_flatpaks[app_id] = method |
83 | | - print(f"[DEBUG] Added to the installation queue: {app_id} ({method})") |
84 | | - |
85 | | - if not desired_flatpaks: |
86 | | - print("[DEBUG] The installation queue is empty. It couldn't find any valid Flatpaks to install.") |
87 | | - |
88 | | - # Get currently installed Flatpaks |
89 | | - system_flatpak_dir = '/var/lib/flatpak/app' |
90 | | - user_flatpak_dir = os.path.expanduser('~/.local/share/flatpak/app') |
91 | | - installed_apps = {} |
92 | | - for directory, method in [(system_flatpak_dir, '--system'), (user_flatpak_dir, '--user')]: |
93 | | - if os.path.exists(directory): |
94 | | - for app in os.listdir(directory): |
95 | | - if os.path.isdir(os.path.join(directory, app)): |
96 | | - installed_apps[app] = method |
97 | | - |
98 | | - for app, method in desired_flatpaks.items(): |
99 | | - if app not in installed_apps: |
100 | | - print(f"[INSTALL] Installing {app} ({method})") |
101 | | - if method == '--user': |
102 | | - os.system("flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo") |
103 | | - subprocess.run(['flatpak', 'install', method, 'flathub', app, '-y']) |
104 | | - else: |
105 | | - print(f"[INFO] {app} is available in the system.") |
106 | | - |
107 | | - # Remove Flatpaks, which are not in the list (only if it's allowed by the user) |
| 45 | + app_id = next((p for p in parts if "." in p and not p.startswith("-")), None) |
| 46 | + if app_id and app_id not in disabled_flatpaks: |
| 47 | + desired_flatpaks[app_id] = "--user" if "--user" in parts else "--system" |
| 48 | + |
| 49 | + installed_apps = {} |
| 50 | + for directory, method in [('/var/lib/flatpak/app', '--system'), (os.path.expanduser('~/.local/share/flatpak/app'), '--user')]: |
| 51 | + if os.path.exists(directory): |
| 52 | + for app in os.listdir(directory): |
| 53 | + if os.path.isdir(os.path.join(directory, app)): |
| 54 | + installed_apps[app] = method |
| 55 | + |
| 56 | + apps_to_install = {app: method for app, method in desired_flatpaks.items() if app not in installed_apps} |
| 57 | + apps_to_remove = {app: method for app, method in installed_apps.items() if app not in desired_flatpaks} if not keep_flatpaks else {} |
| 58 | + |
| 59 | + # Calculate exact total steps |
| 60 | + total_steps = 0 |
| 61 | + if copy_data: total_steps += 1 |
| 62 | + if install_flatpaks: total_steps += len(apps_to_install) |
108 | 63 | if not keep_flatpaks: |
109 | | - for app, method in installed_apps.items(): |
110 | | - if app not in desired_flatpaks: |
111 | | - print(f"[REMOVE] {method.title()} Flatpak: {app}") |
| 64 | + total_steps += len(apps_to_remove) |
| 65 | + total_steps += 1 # one extra step for orphaned dir cleanup |
| 66 | + |
| 67 | + current_step = 0 |
| 68 | + |
| 69 | + def report_progress(): |
| 70 | + """Helper to print the hidden progress tag for the GTK UI""" |
| 71 | + global current_step |
| 72 | + current_step += 1 |
| 73 | + print(f"[PROGRESS] {current_step}/{total_steps}", flush=True) |
| 74 | + |
| 75 | + if total_steps == 0: |
| 76 | + print("There's no need to install any new apps, since they're all available on your system.", flush=True) |
| 77 | + else: |
| 78 | + # phase 2: execution |
| 79 | + |
| 80 | + # Restore Data |
| 81 | + if copy_data: |
| 82 | + print("[COPY] Copying the Flatpak's user data to ~/.var/app", flush=True) |
| 83 | + if os.path.exists("app"): |
| 84 | + os.system("cp -au ./app/ ~/.var/") |
| 85 | + archive_file = "Flatpak_Apps/flatpak-apps-data.tgz" if os.path.exists("Flatpak_Apps/flatpak-apps-data.tgz") else "flatpak-apps-data.tgz" |
| 86 | + if os.path.exists(archive_file): |
| 87 | + tar_cmd = ["tar", "-xzf", archive_file, "-C", f"{Path.home()}/.var"] |
| 88 | + for d_app in disabled_flatpaks: |
| 89 | + tar_cmd.extend([f"--exclude={d_app}", f"--exclude=app/{d_app}"]) |
| 90 | + subprocess.run(tar_cmd) |
| 91 | + print("✔ Copied Flatpak's user data", flush=True) |
| 92 | + report_progress() # sends update to UI |
| 93 | + |
| 94 | + # Install Apps |
| 95 | + if install_flatpaks: |
| 96 | + for app in desired_flatpaks: |
| 97 | + if app in installed_apps: |
| 98 | + print(f"[INFO] {app} is already available in the system.", flush=True) |
| 99 | + |
| 100 | + if apps_to_install: |
| 101 | + print(f"Installing {len(apps_to_install)} new apps", flush=True) |
| 102 | + for i, (app, method) in enumerate(apps_to_install.items(), start=1): |
| 103 | + print(f"↓ Installing {app} ({i}/{len(apps_to_install)})", flush=True) |
| 104 | + if method == '--user': |
| 105 | + os.system("flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo") |
| 106 | + subprocess.run(['flatpak', 'install', method, 'flathub', app, '-y']) |
| 107 | + print(f"✔ Finished installing {app}", flush=True) |
| 108 | + report_progress() # sends update to UI |
| 109 | + |
| 110 | + # Remove Apps and Cleanup |
| 111 | + if not keep_flatpaks: |
| 112 | + for app, method in apps_to_remove.items(): |
| 113 | + print(f"[REMOVE] {method.title()} Flatpak: {app}", flush=True) |
112 | 114 | subprocess.run(['flatpak', 'uninstall', method, app, '--delete-data', '-y']) |
| 115 | + report_progress() # <--- SEND UPDATE TO UI |
113 | 116 |
|
114 | | - # Remove orphaned ~/.var/app directories |
115 | | - user_var_app = Path.home() / ".var/app" |
116 | | - if user_var_app.exists(): |
117 | | - for app_dir in user_var_app.iterdir(): |
118 | | - if app_dir.is_dir() and app_dir.name not in desired_flatpaks: |
119 | | - print(f"[REMOVE] Orphaned Flatpak user data: {app_dir.name}") |
120 | | - shutil.rmtree(app_dir) |
| 117 | + print("[REMOVE] Cleaning up orphaned user data...", flush=True) |
| 118 | + user_var_app = Path.home() / ".var/app" |
| 119 | + if user_var_app.exists(): |
| 120 | + for app_dir in user_var_app.iterdir(): |
| 121 | + if app_dir.is_dir() and app_dir.name not in desired_flatpaks: |
| 122 | + print(f" -> Deleted: {app_dir.name}", flush=True) |
| 123 | + shutil.rmtree(app_dir) |
| 124 | + print("[OK] All useless apps and orphaned data have been removed", flush=True) |
| 125 | + report_progress() # <--- SEND UPDATE TO UI |
| 126 | + |
| 127 | + print("✔ All operations have been completed successfully.", flush=True) |
121 | 128 |
|
122 | 129 | else: |
123 | | - print("Nothing to do.") |
| 130 | + print("There's no need to install any new apps, since they're all available on your system.", flush=True) |
124 | 131 |
|
125 | 132 | # Remove the autostart file after finishing the operations |
126 | 133 | autostart_file = f"{Path.home()}/.config/autostart/io.github.vikdevelop.SaveDesktop.Flatpak.desktop" |
127 | 134 | if os.path.exists(autostart_file): |
128 | 135 | os.remove(autostart_file) |
129 | | - |
130 | | -# Remove the cache dir after finishing the operations |
131 | | -if os.path.exists(CACHE_FLATPAK): |
132 | | - shutil.rmtree(CACHE_FLATPAK) |
|
0 commit comments