186 lines
5.0 KiB
Python
186 lines
5.0 KiB
Python
|
#!/bin/python
|
||
|
|
||
|
"""
|
||
|
Browser select script.
|
||
|
Use this as the default browser to allow opening differnt browsers based on
|
||
|
URL patterns.
|
||
|
|
||
|
Controllable via TOML file
|
||
|
"""
|
||
|
import os
|
||
|
import sys
|
||
|
from subprocess import Popen
|
||
|
import re
|
||
|
import pathlib
|
||
|
import appdirs as app_dirs
|
||
|
import toml
|
||
|
|
||
|
HARDCODED_DEFAULT_BROWSER = "chromium"
|
||
|
CONFIG_FILE_NAME = "browser_select.toml"
|
||
|
|
||
|
# pylint: disable=too-few-public-methods
|
||
|
class Browser:
|
||
|
"""
|
||
|
Represents an executable browser
|
||
|
"""
|
||
|
def __init__(self, executable, working_dir=None):
|
||
|
self.exec = executable
|
||
|
self.working_dir = working_dir
|
||
|
|
||
|
def start_browser(self, args):
|
||
|
"""
|
||
|
Start a browser with command line arguments
|
||
|
"""
|
||
|
popen_args = [self.exec, *args]
|
||
|
with Popen(popen_args, cwd=self.working_dir) as proc:
|
||
|
ret_code = proc.poll()
|
||
|
return ret_code
|
||
|
|
||
|
|
||
|
class Site:
|
||
|
"""
|
||
|
Website class that allows to match an url and provides the correct browser
|
||
|
for this website
|
||
|
"""
|
||
|
# pylint: disable=too-many-arguments
|
||
|
def __init__(self, urls, www, protocols, browser, regex=None, name=None):
|
||
|
self.name = "Unnamed Site"
|
||
|
if name is not None:
|
||
|
self.name = name
|
||
|
|
||
|
urls = urls if isinstance(urls, list) else [urls]
|
||
|
protocols = protocols if isinstance(protocols, list) else [protocols]
|
||
|
|
||
|
if regex is None:
|
||
|
self.regexes = []
|
||
|
proto_regex = r'|'.join(protocols)
|
||
|
www_regex = r'(?:www\.|)' if www else ''
|
||
|
for url in urls:
|
||
|
regex = r'^(?:'+proto_regex+r')://'+www_regex+url
|
||
|
self.regexes.append(re.compile(regex))
|
||
|
else:
|
||
|
self.regexes = [re.compile(regex)]
|
||
|
|
||
|
self.browser = browser
|
||
|
|
||
|
def match_url(self, url) -> bool:
|
||
|
"""
|
||
|
Checks if a given url matches the registered patterns for this website
|
||
|
"""
|
||
|
for regex in self.regexes:
|
||
|
if bool(regex.match(url)):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def get_browser(self) -> Browser:
|
||
|
"""
|
||
|
Get the assosciated browser for this website
|
||
|
"""
|
||
|
return self.browser
|
||
|
|
||
|
|
||
|
def get_config():
|
||
|
"""
|
||
|
Return the content of the configuration file.
|
||
|
returns None if nothing is loaded
|
||
|
"""
|
||
|
|
||
|
config_file_path = None
|
||
|
|
||
|
# Check user directory
|
||
|
user_dir = app_dirs.user_config_dir('browser_select', 'shimatta')
|
||
|
user_config_file = os.path.join(user_dir, CONFIG_FILE_NAME)
|
||
|
if os.path.exists(user_config_file):
|
||
|
config_file_path = user_config_file
|
||
|
|
||
|
# Check the location besides the script
|
||
|
if config_file_path is None:
|
||
|
cfg = os.path.join(pathlib.Path(
|
||
|
__file__).parent.resolve(), CONFIG_FILE_NAME)
|
||
|
if os.path.exists(cfg):
|
||
|
config_file_path = cfg
|
||
|
|
||
|
if config_file_path is None:
|
||
|
# Config file not found.
|
||
|
return None
|
||
|
|
||
|
parsed_toml = toml.load(config_file_path)
|
||
|
|
||
|
return parsed_toml
|
||
|
|
||
|
|
||
|
# pylint: disable=too-many-locals # Local variables needed. This is ugly, but I don't care
|
||
|
def parse_config(cfg_dict):
|
||
|
"""
|
||
|
Parse the toml configuration
|
||
|
"""
|
||
|
# Get the browser list
|
||
|
browsers = {}
|
||
|
|
||
|
cfg_browsers = cfg_dict['browsers']
|
||
|
for key, value in cfg_browsers.items():
|
||
|
wdir = value.get('working_dir', None)
|
||
|
browsers[key] = Browser(value['exec'], wdir)
|
||
|
|
||
|
# Get Sites
|
||
|
sites = []
|
||
|
cfg_pattens = cfg_dict['urlpatterns']
|
||
|
for key, value in cfg_pattens.items():
|
||
|
name = value.get('site_name', None)
|
||
|
if name is None:
|
||
|
name = key
|
||
|
|
||
|
protocol = value.get('protocol', ['http', 'https'])
|
||
|
url = value.get('site_url', None)
|
||
|
site_regex = value.get('site_regex', None)
|
||
|
www = value.get('www', True)
|
||
|
site_browser = browsers[value['browser']]
|
||
|
|
||
|
site = Site(url, www, protocol, site_browser,
|
||
|
regex=site_regex, name=name)
|
||
|
sites.append(site)
|
||
|
|
||
|
# Get default browser
|
||
|
try:
|
||
|
default_executable = cfg_dict['general']['default_browser']
|
||
|
except KeyError as _ex:
|
||
|
default_executable = HARDCODED_DEFAULT_BROWSER
|
||
|
default_browser = Browser(default_executable)
|
||
|
|
||
|
return sites, default_browser
|
||
|
|
||
|
|
||
|
def execute_browser():
|
||
|
"""
|
||
|
Main function that checks which browser to use and then executes it
|
||
|
"""
|
||
|
browser_to_execute = Browser(HARDCODED_DEFAULT_BROWSER)
|
||
|
# Try read configuration TOML
|
||
|
try:
|
||
|
cfg = get_config()
|
||
|
sites, default_browser = parse_config(cfg)
|
||
|
|
||
|
browser_to_execute = default_browser
|
||
|
|
||
|
# pylint: disable=broad-except # Catch all errors in config parsing. It has to be correct
|
||
|
except Exception as ex:
|
||
|
print(ex)
|
||
|
|
||
|
if len(sys.argv) > 1:
|
||
|
cmd_args = sys.argv[1:]
|
||
|
url_param = sys.argv[1]
|
||
|
else:
|
||
|
cmd_args = []
|
||
|
url_param = None
|
||
|
|
||
|
if url_param is not None:
|
||
|
for site in sites:
|
||
|
if site.match_url(url_param):
|
||
|
browser_to_execute = site.get_browser()
|
||
|
break
|
||
|
|
||
|
return browser_to_execute.start_browser(cmd_args)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
sys.exit(execute_browser())
|