qutebrowser-1.1.1/0000755000175000017510000000000013230704465015207 5ustar florianflorian00000000000000qutebrowser-1.1.1/scripts/0000755000175000017510000000000013230704465016676 5ustar florianflorian00000000000000qutebrowser-1.1.1/scripts/dictcli.py0000755000175000017510000002165213230703634020671 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2017 Michal Siedlaczek # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """A script installing Hunspell dictionaries. Use: python -m scripts.dictcli [-h] {list,update,remove-old,install} ... """ import argparse import base64 import json import os import sys import re import urllib.request import attr sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) from qutebrowser.browser.webengine import spell from qutebrowser.config import configdata API_URL = 'https://chromium.googlesource.com/chromium/deps/hunspell_dictionaries.git/+/master/' class InvalidLanguageError(Exception): """Raised when requesting invalid languages.""" def __init__(self, invalid_langs): msg = 'invalid languages: {}'.format(', '.join(invalid_langs)) super().__init__(msg) @attr.s class Language: """Dictionary language specs.""" code = attr.ib() name = attr.ib() remote_filename = attr.ib() local_filename = attr.ib(default=None) _file_extension = attr.ib('bdic', init=False) def __attrs_post_init__(self): if self.local_filename is None: self.local_filename = spell.local_filename(self.code) @property def remote_path(self): """Resolve the filename with extension the remote dictionary.""" return '.'.join([self.remote_filename, self._file_extension]) @property def local_path(self): """Resolve the filename with extension the local dictionary.""" if self.local_filename is None: return None return '.'.join([self.local_filename, self._file_extension]) @property def remote_version(self): """Resolve the version of the local dictionary.""" return spell.version(self.remote_path) @property def local_version(self): """Resolve the version of the local dictionary.""" local_path = self.local_path if local_path is None: return None return spell.version(local_path) def get_argparser(): """Get the argparse parser.""" desc = 'Install and manage Hunspell dictionaries for QtWebEngine.' parser = argparse.ArgumentParser(prog='dictcli', description=desc) subparsers = parser.add_subparsers(help='Command', dest='cmd') subparsers.required = True subparsers.add_parser('list', help='Display the list of available languages.') subparsers.add_parser('update', help='Update dictionaries') subparsers.add_parser('remove-old', help='Remove old versions of dictionaries.') install_parser = subparsers.add_parser('install', help='Install dictionaries') install_parser.add_argument('language', nargs='*', help="A list of languages to install.") return parser def version_str(version): return '.'.join(str(n) for n in version) def print_list(languages): """Print the list of available languages.""" pat = '{:<7}{:<26}{:<8}{:<5}' print(pat.format('Code', 'Name', 'Version', 'Installed')) for lang in languages: remote_version = version_str(lang.remote_version) local_version = '-' if lang.local_version is not None: local_version = version_str(lang.local_version) if lang.local_version < lang.remote_version: local_version += ' - update available!' print(pat.format(lang.code, lang.name, remote_version, local_version)) def valid_languages(): """Return a mapping from valid language codes to their names.""" option = configdata.DATA['spellcheck.languages'] return option.typ.valtype.valid_values.descriptions def parse_entry(entry): """Parse an entry from the remote API.""" dict_re = re.compile(r""" (?P(?P[a-z]{2}(-[A-Z]{2})?).*)\.bdic """, re.VERBOSE) match = dict_re.fullmatch(entry['name']) if match is not None: return match.group('code'), match.group('filename') else: return None def language_list_from_api(): """Return a JSON with a list of available languages from Google API.""" listurl = API_URL + '?format=JSON' response = urllib.request.urlopen(listurl) # A special 5-byte prefix must be stripped from the response content # See: https://github.com/google/gitiles/issues/22 # https://github.com/google/gitiles/issues/82 json_content = response.read()[5:] entries = json.loads(json_content.decode('utf-8'))['entries'] parsed_entries = [parse_entry(entry) for entry in entries] return [entry for entry in parsed_entries if entry is not None] def latest_yet(code2file, code, filename): """Determine whether the latest version so far.""" if code not in code2file: return True return spell.version(code2file[code]) < spell.version(filename) def available_languages(): """Return a list of Language objects of all available languages.""" lang_map = valid_languages() api_list = language_list_from_api() code2file = {} for code, filename in api_list: if latest_yet(code2file, code, filename): code2file[code] = filename return [ Language(code, name, code2file[code]) for code, name in lang_map.items() if code in code2file ] def download_dictionary(url, dest): """Download a decoded dictionary file.""" response = urllib.request.urlopen(url) decoded = base64.decodebytes(response.read()) with open(dest, 'bw') as dict_file: dict_file.write(decoded) def filter_languages(languages, selected): """Filter a list of languages based on an inclusion list. Args: languages: a list of languages to filter selected: a list of keys to select """ filtered_languages = [] for language in languages: if language.code in selected: filtered_languages.append(language) selected.remove(language.code) if selected: raise InvalidLanguageError(selected) return filtered_languages def install_lang(lang): """Install a single lang given by the argument.""" lang_url = API_URL + lang.remote_path + '?format=TEXT' if not os.path.isdir(spell.dictionary_dir()): msg = '{} does not exist, creating the directory' print(msg.format(spell.dictionary_dir())) os.makedirs(spell.dictionary_dir()) print('Downloading {}'.format(lang_url)) dest = os.path.join(spell.dictionary_dir(), lang.remote_path) download_dictionary(lang_url, dest) print('Done.') def install(languages): """Install languages.""" for lang in languages: try: print('Installing {}: {}'.format(lang.code, lang.name)) install_lang(lang) except PermissionError as e: sys.exit(str(e)) def update(languages): """Update the given languages.""" installed = [lang for lang in languages if lang.local_version is not None] for lang in installed: if lang.local_version < lang.remote_version: print('Upgrading {} from {} to {}'.format( lang.code, version_str(lang.local_version), version_str(lang.remote_version))) install_lang(lang) def remove_old(languages): """Remove old versions of languages.""" installed = [lang for lang in languages if lang.local_version is not None] for lang in installed: local_files = spell.local_files(lang.code) for old_file in local_files[1:]: os.remove(os.path.join(spell.dictionary_dir(), old_file)) def main(): if configdata.DATA is None: configdata.init() parser = get_argparser() argv = sys.argv[1:] args = parser.parse_args(argv) languages = available_languages() if args.cmd == 'list': print_list(languages) elif args.cmd == 'update': update(languages) elif args.cmd == 'remove-old': remove_old(languages) elif not args.language: sys.exit('You must provide a list of languages to install.') else: try: install(filter_languages(languages, args.language)) except InvalidLanguageError as e: print(e) if __name__ == '__main__': main() qutebrowser-1.1.1/scripts/testbrowser/0000755000175000017510000000000013230704465021261 5ustar florianflorian00000000000000qutebrowser-1.1.1/scripts/testbrowser/testbrowser_webengine.py0000755000175000017510000000310613230703634026241 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Very simple browser for testing purposes.""" import sys import argparse from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QApplication from PyQt5.QtWebEngineWidgets import QWebEngineView def parse_args(): """Parse commandline arguments.""" parser = argparse.ArgumentParser() parser.add_argument('url', help='The URL to open') return parser.parse_known_args()[0] if __name__ == '__main__': args = parse_args() app = QApplication(sys.argv) wv = QWebEngineView() wv.loadStarted.connect(lambda: print("Loading started")) wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) wv.loadFinished.connect(lambda: print("Loading finished")) wv.load(QUrl.fromUserInput(args.url)) wv.show() app.exec_() qutebrowser-1.1.1/scripts/testbrowser/testbrowser_webkit.py0000755000175000017510000000347113230703634025570 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Very simple browser for testing purposes.""" import sys import argparse from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKitWidgets import QWebView def parse_args(): """Parse commandline arguments.""" parser = argparse.ArgumentParser() parser.add_argument('url', help='The URL to open') parser.add_argument('--plugins', '-p', help='Enable plugins', default=False, action='store_true') return parser.parse_known_args()[0] if __name__ == '__main__': args = parse_args() app = QApplication(sys.argv) wv = QWebView() wv.loadStarted.connect(lambda: print("Loading started")) wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) wv.loadFinished.connect(lambda: print("Loading finished")) if args.plugins: wv.settings().setAttribute(QWebSettings.PluginsEnabled, True) wv.load(QUrl.fromUserInput(args.url)) wv.show() app.exec_() qutebrowser-1.1.1/scripts/open_url_in_instance.sh0000755000175000017510000000103113230703634023422 0ustar florianflorian00000000000000#!/bin/bash # initial idea: Florian Bruhin (The-Compiler) # author: Thore Bödecker (foxxx0) _url="$1" _qb_version='1.0.4' _proto_version=1 _ipc_socket="${XDG_RUNTIME_DIR}/qutebrowser/ipc-$(echo -n "$USER" | md5sum | cut -d' ' -f1)" _qute_bin="/usr/bin/qutebrowser" printf '{"args": ["%s"], "target_arg": null, "version": "%s", "protocol_version": %d, "cwd": "%s"}\n' \ "${_url}" \ "${_qb_version}" \ "${_proto_version}" \ "${PWD}" | socat - UNIX-CONNECT:"${_ipc_socket}" 2>/dev/null || "$_qute_bin" "$@" & qutebrowser-1.1.1/scripts/link_pyqt.py0000644000175000017510000001602013230703634021256 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Symlink PyQt into a given virtualenv.""" import os import os.path import argparse import shutil import sys import subprocess import tempfile import filecmp class Error(Exception): """Exception raised when linking fails.""" pass def run_py(executable, *code): """Run the given python code with the given executable.""" if os.name == 'nt' and len(code) > 1: # Windows can't do newlines in arguments... oshandle, filename = tempfile.mkstemp() with os.fdopen(oshandle, 'w') as f: f.write('\n'.join(code)) cmd = [executable, filename] try: ret = subprocess.run(cmd, universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout finally: os.remove(filename) else: cmd = [executable, '-c', '\n'.join(code)] ret = subprocess.run(cmd, universal_newlines=True, check=True, stdout=subprocess.PIPE).stdout return ret.rstrip() def verbose_copy(src, dst, *, follow_symlinks=True): """Copy function for shutil.copytree which prints copied files.""" if '-v' in sys.argv: print('{} -> {}'.format(src, dst)) shutil.copy(src, dst, follow_symlinks=follow_symlinks) def get_ignored_files(directory, files): """Get the files which should be ignored for link_pyqt() on Windows.""" needed_exts = ('.py', '.dll', '.pyd', '.so') ignored_dirs = ('examples', 'qml', 'uic', 'doc') filtered = [] for f in files: ext = os.path.splitext(f)[1] full_path = os.path.join(directory, f) if os.path.isdir(full_path) and f in ignored_dirs: filtered.append(f) elif (ext not in needed_exts) and os.path.isfile(full_path): filtered.append(f) return filtered def needs_update(source, dest): """Check if a file to be linked/copied needs to be updated.""" if os.path.islink(dest): # No need to delete a link and relink -> skip this return False elif os.path.isdir(dest): diffs = filecmp.dircmp(source, dest) ignored = get_ignored_files(source, diffs.left_only) has_new_files = set(ignored) != set(diffs.left_only) return (has_new_files or diffs.right_only or diffs.common_funny or diffs.diff_files or diffs.funny_files) else: return not filecmp.cmp(source, dest) def get_lib_path(executable, name, required=True): """Get the path of a python library. Args: executable: The Python executable to use. name: The name of the library to get the path for. required: Whether Error should be raised if the lib was not found. """ code = [ 'try:', ' import {}'.format(name), 'except ImportError as e:', ' print("ImportError: " + str(e))', 'else:', ' print("path: " + {}.__file__)'.format(name) ] output = run_py(executable, *code) try: prefix, data = output.split(': ') except ValueError: raise ValueError("Unexpected output: {!r}".format(output)) if prefix == 'path': return data elif prefix == 'ImportError': if required: raise Error("Could not import {} with {}: {}!".format( name, executable, data)) else: return None else: raise ValueError("Unexpected output: {!r}".format(output)) def link_pyqt(executable, venv_path): """Symlink the systemwide PyQt/sip into the venv. Args: executable: The python executable where the source files are present. venv_path: The path to the virtualenv site-packages. """ sip_file = get_lib_path(executable, 'sip') sipconfig_file = get_lib_path(executable, 'sipconfig', required=False) pyqt_dir = os.path.dirname(get_lib_path(executable, 'PyQt5.QtCore')) for path in [sip_file, sipconfig_file, pyqt_dir]: if path is None: continue fn = os.path.basename(path) dest = os.path.join(venv_path, fn) if os.path.exists(dest): if needs_update(path, dest): remove(dest) else: continue copy_or_link(path, dest) def copy_or_link(source, dest): """Copy or symlink source to dest.""" if os.name == 'nt': if os.path.isdir(source): print('{} -> {}'.format(source, dest)) shutil.copytree(source, dest, ignore=get_ignored_files, copy_function=verbose_copy) else: print('{} -> {}'.format(source, dest)) shutil.copy(source, dest) else: print('{} -> {}'.format(source, dest)) os.symlink(source, dest) def remove(filename): """Remove a given filename, regardless of whether it's a file or dir.""" if os.path.isdir(filename): shutil.rmtree(filename) else: os.unlink(filename) def get_venv_lib_path(path): """Get the library path of a virtualenv.""" subdir = 'Scripts' if os.name == 'nt' else 'bin' executable = os.path.join(path, subdir, 'python') return run_py(executable, 'from distutils.sysconfig import get_python_lib', 'print(get_python_lib())') def get_tox_syspython(tox_path): """Get the system python based on a virtualenv created by tox.""" path = os.path.join(tox_path, '.tox-config1') with open(path, encoding='ascii') as f: line = f.readline() _md5, sys_python = line.rstrip().split(' ', 1) return sys_python def main(): parser = argparse.ArgumentParser() parser.add_argument('path', help="Base path to the venv.") parser.add_argument('--tox', help="Add when called via tox.", action='store_true') args = parser.parse_args() if args.tox: # Workaround for the lack of negative factors in tox.ini if 'LINK_PYQT_SKIP' in os.environ: print('LINK_PYQT_SKIP set, exiting...') sys.exit(0) executable = get_tox_syspython(args.path) else: executable = sys.executable venv_path = get_venv_lib_path(args.path) link_pyqt(executable, venv_path) if __name__ == '__main__': try: main() except Error as e: print(str(e), file=sys.stderr) sys.exit(1) qutebrowser-1.1.1/scripts/setupcommon.py0000644000175000017510000000461413230703634021623 0ustar florianflorian00000000000000# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Data used by setup.py and the PyInstaller qutebrowser.spec.""" import sys import os import os.path import subprocess sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir)) if sys.hexversion >= 0x03000000: open_file = open else: import codecs open_file = codecs.open BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.path.pardir) def _git_str(): """Try to find out git version. Return: string containing the git commit ID and timestamp. None if there was an error or we're not in a git repo. """ if BASEDIR is None: return None if not os.path.isdir(os.path.join(BASEDIR, ".git")): return None try: # https://stackoverflow.com/questions/21017300/21017394#21017394 commit_hash = subprocess.run( ['git', 'describe', '--match=NeVeRmAtCh', '--always', '--dirty'], cwd=BASEDIR, check=True, stdout=subprocess.PIPE).stdout.decode('UTF-8').strip() date = subprocess.run( ['git', 'show', '-s', '--format=%ci', 'HEAD'], cwd=BASEDIR, check=True, stdout=subprocess.PIPE).stdout.decode('UTF-8').strip() return '{} ({})'.format(commit_hash, date) except (subprocess.CalledProcessError, OSError): return None def write_git_file(): """Write the git-commit-id file with the current commit.""" gitstr = _git_str() if gitstr is None: gitstr = '' path = os.path.join(BASEDIR, 'qutebrowser', 'git-commit-id') with open_file(path, 'w', encoding='ascii') as f: f.write(gitstr) qutebrowser-1.1.1/scripts/hist_importer.py0000755000175000017510000001353413230704277022152 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2017 Florian Bruhin (The Compiler) # Copyright 2017 Josefson Souza # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Tool to import browser history from other browsers.""" import argparse import sqlite3 import sys import os class Error(Exception): """Exception for errors in this module.""" pass def parse(): """Parse command line arguments.""" description = ("This program is meant to extract browser history from your" " previous browser and import them into qutebrowser.") epilog = ("Databases:\n\n\tqutebrowser: Is named 'history.sqlite' and can " "be found at your --basedir. In order to find where your " "basedir is you can run ':open qute:version' inside qutebrowser." "\n\n\tFirefox: Is named 'places.sqlite', and can be found at " "your system's profile folder. Check this link for where it is " "located: http://kb.mozillazine.org/Profile_folder" "\n\n\tChrome: Is named 'History', and can be found at the " "respective User Data Directory. Check this link for where it is" "located: https://chromium.googlesource.com/chromium/src/+/" "master/docs/user_data_dir.md\n\n" "Example: hist_importer.py -b firefox -s /Firefox/Profile/" "places.sqlite -d /qutebrowser/data/history.sqlite") parser = argparse.ArgumentParser( description=description, epilog=epilog, formatter_class=argparse.RawTextHelpFormatter ) parser.add_argument('-b', '--browser', dest='browser', required=True, type=str, help='Browsers: {firefox, chrome}') parser.add_argument('-s', '--source', dest='source', required=True, type=str, help='Source: Full path to the sqlite data' 'base file from the source browser.') parser.add_argument('-d', '--dest', dest='dest', required=True, type=str, help='\nDestination: Full path to the qutebrowser ' 'sqlite database') return parser.parse_args() def open_db(data_base): """Open connection with database.""" if os.path.isfile(data_base): return sqlite3.connect(data_base) raise Error('The file {} does not exist.'.format(data_base)) def extract(source, query): """Get records from source database. Args: source: File path to the source database where we want to extract the data from. query: The query string to be executed in order to retrieve relevant attributes as (datetime, url, time) from the source database according to the browser chosen. """ try: conn = open_db(source) cursor = conn.cursor() cursor.execute(query) history = cursor.fetchall() conn.close() return history except sqlite3.OperationalError as op_e: raise Error('Could not perform queries on the source database: ' '{}'.format(op_e)) def clean(history): """Clean up records from source database. Receives a list of record and sanityze them in order for them to be properly imported to qutebrowser. Sanitation requires adding a 4th attribute 'redirect' which is filled with '0's, and also purging all records that have a NULL/None datetime attribute. Args: history: List of records (datetime, url, title) from source database. """ # replace missing titles with an empty string for index, record in enumerate(history): if record[1] is None: cleaned = list(record) cleaned[1] = '' history[index] = tuple(cleaned) nulls = [record for record in history if None in record] for null_record in nulls: history.remove(null_record) history = [list(record) for record in history] for record in history: record.append('0') return history def insert_qb(history, dest): """Insert history into dest database. Args: history: List of records. dest: File path to the destination database, where history will be inserted. """ conn = open_db(dest) cursor = conn.cursor() cursor.executemany( 'INSERT INTO History (url,title,atime,redirect) VALUES (?,?,?,?)', history ) cursor.execute('DROP TABLE CompletionHistory') conn.commit() conn.close() def run(): """Main control flux of the script.""" args = parse() browser = args.browser.lower() source, dest = args.source, args.dest query = { 'firefox': 'select url,title,last_visit_date/1000000 as date ' 'from moz_places', 'chrome': 'select url,title,last_visit_time/10000000 as date ' 'from urls', } if browser not in query: raise Error('Sorry, the selected browser: "{}" is not ' 'supported.'.format(browser)) else: history = extract(source, query[browser]) history = clean(history) insert_qb(history, dest) def main(): try: run() except Error as e: sys.exit(str(e)) if __name__ == "__main__": main() qutebrowser-1.1.1/scripts/keytester.py0000644000175000017510000000327613202372777021305 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Small test script to show key presses. Use python3 -m scripts.keytester to launch it. """ from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout from qutebrowser.utils import utils class KeyWidget(QWidget): """Widget displaying key presses.""" def __init__(self, parent=None): super().__init__(parent) self._layout = QHBoxLayout(self) self._label = QLabel(text="Waiting for keypress...") self._layout.addWidget(self._label) def keyPressEvent(self, e): """Show pressed keys.""" lines = [ str(utils.keyevent_to_string(e)), '', 'key: 0x{:x}'.format(int(e.key())), 'modifiers: 0x{:x}'.format(int(e.modifiers())), 'text: {!r}'.format(e.text()), ] self._label.setText('\n'.join(lines)) app = QApplication([]) w = KeyWidget() w.show() app.exec_() qutebrowser-1.1.1/scripts/utils.py0000644000175000017510000000467213202372777020427 0ustar florianflorian00000000000000# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Utility functions for scripts.""" import os import os.path # Import side-effects are an evil thing, but here it's okay so scripts using # colors work on Windows as well. try: import colorama except ImportError: colorama = None else: colorama.init() use_color = os.name != 'nt' or colorama fg_colors = { 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'reset': 39, } bg_colors = {name: col + 10 for name, col in fg_colors.items()} term_attributes = { 'bright': 1, 'dim': 2, 'normal': 22, 'reset': 0, } def _esc(code): """Get an ANSI color code based on a color number.""" return '\033[{}m'.format(code) def print_col(text, color): """Print a colorized text.""" if use_color: fg = _esc(fg_colors[color.lower()]) reset = _esc(fg_colors['reset']) print(''.join([fg, text, reset])) else: print(text) def print_title(text): """Print a title.""" print_col("==================== {} ====================".format(text), 'yellow') def print_subtitle(text): """Print a subtitle.""" print_col("------ {} ------".format(text), 'cyan') def print_bold(text): """Print a bold text.""" if use_color: bold = _esc(term_attributes['bright']) reset = _esc(term_attributes['reset']) print(''.join([bold, text, reset])) else: print(text) def change_cwd(): """Change the scripts cwd if it was started inside the script folder.""" cwd = os.getcwd() if os.path.split(cwd)[1] == 'scripts': os.chdir(os.path.join(cwd, os.pardir)) qutebrowser-1.1.1/scripts/__init__.py0000644000175000017510000000012313202372777021011 0ustar florianflorian00000000000000# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: """Various utility scripts.""" qutebrowser-1.1.1/scripts/importer.py0000755000175000017510000002712613230703634021121 0ustar florianflorian00000000000000#!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2014-2017 Claude (longneck) # Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . """Tool to import data from other browsers. Currently importing bookmarks from Netscape Bookmark files and Mozilla profiles is supported. """ import argparse import sqlite3 import os import urllib.parse import json import string browser_default_input_format = { 'chromium': 'chrome', 'chrome': 'chrome', 'ie': 'netscape', 'firefox': 'mozilla', 'seamonkey': 'mozilla', 'palemoon': 'mozilla', } def main(): args = get_args() bookmark_types = [] output_format = None input_format = args.input_format if args.search_output: bookmark_types = ['search'] if args.oldconfig: output_format = 'oldsearch' else: output_format = 'search' else: if args.bookmark_output: output_format = 'bookmark' elif args.quickmark_output: output_format = 'quickmark' if args.import_bookmarks: bookmark_types.append('bookmark') if args.import_keywords: bookmark_types.append('keyword') if not bookmark_types: bookmark_types = ['bookmark', 'keyword'] if not output_format: output_format = 'quickmark' if not input_format: if args.browser: input_format = browser_default_input_format[args.browser] else: #default to netscape input_format = 'netscape' import_function = { 'netscape': import_netscape_bookmarks, 'mozilla': import_moz_places, 'chrome': import_chrome, } import_function[input_format](args.bookmarks, bookmark_types, output_format) def get_args(): """Get the argparse parser.""" parser = argparse.ArgumentParser( epilog="To import bookmarks from Chromium, Firefox or IE, " "export them to HTML in your browsers bookmark manager. ") parser.add_argument( 'browser', help="Which browser? {%(choices)s}", choices=browser_default_input_format.keys(), nargs='?', metavar='browser') parser.add_argument( '-i', '--input-format', help='Which input format? (overrides browser default; "netscape" if ' 'neither given)', choices=set(browser_default_input_format.values()), required=False) parser.add_argument( '-b', '--bookmark-output', help="Output in bookmark format.", action='store_true', default=False, required=False) parser.add_argument( '-q', '--quickmark-output', help="Output in quickmark format (default).", action='store_true', default=False, required=False) parser.add_argument( '-s', '--search-output', help="Output config.py search engine format (negates -B and -K)", action='store_true', default=False, required=False) parser.add_argument( '--oldconfig', help="Output search engine format for old qutebrowser.conf format", default=False, action='store_true', required=False) parser.add_argument( '-B', '--import-bookmarks', help="Import plain bookmarks (can be combiend with -K)", action='store_true', default=False, required=False) parser.add_argument( '-K', '--import-keywords', help="Import keywords (can be combined with -B)", action='store_true', default=False, required=False) parser.add_argument( 'bookmarks', help="Bookmarks file (html format) or " "profile folder (Mozilla format)") args = parser.parse_args() return args def search_escape(url): """Escape URLs such that preexisting { and } are handled properly. Will obviously trash a properly-formatted qutebrowser URL. """ return url.replace('{', '{{').replace('}', '}}') def opensearch_convert(url): """Convert a basic OpenSearch URL into something qutebrowser can use. Exceptions: KeyError: An unknown and required parameter is present in the URL. This usually means there's browser/addon specific functionality needed to build the URL (I'm looking at you and your browser, Google) that obviously won't be present here. """ subst = { 'searchTerms': '%s', # for proper escaping later 'language': '*', 'inputEncoding': 'UTF-8', 'outputEncoding': 'UTF-8' } # remove optional parameters (even those we don't support) for param in string.Formatter().parse(url): if param[1]: if param[1].endswith('?'): url = url.replace('{' + param[1] + '}', '') elif param[2] and param[2].endswith('?'): url = url.replace('{' + param[1] + ':' + param[2] + '}', '') return search_escape(url.format(**subst)).replace('%s', '{}') def import_netscape_bookmarks(bookmarks_file, bookmark_types, output_format): """Import bookmarks from a NETSCAPE-Bookmark-file v1. Generated by Chromium, Firefox, IE and possibly more browsers. Not all export all possible bookmark types: - Firefox mostly works with everything - Chrome doesn't support keywords at all; searches are a separate database """ import bs4 with open(bookmarks_file, encoding='utf-8') as f: soup = bs4.BeautifulSoup(f, 'html.parser') bookmark_query = { 'search': lambda tag: ( (tag.name == 'a') and ('shortcuturl' in tag.attrs) and ('%s' in tag['href'])), 'keyword': lambda tag: ( (tag.name == 'a') and ('shortcuturl' in tag.attrs) and ('%s' not in tag['href'])), 'bookmark': lambda tag: ( (tag.name == 'a') and ('shortcuturl' not in tag.attrs) and (tag.string)), } output_template = { 'search': { 'search': "c.url.searchengines['{tag[shortcuturl]}'] = " "'{tag[href]}' #{tag.string}" }, 'oldsearch': { 'search': '{tag[shortcuturl]} = {tag[href]} #{tag.string}', }, 'bookmark': { 'bookmark': '{tag[href]} {tag.string}', 'keyword': '{tag[href]} {tag.string}' }, 'quickmark': { 'bookmark': '{tag.string} {tag[href]}', 'keyword': '{tag[shortcuturl]} {tag[href]}' } } bookmarks = [] for typ in bookmark_types: tags = soup.findAll(bookmark_query[typ]) for tag in tags: if typ == 'search': tag['href'] = search_escape(tag['href']).replace('%s', '{}') if tag['href'] not in bookmarks: bookmarks.append( output_template[output_format][typ].format(tag=tag)) for bookmark in bookmarks: print(bookmark) def import_moz_places(profile, bookmark_types, output_format): """Import bookmarks from a Mozilla profile's places.sqlite database.""" place_query = { 'bookmark': ( "SELECT DISTINCT moz_bookmarks.title,moz_places.url " "FROM moz_bookmarks,moz_places " "WHERE moz_places.id=moz_bookmarks.fk " "AND moz_places.id NOT IN (SELECT place_id FROM moz_keywords) " "AND moz_places.url NOT LIKE 'place:%';" ), # Bookmarks with no keywords assigned 'keyword': ( "SELECT moz_keywords.keyword,moz_places.url " "FROM moz_keywords,moz_places,moz_bookmarks " "WHERE moz_places.id=moz_bookmarks.fk " "AND moz_places.id=moz_keywords.place_id " "AND moz_places.url NOT LIKE '%!%s%' ESCAPE '!';" ), # Bookmarks with keywords assigned but no %s substitution 'search': ( "SELECT moz_keywords.keyword, " " moz_bookmarks.title, " " search_conv(moz_places.url) AS url " "FROM moz_keywords,moz_places,moz_bookmarks " "WHERE moz_places.id=moz_bookmarks.fk " "AND moz_places.id=moz_keywords.place_id " "AND moz_places.url LIKE '%!%s%' ESCAPE '!';" ) # bookmarks with keyword and %s substitution } out_template = { 'bookmark': { 'bookmark': '{url} {title}', 'keyword': '{url} {keyword}' }, 'quickmark': { 'bookmark': '{title} {url}', 'keyword': '{keyword} {url}' }, 'oldsearch': { 'search': '{keyword} {url} #{title}' }, 'search': { 'search': "c.url.searchengines['{keyword}'] = '{url}' #{title}" } } def search_conv(url): return search_escape(url).replace('%s', '{}') places = sqlite3.connect(os.path.join(profile, "places.sqlite")) places.create_function('search_conv', 1, search_conv) places.row_factory = sqlite3.Row c = places.cursor() for typ in bookmark_types: c.execute(place_query[typ]) for row in c: print(out_template[output_format][typ].format(**row)) def import_chrome(profile, bookmark_types, output_format): """Import bookmarks and search keywords from Chrome-type profiles. On Chrome, keywords and search engines are the same thing and handled in their own database table; bookmarks cannot have associated keywords. This is why the dictionary lookups here are much simpler. """ out_template = { 'bookmark': '{url} {name}', 'quickmark': '{name} {url}', 'search': "c.url.searchengines['{keyword}'] = '{url}'", 'oldsearch': '{keyword} {url}' } if 'search' in bookmark_types: webdata = sqlite3.connect(os.path.join(profile, 'Web Data')) c = webdata.cursor() c.execute('SELECT keyword,url FROM keywords;') for keyword, url in c: try: url = opensearch_convert(url) print(out_template[output_format].format( keyword=keyword, url=url)) except KeyError: print('# Unsupported parameter in url for {}; skipping....'. format(keyword)) else: with open(os.path.join(profile, 'Bookmarks'), encoding='utf-8') as f: bookmarks = json.load(f) def bm_tree_walk(bm, template): """Recursive function to walk through bookmarks.""" assert 'type' in bm, bm if bm['type'] == 'url': if urllib.parse.urlparse(bm['url']).scheme != 'chrome': print(template.format(**bm)) elif bm['type'] == 'folder': for child in bm['children']: bm_tree_walk(child, template) for root in bookmarks['roots'].values(): bm_tree_walk(root, out_template[output_format]) if __name__ == '__main__': main() qutebrowser-1.1.1/MANIFEST.in0000644000175000017510000000234513230703634016746 0ustar florianflorian00000000000000recursive-include qutebrowser *.py recursive-include qutebrowser/img *.svg *.png recursive-include qutebrowser/test *.py recursive-include qutebrowser/javascript *.js graft qutebrowser/html graft qutebrowser/3rdparty graft icons graft doc/img graft misc/apparmor graft misc/userscripts recursive-include scripts *.py *.sh include qutebrowser/utils/testfile include qutebrowser/git-commit-id include LICENSE doc/* README.asciidoc include misc/qutebrowser.desktop include misc/qutebrowser.appdata.xml include misc/Makefile include requirements.txt include tox.ini include qutebrowser.py include misc/cheatsheet.svg include qutebrowser/config/configdata.yml prune www prune scripts/dev prune scripts/testbrowser/cpp prune .github exclude scripts/asciidoc2html.py exclude doc/notes recursive-exclude doc *.asciidoc include doc/qutebrowser.1.asciidoc include doc/changelog.asciidoc prune tests prune qutebrowser/3rdparty prune misc/requirements prune misc/docker exclude pytest.ini exclude qutebrowser.rcc exclude qutebrowser/javascript/.eslintrc.yaml exclude qutebrowser/javascript/.eslintignore exclude doc/help exclude .* exclude misc/appveyor_install.py exclude misc/qutebrowser.spec exclude misc/qutebrowser.nsi global-exclude __pycache__ *.pyc *.pyo qutebrowser-1.1.1/setup.cfg0000644000175000017510000000004613230704465017030 0ustar florianflorian00000000000000[egg_info] tag_build = tag_date = 0 qutebrowser-1.1.1/misc/0000755000175000017510000000000013230704465016142 5ustar florianflorian00000000000000qutebrowser-1.1.1/misc/qutebrowser.appdata.xml0000644000175000017510000000323613230703634022660 0ustar florianflorian00000000000000 org.qutebrowser.qutebrowser CC-BY-SA-3.0 GPL-3.0 qutebrowser A keyboard-driven web browser

