#!/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())