#!/bin/python """ Browser select script. Use this as the default browser to allow opening differnt browsers based on URL patterns. Controllable via TOML file License: MIT Author: Mario Hüttel """ 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())