qutebrowser is a keyboard-focused browser with a minimal GUI. It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl, and is based on Python and PyQt5.

Network WebBrowser qutebrowser qutebrowser.desktop https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/main.png https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/downloads.png https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/completion.png https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/hints.png https://www.qutebrowser.org https://qutebrowser.org/doc/faq.html https://qutebrowser.org/doc/help/ https://github.com/qutebrowser/qutebrowser/issues/ https://github.com/qutebrowser/qutebrowser#donating
qutebrowser-1.1.1/misc/apparmor/0000755000175000017510000000000013230704465017763 5ustar florianflorian00000000000000qutebrowser-1.1.1/misc/apparmor/usr.bin.qutebrowser0000644000175000017510000000174513202372777023664 0ustar florianflorian00000000000000# AppArmor profile for qutebrowser # Tested on Debian jessie #include profile qutebrowser /usr/{local/,}bin/qutebrowser { #include #include #include #include #include #include #include #include #include capability dac_override, /usr/{local/,}bin/ r, /usr/{local/,}bin/qutebrowser rix, /usr/bin/python3.? r, /usr/lib/python3/ mr, /usr/lib/python3/** mr, /usr/lib/python3.?/ r, /usr/lib/python3.?/** mr, /usr/local/lib/python3.?/** r, /proc/*/mounts r, owner /tmp/** rwkl, owner /run/user/*/ rw, owner /run/user/*/** krw, @{HOME}/.config/qutebrowser/** krw, @{HOME}/.local/share/qutebrowser/** krw, @{HOME}/.cache/qutebrowser/** krw, @{HOME}/.gstreamer-0.10/* r, } qutebrowser-1.1.1/misc/qutebrowser.rcc0000644000175000017510000000073513202372777021230 0ustar florianflorian00000000000000 icons/qutebrowser-16x16.png icons/qutebrowser-24x24.png icons/qutebrowser-32x32.png icons/qutebrowser-48x48.png icons/qutebrowser-64x64.png icons/qutebrowser-96x96.png icons/qutebrowser-128x128.png icons/qutebrowser-256x256.png icons/qutebrowser-512x512.png qutebrowser-1.1.1/misc/Makefile0000644000175000017510000000203013230703634017572 0ustar florianflorian00000000000000PYTHON = python3 DESTDIR = / ICONSIZES = 16 24 32 48 64 128 256 512 .PHONY: install doc/qutebrowser.1.html: a2x -f manpage doc/qutebrowser.1.asciidoc install: doc/qutebrowser.1.html $(PYTHON) setup.py install --root="$(DESTDIR)" --optimize=1 install -Dm644 doc/qutebrowser.1 \ "$(DESTDIR)/usr/share/man/man1/qutebrowser.1" install -Dm644 misc/qutebrowser.desktop \ "$(DESTDIR)/usr/share/applications/qutebrowser.desktop" $(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \ "$(DESTDIR)/usr/share/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";) install -Dm644 icons/qutebrowser.svg \ "$(DESTDIR)/usr/share/icons/hicolor/scalable/apps/qutebrowser.svg" install -Dm755 -t "$(DESTDIR)/usr/share/qutebrowser/userscripts/" \ $(wildcard misc/userscripts/*) install -Dm755 -t "$(DESTDIR)/usr/share/qutebrowser/scripts/" \ $(filter-out scripts/__init__.py scripts/__pycache__ scripts/dev \ scripts/testbrowser scripts/asciidoc2html.py scripts/setupcommon.py \ scripts/link_pyqt.py,$(wildcard scripts/*)) qutebrowser-1.1.1/misc/qutebrowser.desktop0000644000175000017510000000057313230703634022121 0ustar florianflorian00000000000000[Desktop Entry] Name=qutebrowser GenericName=Web Browser Icon=qutebrowser Type=Application Categories=Network;WebBrowser; Exec=qutebrowser %u Terminal=false StartupNotify=false MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute; Keywords=Browser qutebrowser-1.1.1/misc/userscripts/0000755000175000017510000000000013230704465020530 5ustar florianflorian00000000000000qutebrowser-1.1.1/misc/userscripts/qutedmenu0000755000175000017510000000274613230703634022473 0ustar florianflorian00000000000000#!/usr/bin/env bash # Handle open -s && open -t with bemenu #:bind o spawn --userscript /path/to/userscripts/qutedmenu open #:bind O spawn --userscript /path/to/userscripts/qutedmenu tab # If you would like to set a custom colorscheme/font use these dirs. # https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/bemenucolors readonly confdir=${XDG_CONFIG_HOME:-$HOME/.config} readonly optsfile=$confdir/dmenu/bemenucolors create_menu() { # Check quickmarks while read -r url; do printf -- '%s\n' "$url" done < "$QUTE_CONFIG_DIR"/quickmarks # Next bookmarks while read -r url _; do printf -- '%s\n' "$url" done < "$QUTE_CONFIG_DIR"/bookmarks/urls # Finally history while read -r _ url; do printf -- '%s\n' "$url" done < "$QUTE_DATA_DIR"/history } get_selection() { opts+=(-p qutebrowser) #create_menu | dmenu -l 10 "${opts[@]}" create_menu | bemenu -l 10 "${opts[@]}" } # Main # https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font [[ -s $confdir/dmenu/font ]] && read -r font < "$confdir"/dmenu/font [[ $font ]] && opts+=(-fn "$font") # shellcheck source=/dev/null [[ -s $optsfile ]] && source "$optsfile" url=$(get_selection) url=${url/*http/http} # If no selection is made, exit (escape pressed, e.g.) [[ ! $url ]] && exit 0 case $1 in open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;; tab) printf '%s' "open -t $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;; esac qutebrowser-1.1.1/misc/userscripts/cast0000755000175000017510000001155113230703634021410 0ustar florianflorian00000000000000#!/usr/bin/env bash # # Behaviour # Userscript for qutebrowser which casts the url passed in $1 to the default # ChromeCast device in the network using the program `castnow` # # Usage # You can launch the script from qutebrowser as follows: # spawn --userscript ${PATH_TO_FILE} {url} # # Then, you can control the chromecast by launching the simple command # `castnow` in a shell which will connect to the running castnow instance. # # For stopping the script, issue the command `pkill -f castnow` which would # then let the rest of the userscript execute for cleaning temporary file. # # Thanks # This userscript borrows Thorsten Wißmann's javascript code from his `mpv` # userscript. # # Dependencies # - castnow, https://github.com/xat/castnow # # Author # Simon Désaulniers if [ -z "$QUTE_FIFO" ] ; then cat 1>&2 <&2 else echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO" fi } js() { cat < The video is being cast on your ChromeCast device.

