add ability to install from .flatpakref file via drag and drop or command
This commit is contained in:
parent
6a4e165d04
commit
c6eab03914
2 changed files with 156 additions and 47 deletions
|
|
@ -901,6 +901,41 @@ def install_flatpak(app: AppStreamPackage, repo_name=None, system=False) -> tupl
|
||||||
return False, f"Installation failed: {e}"
|
return False, f"Installation failed: {e}"
|
||||||
return True, f"Successfully installed {app.id}"
|
return True, f"Successfully installed {app.id}"
|
||||||
|
|
||||||
|
def install_flatpakref(ref_file, system=False):
|
||||||
|
"""Add a new repository using a .flatpakrepo file"""
|
||||||
|
# Get existing repositories
|
||||||
|
installation = get_installation(system)
|
||||||
|
|
||||||
|
if not ref_file.endswith('.flatpakref'):
|
||||||
|
return False, "Flatpak ref file path or URL must end with .flatpakref extension."
|
||||||
|
|
||||||
|
if not os.path.exists(ref_file):
|
||||||
|
return False, f"Flatpak ref file '{ref_file}' does not exist."
|
||||||
|
|
||||||
|
# Read the flatpakref file
|
||||||
|
try:
|
||||||
|
with open(ref_file, 'rb') as f:
|
||||||
|
repo_data = f.read()
|
||||||
|
except IOError as e:
|
||||||
|
return False, f"Failed to read flatpakref file: {str(e)}"
|
||||||
|
|
||||||
|
# Convert the data to GLib.Bytes
|
||||||
|
repo_bytes = GLib.Bytes.new(repo_data)
|
||||||
|
|
||||||
|
installation = get_installation(system)
|
||||||
|
|
||||||
|
transaction = Flatpak.Transaction.new_for_installation(installation)
|
||||||
|
|
||||||
|
# Add the install operation
|
||||||
|
transaction.add_install_flatpakref(repo_bytes)
|
||||||
|
# Run the transaction
|
||||||
|
try:
|
||||||
|
transaction.run()
|
||||||
|
except GLib.Error as e:
|
||||||
|
return False, f"Installation failed: {e}"
|
||||||
|
return True, f"Successfully installed {ref_file}"
|
||||||
|
|
||||||
|
|
||||||
def remove_flatpak(app: AppStreamPackage, repo_name=None, system=False) -> tuple[bool, str]:
|
def remove_flatpak(app: AppStreamPackage, repo_name=None, system=False) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
Remove a Flatpak package using transactions.
|
Remove a Flatpak package using transactions.
|
||||||
|
|
@ -1240,17 +1275,25 @@ def handle_remove_repo(args):
|
||||||
print(f"\nRepository removed successfully: {args.remove_repo}")
|
print(f"\nRepository removed successfully: {args.remove_repo}")
|
||||||
|
|
||||||
def handle_install(args, searcher):
|
def handle_install(args, searcher):
|
||||||
packagelist = searcher.search_flatpak(args.install, args.repo)
|
if args.install.endswith('.flatpakref'):
|
||||||
result_message = ""
|
|
||||||
for package in packagelist:
|
|
||||||
try:
|
try:
|
||||||
success, message = install_flatpak(package, args.repo, args.system)
|
success, message = install_flatpakref(args.install, args.system)
|
||||||
result_message = f"{message}"
|
result_message = f"{message}"
|
||||||
break
|
|
||||||
except GLib.Error as e:
|
except GLib.Error as e:
|
||||||
result_message = f"Installation of {args.install} failed: {str(e)}"
|
result_message = f"Installation of {args.install} failed: {str(e)}"
|
||||||
pass
|
print(result_message)
|
||||||
print(result_message)
|
else:
|
||||||
|
packagelist = searcher.search_flatpak(args.install, args.repo)
|
||||||
|
result_message = ""
|
||||||
|
for package in packagelist:
|
||||||
|
try:
|
||||||
|
success, message = install_flatpak(package, args.repo, args.system)
|
||||||
|
result_message = f"{message}"
|
||||||
|
break
|
||||||
|
except GLib.Error as e:
|
||||||
|
result_message = f"Installation of {args.install} failed: {str(e)}"
|
||||||
|
pass
|
||||||
|
print(result_message)
|
||||||
|
|
||||||
def handle_remove(args, searcher):
|
def handle_remove(args, searcher):
|
||||||
packagelist = searcher.search_flatpak(args.remove, args.repo)
|
packagelist = searcher.search_flatpak(args.remove, args.repo)
|
||||||
|
|
|
||||||
146
main.py
146
main.py
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
import sys
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
gi.require_version("GLib", "2.0")
|
gi.require_version("GLib", "2.0")
|
||||||
gi.require_version("Flatpak", "1.0")
|
gi.require_version("Flatpak", "1.0")
|
||||||
|
|
@ -32,6 +33,11 @@ class MainWindow(Gtk.Window):
|
||||||
# Set window size
|
# Set window size
|
||||||
self.set_default_size(1280, 720)
|
self.set_default_size(1280, 720)
|
||||||
|
|
||||||
|
# Enable drag and drop
|
||||||
|
self.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
||||||
|
self.drag_dest_add_uri_targets()
|
||||||
|
self.connect("drag-data-received", self.on_drag_data_received)
|
||||||
|
|
||||||
# Define category groups and their titles
|
# Define category groups and their titles
|
||||||
self.category_groups = {
|
self.category_groups = {
|
||||||
'system': {
|
'system': {
|
||||||
|
|
@ -305,6 +311,27 @@ class MainWindow(Gtk.Window):
|
||||||
# Select Trending by default
|
# Select Trending by default
|
||||||
self.select_default_category()
|
self.select_default_category()
|
||||||
|
|
||||||
|
def on_drag_data_received(self, widget, context, x, y, data, info, time):
|
||||||
|
"""Handle drag and drop events"""
|
||||||
|
# Check if data is a URI list
|
||||||
|
if isinstance(data, int):
|
||||||
|
return
|
||||||
|
uri = data.get_uris()[0]
|
||||||
|
file_path = Gio.File.new_for_uri(uri).get_path()
|
||||||
|
if file_path and file_path.endswith('.flatpakref'):
|
||||||
|
self.handle_flatpakref_file(file_path)
|
||||||
|
if file_path and file_path.endswith('.flatpakrepo'):
|
||||||
|
self.handle_flatpakref_file(file_path)
|
||||||
|
context.finish(True, False, time)
|
||||||
|
|
||||||
|
def handle_flatpakref_file(self, file_path):
|
||||||
|
"""Handle .flatpakref file installation"""
|
||||||
|
self.on_install_clicked(file_path, None)
|
||||||
|
|
||||||
|
def handle_flatpakrepo_file(self, file_path):
|
||||||
|
"""Handle .flatpakrepo file installation"""
|
||||||
|
# Show waiting dialog
|
||||||
|
|
||||||
def create_header_bar(self):
|
def create_header_bar(self):
|
||||||
# Create horizontal bar
|
# Create horizontal bar
|
||||||
self.top_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
self.top_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
@ -1435,13 +1462,38 @@ class MainWindow(Gtk.Window):
|
||||||
# Show dialog
|
# Show dialog
|
||||||
self.waiting_dialog.show_all()
|
self.waiting_dialog.show_all()
|
||||||
|
|
||||||
def on_install_clicked(self, button, app):
|
def on_install_clicked(self, button=None, app=None):
|
||||||
"""Handle the Install button click with installation options"""
|
"""Handle the Install button click with installation options"""
|
||||||
details = app.get_details()
|
id=""
|
||||||
|
if button and app:
|
||||||
|
details = app.get_details()
|
||||||
|
title=f"Install {details['name']}?"
|
||||||
|
label=f"Install: {details['id']}?"
|
||||||
|
id=details['id']
|
||||||
|
# this is a stupid workaround for our button creator
|
||||||
|
# so that we can use the same function in drag and drop
|
||||||
|
# which of course does not have a button object
|
||||||
|
elif button and not app:
|
||||||
|
app = button
|
||||||
|
button = None
|
||||||
|
title=f"Install {app}?"
|
||||||
|
label=f"Install: {app}?"
|
||||||
|
else:
|
||||||
|
message_type=Gtk.MessageType.ERROR
|
||||||
|
finished_dialog = Gtk.MessageDialog(
|
||||||
|
transient_for=self,
|
||||||
|
modal=True,
|
||||||
|
destroy_with_parent=True,
|
||||||
|
message_type=message_type,
|
||||||
|
buttons=Gtk.ButtonsType.OK,
|
||||||
|
text="Error: No app specified"
|
||||||
|
)
|
||||||
|
finished_dialog.run()
|
||||||
|
finished_dialog.destroy()
|
||||||
|
return
|
||||||
# Create dialog
|
# Create dialog
|
||||||
dialog = Gtk.Dialog(
|
dialog = Gtk.Dialog(
|
||||||
title=f"Install {details['name']}?",
|
title=title,
|
||||||
transient_for=self,
|
transient_for=self,
|
||||||
modal=True,
|
modal=True,
|
||||||
destroy_with_parent=True,
|
destroy_with_parent=True,
|
||||||
|
|
@ -1458,7 +1510,7 @@ class MainWindow(Gtk.Window):
|
||||||
# Create repository dropdown
|
# Create repository dropdown
|
||||||
repo_combo = Gtk.ComboBoxText()
|
repo_combo = Gtk.ComboBoxText()
|
||||||
|
|
||||||
content_area.pack_start(Gtk.Label(label=f"Install: {details['id']}?"), False, False, 0)
|
content_area.pack_start(Gtk.Label(label=label), False, False, 0)
|
||||||
|
|
||||||
# Search for available repositories containing this app
|
# Search for available repositories containing this app
|
||||||
searcher = libflatpak_query.get_reposearcher(self.system_mode)
|
searcher = libflatpak_query.get_reposearcher(self.system_mode)
|
||||||
|
|
@ -1468,56 +1520,59 @@ class MainWindow(Gtk.Window):
|
||||||
content_area.pack_start(Gtk.Label(label="Installation Type: System"), False, False, 0)
|
content_area.pack_start(Gtk.Label(label="Installation Type: System"), False, False, 0)
|
||||||
|
|
||||||
# Populate repository dropdown
|
# Populate repository dropdown
|
||||||
available_repos = set()
|
if button and app:
|
||||||
repos = libflatpak_query.repolist(self.system_mode)
|
available_repos = set()
|
||||||
for repo in repos:
|
repos = libflatpak_query.repolist(self.system_mode)
|
||||||
if not repo.get_disabled():
|
for repo in repos:
|
||||||
search_results = searcher.search_flatpak(details['id'], repo.get_name())
|
if not repo.get_disabled():
|
||||||
if search_results:
|
search_results = searcher.search_flatpak(id, repo.get_name())
|
||||||
available_repos.add(repo)
|
if search_results:
|
||||||
|
available_repos.add(repo)
|
||||||
|
|
||||||
# Add repositories to dropdown
|
# Add repositories to dropdown
|
||||||
if available_repos:
|
if available_repos:
|
||||||
repo_combo.remove_all() # Clear any existing items
|
repo_combo.remove_all() # Clear any existing items
|
||||||
|
|
||||||
# Add all repositories
|
# Add all repositories
|
||||||
for repo in available_repos:
|
for repo in available_repos:
|
||||||
repo_combo.append_text(repo.get_name())
|
repo_combo.append_text(repo.get_name())
|
||||||
|
|
||||||
# Only show dropdown if there are multiple repositories
|
# Only show dropdown if there are multiple repositories
|
||||||
if len(available_repos) >= 2:
|
if len(available_repos) >= 2:
|
||||||
# Remove and re-add with dropdown visible
|
# Remove and re-add with dropdown visible
|
||||||
content_area.pack_start(repo_combo, False, False, 0)
|
content_area.pack_start(repo_combo, False, False, 0)
|
||||||
repo_combo.set_button_sensitivity(Gtk.SensitivityType.AUTO)
|
repo_combo.set_button_sensitivity(Gtk.SensitivityType.AUTO)
|
||||||
repo_combo.set_active(0)
|
repo_combo.set_active(0)
|
||||||
|
else:
|
||||||
|
# Remove and re-add without dropdown
|
||||||
|
content_area.remove(repo_combo)
|
||||||
|
repo_combo.set_active(0)
|
||||||
else:
|
else:
|
||||||
# Remove and re-add without dropdown
|
repo_combo.remove_all() # Clear any existing items
|
||||||
|
repo_combo.append_text("No repositories available")
|
||||||
content_area.remove(repo_combo)
|
content_area.remove(repo_combo)
|
||||||
repo_combo.set_active(0)
|
# Show dialog
|
||||||
|
dialog.show_all()
|
||||||
else:
|
else:
|
||||||
repo_combo.remove_all() # Clear any existing items
|
# Show dialog
|
||||||
repo_combo.append_text("No repositories available")
|
dialog.show_all()
|
||||||
content_area.remove(repo_combo)
|
|
||||||
|
|
||||||
# Show dialog
|
|
||||||
dialog.show_all()
|
|
||||||
|
|
||||||
# Run dialog
|
# Run dialog
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
if response == Gtk.ResponseType.OK:
|
if response == Gtk.ResponseType.OK:
|
||||||
selected_repo = repo_combo.get_active_text()
|
selected_repo = None
|
||||||
|
if button and app:
|
||||||
|
selected_repo = repo_combo.get_active_text()
|
||||||
# Perform installation
|
# Perform installation
|
||||||
def perform_installation():
|
def perform_installation():
|
||||||
# Show waiting dialog
|
# Show waiting dialog
|
||||||
GLib.idle_add(self.show_waiting_dialog)
|
GLib.idle_add(self.show_waiting_dialog)
|
||||||
|
if button and app:
|
||||||
success, message = libflatpak_query.install_flatpak(app, selected_repo, self.system_mode)
|
success, message = libflatpak_query.install_flatpak(app, selected_repo, self.system_mode)
|
||||||
|
else:
|
||||||
# Update UI on main thread
|
success, message = libflatpak_query.install_flatpakref(app, self.system_mode)
|
||||||
GLib.idle_add(lambda: self.on_task_complete(dialog, success, message))
|
GLib.idle_add(lambda: self.on_task_complete(dialog, success, message))
|
||||||
|
# Start spinner and begin installation
|
||||||
# Start spinner and begin installation
|
|
||||||
thread = threading.Thread(target=perform_installation)
|
thread = threading.Thread(target=perform_installation)
|
||||||
thread.daemon = True # Allow program to exit even if thread is still running
|
thread.daemon = True # Allow program to exit even if thread is still running
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
@ -1836,6 +1891,17 @@ class MainWindow(Gtk.Window):
|
||||||
self.on_category_clicked('trending', 'collections')
|
self.on_category_clicked('trending', 'collections')
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# Check for command line argument
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
arg = sys.argv[1]
|
||||||
|
if arg.endswith('.flatpakref'):
|
||||||
|
# Create a temporary window just to handle the installation
|
||||||
|
app = MainWindow()
|
||||||
|
app.handle_flatpakref_file(arg)
|
||||||
|
# Keep the window open for 5 seconds to show the result
|
||||||
|
GLib.timeout_add_seconds(5, Gtk.main_quit)
|
||||||
|
Gtk.main()
|
||||||
|
return
|
||||||
app = MainWindow()
|
app = MainWindow()
|
||||||
app.connect("destroy", Gtk.main_quit)
|
app.connect("destroy", Gtk.main_quit)
|
||||||
app.show_all()
|
app.show_all()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue