Skip to content

Commit fc39993

Browse files
authored
Release 4.1 (#538)
2 parents 6559e85 + 4e0b1c0 commit fc39993

10 files changed

Lines changed: 448 additions & 92 deletions

data/io.github.vikdevelop.SaveDesktop.metainfo.xml.in

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@
6666
</screenshots>
6767

6868
<releases>
69+
<release version="4.1" date="2026-07-05">
70+
<url>https://github.com/vikdevelop/SaveDesktop/releases/tag/4.0</url>
71+
<description>
72+
<ul>
73+
<li>Added support for Niri WM and LXQt DE</li>
74+
<li>Added a status window for showing the progress of Flatpak apps installation after configuration import</li>
75+
<li>Updated the GNOME runtime and 7-Zip to the latest versions</li>
76+
</ul>
77+
</description>
78+
</release>
6979
<release version="4.0" date="2026-02-22">
7080
<url>https://github.com/vikdevelop/SaveDesktop/releases/tag/4.0</url>
7181
<description>

io.github.vikdevelop.SaveDesktop.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@
4747
{
4848
"type" : "git",
4949
"url" : "https://github.com/ip7z/7zip.git",
50-
"tag" : "26.01",
51-
"commit" : "8c63d71ff886bda90c86db28466287f977374237"
50+
"tag" : "26.02",
51+
"commit" : "f9d78aff31a5f2521ae7ddbdc97c4a8855808959"
5252
}
5353
],
5454
"x-checker-data" : {

meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
project('savedesktop',
2-
version: '4.0',
2+
version: '4.1',
33
meson_version: '>= 1.0.0',
44
default_options: [ 'warning_level=2', 'werror=false', ],
55
)

po/savedesktop.pot

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,62 @@ msgstr ""
108108
msgid "Keep existing Flatpak apps and data"
109109
msgstr ""
110110

111+
#: src/gui/flatpaks_installer_window.py
112+
msgid "Installing your Flatpak apps..."
113+
msgstr ""
114+
115+
#: src/gui/flatpaks_installer_window.py
116+
msgid "You can continue using your computer; everything runs in the background. Once the installation of your Flatpak apps and, if applicable, your user data is complete, a message will appear here and you’ll also see a system notification. You can also show the progress below."
117+
msgstr ""
118+
119+
#: src/gui/flatpaks_installer_window.py
120+
msgid "Installation queue is empty. Nothing new to install."
121+
msgstr ""
122+
123+
#: src/gui/flatpaks_installer_window.py
124+
msgid "Copying the Flatpak's user data to ~/.var/app"
125+
msgstr ""
126+
127+
#: src/gui/flatpaks_installer_window.py
128+
msgid "✔ Copied Flatpak's user data"
129+
msgstr ""
130+
131+
#: src/gui/flatpaks_installer_window.py
132+
msgid "Cleaning up orphaned user data..."
133+
msgstr ""
134+
135+
#: src/gui/flatpaks_installer_window.py
136+
msgid "All useless apps and orphaned data have been removed"
137+
msgstr ""
138+
139+
#: src/gui/flatpaks_installer_window.py
140+
msgid "Installing {count} new apps"
141+
msgstr ""
142+
143+
#: src/gui/flatpaks_installer_window.py
144+
msgid "{app} is already available in the system."
145+
msgstr ""
146+
147+
#: src/gui/flatpaks_installer_window.py
148+
msgid "↓ Installing {app} ({current}/{total})"
149+
msgstr ""
150+
151+
#: src/gui/flatpaks_installer_window.py
152+
msgid "✔ Finished installing {app}"
153+
msgstr ""
154+
155+
#: src/gui/flatpaks_installer_window.py
156+
msgid "✔ All operations have been completed successfully."
157+
msgstr ""
158+
159+
#: src/gui/flatpaks_installer_window.py
160+
msgid "Your apps have been installed!"
161+
msgstr ""
162+
163+
#: src/gui/flatpaks_installer_window.py
164+
msgid "This window will be closed automatically in 2 minutes."
165+
msgstr ""
166+
111167
#: src/gui/templates/shortcuts_window.ui:50
112168
#: src/gui/more_options_dialog.py
113169
msgid "More options"

src/core/de_config.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def create_flatpak_autostart():
105105
"[Desktop Entry]\n"
106106
"Name=SaveDesktop (Flatpak Apps installer)\n"
107107
"Type=Application\n"
108-
f"Exec=python3 {CACHE}/workspace/flatpaks_installer.py\n"
108+
f"Exec=sh -c \"flatpak run io.github.vikdevelop.SaveDesktop --show-flatpaks-installer & python3 -u {CACHE}/workspace/flatpaks_installer.py > {CACHE}/workspace/log.pipe 2>&1\""
109109
)
110110

