plugins: add basic scheme plugin support
This commit is contained in:
parent
af349f5ac2
commit
b884aed3a8
|
@ -7,6 +7,7 @@ import logging
|
|||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from importlib import import_module
|
||||
from math import inf
|
||||
from pathlib import Path
|
||||
from typing import Optional, Tuple
|
||||
|
@ -86,6 +87,7 @@ class Browser:
|
|||
self.last_download: Optional[Tuple[MimeType, Path]] = None
|
||||
self.identities = {}
|
||||
self.search_res_lines = []
|
||||
self.plugins = []
|
||||
self._current_url = ""
|
||||
|
||||
@property
|
||||
|
@ -176,6 +178,12 @@ class Browser:
|
|||
if not self.history.load():
|
||||
logging.warning("Could not load history file.")
|
||||
|
||||
# Load plugins.
|
||||
self.load_plugins()
|
||||
|
||||
# If there has been any issue to load user files, show them instead of
|
||||
# automatically moving forward. Else either open the URL requested or
|
||||
# show the home page.
|
||||
if failed_to_load:
|
||||
error_msg = (
|
||||
f"Failed to open some local data: {', '.join(failed_to_load)}. "
|
||||
|
@ -454,6 +462,14 @@ class Browser:
|
|||
else:
|
||||
self.set_status_error("Unknown page.")
|
||||
|
||||
else:
|
||||
from bebop.plugins import SchemePlugin
|
||||
plugins = (p for p in self.plugins if isinstance(p, SchemePlugin))
|
||||
plugin = next(filter(lambda p: p.scheme == scheme, plugins), None)
|
||||
if plugin:
|
||||
result_url = plugin.open_url(self, url)
|
||||
if history and result_url:
|
||||
self.history.push(result_url)
|
||||
else:
|
||||
self.set_status_error(f"Protocol '{scheme}' not supported.")
|
||||
|
||||
|
@ -828,3 +844,22 @@ class Browser:
|
|||
self.set_status(f"Result {index}/{max_index}")
|
||||
self.page_pad.current_line = next_line
|
||||
self.refresh_windows()
|
||||
|
||||
def load_plugins(self):
|
||||
"""Load installed and configured plugins."""
|
||||
for plugin_name in self.config["enabled_plugins"]:
|
||||
module_name = f"bebop_{plugin_name}"
|
||||
|
||||
try:
|
||||
module = import_module(module_name)
|
||||
except ImportError as exc:
|
||||
logging.error(f"Could not load module {module_name}: {exc}")
|
||||
continue
|
||||
|
||||
try:
|
||||
self.plugins.append(module.plugin) # type: ignore
|
||||
except AttributeError:
|
||||
logging.error(f"Module {module_name} does not export a plugin.")
|
||||
continue
|
||||
|
||||
logging.info(f"Loaded plugin {plugin_name}.")
|
||||
|
|
|
@ -30,6 +30,7 @@ DEFAULT_CONFIG = {
|
|||
],
|
||||
"scroll_step": 3,
|
||||
"persistent_history": False,
|
||||
"enabled_plugins": [],
|
||||
}
|
||||
|
||||
RENDER_MODES = ("fancy", "dumb")
|
||||
|
|
|
@ -64,6 +64,7 @@ Here are the available options:
|
|||
* command_editor (see note 1): command to use for editing cli input.
|
||||
* connect_timeout (int): seconds before connection times out.
|
||||
* download_path (string): download path.
|
||||
* enabled_plugins: (see note 4): plugin names to load.
|
||||
* external_command_default (see note 1): default command to open files.
|
||||
* external_commands (see note 2): commands to open various files.
|
||||
* generate_client_cert_command (see note 3): command to generate a client cert.
|
||||
|
@ -77,11 +78,13 @@ Here are the available options:
|
|||
|
||||
Notes:
|
||||
|
||||
1: for the "command" parameters such as source_editor and command_editor, a string list is used to separate the different program arguments, e.g. if you wish to use `vim -c 'startinsert'`, you should write the list `["vim", "-c", "startinsert"]`. In both case, a temporary or regular file name will be appended to this command when run.
|
||||
1: For the "command" parameters such as source_editor and command_editor, a string list is used to separate the different program arguments, e.g. if you wish to use `vim -c 'startinsert'`, you should write the list `["vim", "-c", "startinsert"]`. In both case, a temporary or regular file name will be appended to this command when run.
|
||||
|
||||
2: the external_commands dict maps MIME types to commands just as above. For example, if you want to open video files with VLC and audio files in Clementine, you can use the following dict: `{"audio": ["clementine"], "video": ["vlc"]}`. For now only "main" MIME types are supported, i.e. you cannot specify precise types like "audio/flac", just "audio".
|
||||
2: The external_commands dict maps MIME types to commands just as above. For example, if you want to open video files with VLC and audio files in Clementine, you can use the following dict: `{"audio": ["clementine"], "video": ["vlc"]}`. For now only "main" MIME types are supported, i.e. you cannot specify precise types like "audio/flac", just "audio".
|
||||
|
||||
3: the generate_client_cert_command uses the same format as other commands (specified in note 1 above), with the exception that if the strings "{cert_path}", "{key_path}" or "{common_name}" are present in any string for the list, they will be replaced respectively by the certificate output path, the key output path and the CN to use.
|
||||
3: The generate_client_cert_command uses the same format as other commands (specified in note 1 above), with the exception that if the strings "{cert_path}", "{key_path}" or "{common_name}" are present in any string for the list, they will be replaced respectively by the certificate output path, the key output path and the CN to use.
|
||||
|
||||
4: The enabled_plugins list contain plugin names to load. Plugins are available if they are installed Python packages that can be imported using the `bebop_<plugin-name>` package name.
|
||||
|
||||
Your current configuration is:
|
||||
|
||||
|
|
32
bebop/plugins.py
Normal file
32
bebop/plugins.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""Plugin management.
|
||||
|
||||
Plugins are here to allow extending Bebop with additional features, potentially
|
||||
requiring external libraries, without requiring users who just want a Gemini
|
||||
browser to install anything.
|
||||
|
||||
Support for plugins is very simple right now: a plugin can only register an URL
|
||||
scheme to handle.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from bebop.browser.browser import Browser
|
||||
|
||||
|
||||
class SchemePlugin(ABC):
|
||||
"""Plugin for URL scheme management."""
|
||||
|
||||
def __init__(self, scheme: str) -> None:
|
||||
self.scheme = scheme
|
||||
|
||||
@abstractmethod
|
||||
def open_url(self, browser: Browser, url: str) -> Optional[str]:
|
||||
"""Handle an URL for this scheme.
|
||||
|
||||
Returns:
|
||||
The properly handled URL at the end of this query, which may be
|
||||
different from the url parameter if redirections happened, or None if an
|
||||
error happened.
|
||||
"""
|
||||
raise NotImplementedError
|
Reference in a new issue