browser-select/browser_select.py

190 lines
5.0 KiB
Python
Executable File

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