111111
print("[OK] Created Flatpak autostart")
@@ -296,8 +296,7 @@ def __init__(self):
296296
print("Saving KDE Plasma configuration...")
297297
os.makedirs("DE/xdg-config", exist_ok=True)
298298
os.makedirs("DE/xdg-data", exist_ok=True)
299-
os.system(f"cp -R {home}/.config/[k]* ./DE/xdg-config/")
300-
os.system(f"cp -R {home}/.local/share/[k]* ./DE/xdg-data/")
299+
self.save_kde_config_data_dirs()
301300
for src, dst in KDE_DIRS_SAVE:
302301
if os.path.isfile(src):
303302
safe_copy(src, os.path.join("DE", dst))
@@ -316,6 +315,36 @@ def save_dconf(self):
316315
os.system("dconf dump / > ./General/dconf-settings.ini")
317316
print("[OK] Saved dconf")
318317

318+
def save_kde_config_data_dirs(self):
319+
# Target dirs
320+
target_base_dir = "./DE"
321+
mapping = [
322+
(f"{self.home}/.config", Path(target_base_dir) / "xdg-config"),
323+
(f"{self.home}/.local/share", Path(target_base_dir) / "xdg-data"),
324+
]
325+
326+
for src_dir, dst_path in mapping:
327+
# Creating destination dir
328+
dst_path.mkdir(parents=True, exist_ok=True)
329+
330+
# Find folders begining with 'k' or 'K'
331+
for path_str in glob.glob(f"{src_dir}/[kK]*"):
332+
src_path = Path(path_str)
333+
334+
# Ignore kdeconnect
335+
if src_path.name.lower() == "kdeconnect":
336+
continue
337+
338+
target = dst_path / src_path.name
339+
340+
# Copying folders and files
341+
if src_path.is_dir():
342+
if target.exists():
343+
shutil.rmtree(target)
344+
shutil.copytree(src_path, target)
345+
else:
346+
shutil.copy2(src_path, target)
347+
319348
# ------------------------
320349
# Import pipeline
321350
# ------------------------

src/core/flatpaks_installer.py

Lines changed: 88 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python3
2-
import os, subprocess, shutil, json
2+
import os, subprocess, shutil, json, time
33
from pathlib import Path
44

55
CACHE_FLATPAK = f"{Path.home()}/.var/app/io.github.vikdevelop.SaveDesktop/cache/tmp/workspace"
@@ -24,109 +24,112 @@
2424

2525
if dest_dir:
2626
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:
2928
j = json.load(fl)
3029

3130
copy_data = j["copy-data"]
3231
install_flatpaks = j["install-flatpaks"]
3332
disabled_flatpaks = j["disabled-flatpaks"]
3433
keep_flatpaks = j["keep-flatpaks"]
3534

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 = {}
5237
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:
6140
if os.path.exists(file):
62-
print(f"[DEBUG] Reading: {file}")
6341
with open(file, 'r') as f:
64-
for line in f:
42+
for line in f.readlines():
6543
if 'flatpak' in line and 'install' in line:
6644
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)
10863
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)
112114
subprocess.run(['flatpak', 'uninstall', method, app, '--delete-data', '-y'])
115+
report_progress() # <--- SEND UPDATE TO UI
113116

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)
121128

122129
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)
124131

125132
# Remove the autostart file after finishing the operations
126133
autostart_file = f"{Path.home()}/.config/autostart/io.github.vikdevelop.SaveDesktop.Flatpak.desktop"
127134
if os.path.exists(autostart_file):
128135
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)

src/globals.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def get_app_environment():
8383
"MATE": {"de_name": "MATE", "dirs": [(f"{home}/.config/caja", "caja")]},
8484
"Deepin": {"de_name": "Deepin", "dirs": [(f"{home}/.config/deepin", "deepin"), (f"{home}/.local/share/deepin", "deepin-data")]},
8585
"Hyprland": {"de_name": "Hyprland", "dirs": [(f"{home}/.config/hypr", "hypr")]},
86+
"LXQt": {"de_name": "LXQt", "dirs": [(f"{home}/.config/lxqt", "lxqt")]},
87+
"niri": {"de_name": "Niri", "dirs": [(f"{home}/.config/niri", "niri")]},
8688
"KDE": {"de_name": "KDE Plasma"},
8789
}
8890

0 commit comments

Comments
 (0)