From 95c33c1665ea1866a46c29a7bc31e8b25a5017da Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 9 Jul 2020 20:04:50 +0200 Subject: Make /upload work in Flatpak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When /upload isn’t given an argument, it will instead open a file chooser and block poezio until the user selected a file. This will make poezio timeout from all rooms until the user is done choosing a file, but I didn’t find a good way to integrate GLib’s main loop with asyncio for now, and this can be fixed in a latter commit. --- plugins/upload.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 9 deletions(-) diff --git a/plugins/upload.py b/plugins/upload.py index c702dc49..94393d5f 100644 --- a/plugins/upload.py +++ b/plugins/upload.py @@ -9,11 +9,14 @@ This plugin adds a command to the chat tabs. .. glossary:: /upload - **Usage:** ``/upload `` + **Usage:** ``/upload [filename]`` Uploads the file to the preferred HTTP File Upload service (see XEP-0363) and fill the input with its URL. + If isn’t specified, use the FileChooser from + xdg-desktop-portal to ask the user which file to upload. + """ @@ -31,6 +34,83 @@ from poezio.core.structs import Completion from poezio.decorators import command_args_parser from poezio import tabs +try: + from gi.repository import Gio, GLib + from urllib.parse import urlparse, unquote + HAVE_GLIB = True +except ImportError: + HAVE_GLIB = False + +def open_file_xdg_desktop_portal(): + ''' + Use org.freedesktop.portal.FileChooser from xdg-desktop-portal to open a + file chooser dialog. + + This method uses GDBus from GLib, and specifically runs its mainloop which + will block the entirety of poezio until it is done, which might cause us to + drop from rooms and such if the user isn’t quick enough at choosing the + file… + + See https://flatpak.github.io/xdg-desktop-portal/portal-docs.html + ''' + if not HAVE_GLIB: + return None + + def get_file(connection, + sender, + path, + interface, + signal, + params): + nonlocal return_path + # TODO: figure out how to raise an exception to the outside of the GLib + # loop. + if not isinstance(params, GLib.Variant): + loop.quit() + return + response_code, results = params.unpack() + if response_code != 0: + loop.quit() + return + uris = results['uris'] + if len(uris) != 1: + loop.quit() + return + parsed_uri = urlparse(uris[0]) + if parsed_uri.scheme != "file": + loop.quit() + return + return_path = unquote(parsed_uri.path) + loop.quit() + + return_path = None + proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION, + Gio.DBusProxyFlags.NONE, + None, + 'org.freedesktop.portal.Desktop', + '/org/freedesktop/portal/desktop', + 'org.freedesktop.portal.FileChooser', + None) + + try: + handle = proxy.OpenFile('(ssa{sv})', '', 'poezio', { + 'accept_label': GLib.Variant('s', '_Upload'), + }) + except GLib.Error: + return None + conn = proxy.get_connection() + conn.signal_subscribe('org.freedesktop.portal.Desktop', + 'org.freedesktop.portal.Request', + 'Response', + handle, + None, + Gio.DBusSignalFlags.NO_MATCH_RULE, + get_file) + + loop = GLib.MainLoop() + loop.run() + return return_path + class Plugin(BasePlugin): dependencies = {'embed'} @@ -50,7 +130,12 @@ class Plugin(BasePlugin): short='Upload a file', completion=self.completion_filename) - async def upload(self, filename) -> Optional[str]: + async def upload(self, filename: Optional[str]) -> Optional[str]: + if filename is None: + filename = open_file_xdg_desktop_portal() + if filename is None: + self.api.information('Failed to query which file to upload.', 'Error') + return None try: url = await self.core.xmpp['xep_0363'].upload_file(filename) except UploadServiceNotFound: @@ -66,18 +151,18 @@ class Plugin(BasePlugin): return None return url - async def send_upload(self, filename): + async def send_upload(self, filename: Optional[str]): url = await self.upload(filename) if url is not None: self.embed.embed_image_url(url) - @command_args_parser.quoted(1) + @command_args_parser.quoted(0, 1) def command_upload(self, args): - if args is None: - self.core.command.help('upload') - return - filename, = args - filename = expanduser(filename) + if args: + filename, = args + filename = expanduser(filename) + else: + filename = None asyncio.ensure_future(self.send_upload(filename)) @staticmethod @@ -85,3 +170,8 @@ class Plugin(BasePlugin): txt = expanduser(the_input.get_text()[8:]) files = glob(txt + '*') return Completion(the_input.auto_completion, files, quotify=False) + + +if __name__ == '__main__': + path = open_file_xdg_desktop_portal() + print('Obtained path', path) -- cgit v1.2.3