-rw-r--r-- qtools/rofi_searx/searx.py
1 """ 2 Qtile plugin to use rofi to execute a process using the URL for a randomised searx 3 instance. 4 5 Example usage: 6 7 import qtools.rofi_searx 8 searx = qtools.rofi_searx.Searx() 9 keys.extend([EzKey(k, v) for k, v in { 10 'M-s': searx.lazy_search, 11 'M-C-s': searx.lazy_remove_last_used, 12 }.items()]) 13 14 """ 15 16 17 import random 18 import shlex 19 import subprocess 20 21 from qtools import Notifier 22 23 24 class Searx(Notifier): 25 """ 26 This plugin opens a rofi prompt to get a search query from the user, then randomly 27 selects a searx instance from a provided list and opens a browser to carry out the 28 web search. 29 30 Searx instances can be provided directly as a list, or alternatively an 31 instances_file path can be provided to load the list of searx instances from a file 32 containing line-separated full URLs to searx instances. 33 34 Searx.remove_last_used can be used to remove the previously used searx instance from 35 the instance list. If an instance_file was provided, the last instance will simply 36 be commented out in the file and ignored, but not removed. New instances can be 37 added by adding to the file, and either restarting Qtile or binding 38 Searx.lazy_load_instances to a key. 39 """ 40 defaults = [ 41 ('summary', 'Searx', 'Notification summary.'), 42 ('instances', ['https://searx.me'], 'List of searx instance base URLs.'), 43 ('instances_file', None, 'File containing list of searx instances. If specified' 44 ' instances passed directly are ignored.'), 45 ('prompt', 'Search the web', 'Prompt displayed by rofi.'), 46 ('theme', None, 'rofi theme to use.'), 47 ('launcher', 'tor-browser --allow-remote {url}', 'Command used to open web ' 48 'browser. Requires {url} to ' 49 'place the search url.'), 50 ('notify_on_remove', True, 'Whether to make a notification when removing a ' 51 'searx instance.'), 52 ] 53 54 def __init__(self, **config): 55 Notifier.__init__(self, **config) 56 self.add_defaults(Searx.defaults) 57 self.last_used = None 58 59 self.command = ['rofi', '-dmenu', '-l', '0'] 60 if self.prompt: 61 self.command.extend(['-p', self.prompt]) 62 if self.theme: 63 self.command.extend(['-theme', self.theme]) 64 65 if self.instances_file: 66 self.load_instances() 67 68 def search(self, qtile=None): 69 output = subprocess.run( 70 self.command, stdout=subprocess.PIPE, universal_newlines=True, check=False 71 ) 72 if output.stdout and not output.returncode: 73 if self.instances_file: 74 instance = random.choice( 75 [i for i in self.instances if not i.startswith('#')] 76 ) 77 else: 78 instance = random.choice(self.instances) 79 80 query = output.stdout.strip() 81 url = f"'{instance}/?q={query}&categories=general&language=en-US'" 82 command = self.launcher.format(url=url) 83 subprocess.Popen(shlex.split(command)) 84 self.last_used = instance 85 86 def remove_last_used(self, qtile=None): 87 if self.last_used: 88 self.instances.remove(self.last_used) 89 if self.instances_file: 90 self.instances.append(f'#{self.last_used}') 91 self.save_instances() 92 if self.notify_on_remove: 93 self.show(f'Removed: {self.last_used}') 94 self.last_used = None 95 96 def load_instances(self, qtile=None): 97 with open(self.instances_file, 'r') as f: 98 self.instances = f.read().split() 99 100 def save_instances(self): 101 with open(self.instances_file, 'w') as f: 102 f.write('\n'.join(self.instances)) 103