In order to restore this particular video click here.

"; replacement.style.position = "relative"; replacement.style.zIndex = "100003000000"; replacement.style.fontSize = "1rem"; replacement.style.textAlign = "center"; replacement.style.verticalAlign = "middle"; replacement.style.height = "100%"; replacement.style.background = "#101010"; replacement.style.color = "white"; replacement.style.border = "4px dashed #545454"; replacement.style.padding = "2em"; replacement.style.margin = "auto"; App.all_replacements[i] = replacement; App.backup_videos[i] = video; video.parentNode.replaceChild(replacement, video); } function restore_video(obj, index) { obj = App.all_replacements[index]; video = App.backup_videos[index]; console.log(video); obj.parentNode.replaceChild(video, obj); } /** force repainting the video, thanks to: * http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/ */ var siteHeader = document.getElementById('header'); siteHeader.style.display='none'; siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough siteHeader.style.display='block'; EOF } printjs() { js | sed 's,//.*$,,' | tr '\n' ' ' } echo "jseval -q $(printjs)" >> "$QUTE_FIFO" tmpdir=$(mktemp -d) file_to_cast=${tmpdir}/qutecast program_=$(command -v castnow) if [[ "${program_}" == "" ]]; then msg error "castnow can't be found..." exit 1 fi # kill any running instance of castnow pkill -f "${program_}" # start youtube download in stream mode (-o -) into temporary file youtube-dl -qo - "$1" > "${file_to_cast}" & ytdl_pid=$! msg info "Casting $1" >> "$QUTE_FIFO" # start castnow in stream mode to cast on ChromeCast tail -F "${file_to_cast}" | ${program_} - # cleanup remaining background process and file on disk kill ${ytdl_pid} rm -rf "${tmpdir}" qutebrowser-1.1.1/misc/userscripts/ripbang0000755000175000017510000000170213216012203022062 0ustar florianflorian00000000000000#!/usr/bin/env python # # Adds DuckDuckGo bang as searchengine. # # Usage: # :spawn --userscript ripbang [bang]... # # Example: # :spawn --userscript ripbang amazon maps # from __future__ import print_function import os, re, requests, sys try: from urllib.parse import unquote except ImportError: from urllib import unquote for argument in sys.argv[1:]: bang = '!' + argument r = requests.get('https://duckduckgo.com/', params={'q': bang + ' SEARCHTEXT'}) searchengine = unquote(re.search("url=[^']+", r.text).group(0)) searchengine = searchengine.replace('url=', '') searchengine = searchengine.replace('/l/?kh=-1&uddg=', '') searchengine = searchengine.replace('SEARCHTEXT', '{}') if os.getenv('QUTE_FIFO'): with open(os.environ['QUTE_FIFO'], 'w') as fifo: fifo.write('set searchengines %s %s' % (bang, searchengine)) else: print('%s %s' % (bang, searchengine)) qutebrowser-1.1.1/misc/userscripts/rss0000755000175000017510000000707713230703634021275 0ustar florianflorian00000000000000#!/bin/sh # Copyright 2016 Jan Verbeek (blyxxyz) # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . # This script keeps track of URLs in RSS feeds and opens new ones. # New feeds can be added with ':spawn -u /path/to/userscripts/rss add' or # ':spawn -u /path/to/userscripts/rss '. # New items can be opened with ':spawn -u /path/to/userscripts/rss'. # The script doesn't really parse XML, and searches for things that look like # item links. It might open things that aren't real links, and it might miss # real links. config_dir="$HOME/.qute-rss" add_feed () { touch "feeds" if grep -Fq "$1" "feeds"; then notice "$1 is saved already." else printf '%s\n' "$1" >> "feeds" fi } # Show an error message and exit fail () { echo "message-error '$*'" > "$QUTE_FIFO" exit 1 } # Get a sorted list of item URLs from a RSS feed get_items () { $curl "$@" | grep "$text_only" -zo -e ']*>[^<>]*' \ -e ']*>[^<>]*' \ -e ']*href="[^"]*"' | grep "$text_only" -o 'http[^<>"]*' | sort | uniq } # Show an info message notice () { echo "message-info '$*'" > "$QUTE_FIFO" } # Update a database of a feed and open new URLs read_items () { cd read_urls || return 1 feed_file="$(echo "$1" | tr -d /)" feed_temp_file="$(mktemp "$feed_file.tmp.XXXXXXXXXX")" feed_new_items="$(mktemp "$feed_file.new.XXXXXXXXXX")" get_items "$1" > "$feed_temp_file" if [ ! -s "$feed_temp_file" ]; then notice "No items found for $1." rm "$feed_temp_file" "$feed_new_items" elif [ ! -f "$feed_file" ]; then notice "$1 is a new feed. All items will be marked as read." mv "$feed_temp_file" "$feed_file" rm "$feed_new_items" else sort -o "$feed_file" "$feed_file" comm -2 -3 "$feed_temp_file" "$feed_file" | tee "$feed_new_items" cat "$feed_new_items" >> "$feed_file" sort -o "$feed_file" "$feed_file" rm "$feed_temp_file" "$feed_new_items" fi | while read -r item; do echo "open -t $item" > "$QUTE_FIFO" done } if [ ! -d "$config_dir/read_urls" ]; then notice "Creating configuration directory." mkdir -p "$config_dir/read_urls" fi cd "$config_dir" || exit 1 if [ $# != 0 ]; then for arg in "$@"; do if [ "$arg" = "add" ]; then add_feed "$QUTE_URL" else add_feed "$arg" fi done exit fi if [ ! -f "feeds" ]; then fail "Add feeds by running ':spawn -u rss add' or ':spawn -u rss '." fi if curl --version >&-; then curl="curl -sL" elif wget --version >&-; then curl="wget -qO -" else fail "Either curl or wget is needed to run this script." fi # Detect GNU grep so we can force it to treat everything as text if < /dev/null grep --help 2>&1 | grep -q -- -a; then text_only="-a" fi while read -r feed_url; do read_items "$feed_url" & done < "$config_dir/feeds" wait qutebrowser-1.1.1/misc/userscripts/open_download0000755000175000017510000000641413230703634023310 0ustar florianflorian00000000000000#!/usr/bin/env bash # Both standalone script and qutebrowser userscript that opens a rofi menu with # all files from the download director and opens the selected file. It works # both as a userscript and a standalone script that is called from outside of # qutebrowser. # # Suggested keybinding (for "show downloads"): # spawn --userscript ~/.config/qutebrowser/open_download # sd # # Requirements: # - rofi (in a recent version) # - xdg-open and xdg-mime # - You should configure qutebrowser to download files to a single directory # - It comes in handy if you enable downloads.remove_finished. If you want to # see the recent downloads, just press "sd". # # Thorsten Wißmann, 2015 (thorsten` on freenode) # Any feedback is welcome! set -e # open a file from the download directory using rofi DOWNLOAD_DIR=${DOWNLOAD_DIR:-$QUTE_DOWNLOAD_DIR} DOWNLOAD_DIR=${DOWNLOAD_DIR:-$HOME/Downloads} # the name of the rofi command ROFI_CMD=${ROFI_CMD:-rofi} ROFI_ARGS=${ROFI_ARGS:-} msg() { local cmd="$1" shift local msg="$*" if [ -z "$QUTE_FIFO" ] ; then echo "$cmd: $msg" >&2 else echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO" fi } die() { msg error "$*" if [ -n "$QUTE_FIFO" ] ; then # when run as a userscript, the above error message already informs the # user about the failure, and no additional "userscript exited with status # 1" is needed. exit 0; else exit 1; fi } if ! [ -d "$DOWNLOAD_DIR" ] ; then die "Download directory »$DOWNLOAD_DIR« not found!" fi if ! which "${ROFI_CMD}" > /dev/null ; then die "Rofi command »${ROFI_CMD}« not found in PATH!" fi rofi_default_args=( -monitor -2 # place above window -location 6 # aligned at the bottom -width 100 # use full window width -i -no-custom -format i # make rofi return the index -l 10 -p 'Open download:' -dmenu ) crop-first-column() { local maxlength=${1:-40} local expression='s|^\([^\t]\{0,'"$maxlength"'\}\)[^\t]*\t|\1\t|' sed "$expression" } ls-files() { # add the slash at the end of the download dir enforces to follow the # symlink, if the DOWNLOAD_DIR itself is a symlink # shellcheck disable=SC2010 ls -Q --quoting-style escape -h -o -1 -A -t "${DOWNLOAD_DIR}/" \ | grep '^[-]' \ | cut -d' ' -f3- \ | sed 's,^\(.*[^\]\) \(.*\)$,\2\t\1,' \ | sed 's,\\\(.\),\1,g' } mapfile -t entries < <(ls-files) # we need to manually check that there are items, because rofi doesn't show up # if there are no items and -no-custom is passed to rofi. if [ "${#entries[@]}" -eq 0 ] ; then die "Download directory »${DOWNLOAD_DIR}« empty" fi line=$(printf '%s\n' "${entries[@]}" \ | crop-first-column 55 \ | column -s $'\t' -t \ | $ROFI_CMD "${rofi_default_args[@]}" "$ROFI_ARGS") || true if [ -z "$line" ]; then exit 0 fi file="${entries[$line]}" file="${file%%$'\t'*}" path="$DOWNLOAD_DIR/$file" filetype=$(xdg-mime query filetype "$path") application=$(xdg-mime query default "$filetype") if [ -z "$application" ] ; then die "Do not know how to open »$file« of type $filetype" fi msg info "Opening »$file« (of type $filetype) with ${application%.desktop}" xdg-open "$path" & qutebrowser-1.1.1/misc/userscripts/openfeeds0000755000175000017510000000274713216012203022422 0ustar florianflorian00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2015 jnphilipp # Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . # Opens all links to feeds defined in the head of a site # # Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then: # :bind gF spawn --userscript openfeeds # # Use the hotkey to open the feeds in new tab/window, press 'gF' to open # import os import re from bs4 import BeautifulSoup from urllib.parse import urljoin with open(os.environ['QUTE_HTML'], 'r') as f: soup = BeautifulSoup(f) with open(os.environ['QUTE_FIFO'], 'w') as f: for link in soup.find_all('link', rel='alternate', type=re.compile(r'application/((rss|rdf|atom)\+)?xml|text/xml')): f.write('open -t %s\n' % urljoin(os.environ['QUTE_URL'], link.get('href'))) qutebrowser-1.1.1/misc/userscripts/readability0000755000175000017510000000233313216012203022732 0ustar florianflorian00000000000000#!/usr/bin/env python # # Executes python-readability on current page and opens the summary as new tab. # # Depends on the python-readability package, or its fork: # # - https://github.com/buriy/python-readability # - https://github.com/bookieio/breadability # # Usage: # :spawn --userscript readability # from __future__ import absolute_import import codecs, os tmpfile=os.path.expanduser('~/.local/share/qutebrowser/userscripts/readability.html') if not os.path.exists(os.path.dirname(tmpfile)): os.makedirs(os.path.dirname(tmpfile)) with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source: data = source.read() try: from breadability.readable import Article as reader doc = reader(data) content = doc.readable except ImportError: from readability import Document doc = Document(data) content = doc.summary().replace('', '%s' % doc.title()) with codecs.open(tmpfile, 'w', 'utf-8') as target: target.write('') target.write(content) with open(os.environ['QUTE_FIFO'], 'w') as fifo: fifo.write('open -t %s' % tmpfile) qutebrowser-1.1.1/misc/userscripts/qutebrowser_viewsource0000755000175000017510000000220513202372777025320 0ustar florianflorian00000000000000#!/usr/bin/env bash # Copyright 2015 Zach-Button # Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . # # This script fetches the unprocessed HTML source for a page and opens it in vim. # :bind gf spawn --userscript qutebrowser_viewsource # # Caveat: Does not use authentication of any kind. Add it in if you want it to. # path=$(mktemp --tmpdir qutebrowser_XXXXXXXX.html) curl "$QUTE_URL" > "$path" urxvt -e vim "$path" rm "$path" qutebrowser-1.1.1/misc/userscripts/view_in_mpv0000755000175000017510000001201513230703634022774 0ustar florianflorian00000000000000#!/usr/bin/env bash # # Behavior: # Userscript for qutebrowser which views the current web page in mpv using # sensible mpv-flags. While viewing the page in MPV, all