├── images ├── brave.png ├── chrome.png └── chromium.png ├── main.py ├── versions.json ├── manifest.json ├── README.md ├── .gitignore └── ChromeBookmarks.py /images/brave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nortmas/chrome-bookmarks/HEAD/images/brave.png -------------------------------------------------------------------------------- /images/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nortmas/chrome-bookmarks/HEAD/images/chrome.png -------------------------------------------------------------------------------- /images/chromium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nortmas/chrome-bookmarks/HEAD/images/chromium.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from ChromeBookmarks import ChromeBookmarks 2 | 3 | if __name__ == '__main__': 4 | ChromeBookmarks().run() 5 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "required_api_version": "^1.0.0", 4 | "commit": "python2" 5 | }, 6 | { 7 | "required_api_version": "^2.0.0", 8 | "commit": "master" 9 | } 10 | ] -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "required_api_version": "^2.0.0", 3 | "name": "Google Chrome Bookmarks", 4 | "description": "Search and launch Google Chrome bookmarks", 5 | "developer_name": "Dmitry Antonenko", 6 | "icon": "images/chrome.png", 7 | "options": { 8 | "query_debounce": 0.1 9 | }, 10 | "preferences": [ 11 | { 12 | "id": "chrome_bookmarks", 13 | "type": "keyword", 14 | "name": "Chrome bookmarks", 15 | "description": "Find in chrome bookmarks", 16 | "default_value": "cb" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Google Chrome Bookmarks extension for the [Ulauncher](https://ulauncher.io/). 2 | 3 | Find and launch your Google Chrome bookmarks through the Ulauncher. 4 | 5 | [Other extensions](https://ext.ulauncher.io/) 6 | 7 | ![extension screenshot](https://i.imgur.com/1NiDqKI.png) 8 | 9 | ## Requirements 10 | 11 | * Locate command installed. If you do not have it installed, you can install it via `sudo apt install -y mlocate` 12 | 13 | ## FYI 14 | 15 | * This extension was developed for personal usage and tested on Ubuntu 20.04 LTS and Chrome browser. I've also added the 'Chromium' and 'Brave' browser to the bookmark search list but unfortunately, I don't have time to support and test all browsers and Linux distribution variations so you might face bugs. If you have an issue in your chromium based browser/Linux combination feel free to investigate and come up with the patch and please create issues only related to the Ubuntu and Chrome browser combination. 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | #PyCharm 104 | .idea 105 | -------------------------------------------------------------------------------- /ChromeBookmarks.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import os 4 | 5 | from ulauncher.api.client.Extension import Extension 6 | from ulauncher.api.client.EventListener import EventListener 7 | from ulauncher.api.shared.event import KeywordQueryEvent, ItemEnterEvent 8 | from ulauncher.api.shared.item.ExtensionResultItem import ExtensionResultItem 9 | from ulauncher.api.shared.action.RenderResultListAction import RenderResultListAction 10 | from ulauncher.api.shared.action.OpenUrlAction import OpenUrlAction 11 | 12 | logging.basicConfig() 13 | logger = logging.getLogger(__name__) 14 | 15 | support_browsers = ['google-chrome', 'chromium', 'Brave-Browser'] 16 | browser_imgs = { 17 | 'google-chrome': 'images/chrome.png', 18 | 'chromium': 'images/chromium.png', 19 | 'Brave-Browser': 'images/brave.png', 20 | } 21 | 22 | 23 | class KeywordQueryEventListener(EventListener): 24 | def on_event(self, event, extension): 25 | items = extension.get_items(event.get_argument()) 26 | return RenderResultListAction(items) 27 | 28 | 29 | class ChromeBookmarks(Extension): 30 | matches_len = 0 31 | max_matches_len = 10 32 | 33 | def __init__(self): 34 | self.bookmarks_paths = self.find_bookmarks_paths() 35 | super(ChromeBookmarks, self).__init__() 36 | self.subscribe(KeywordQueryEvent, KeywordQueryEventListener()) 37 | 38 | @staticmethod 39 | def find_bookmarks_paths(): 40 | res_lst = [] 41 | for browser in support_browsers: 42 | f = os.popen('locate %s | grep Bookmarks' % browser) 43 | res = f.read().split('\n') 44 | if len(res) == 0: 45 | logger.info('Path to the %s Bookmarks was not found' % browser) 46 | continue 47 | for one_path in res: 48 | if one_path.endswith('Bookmarks'): 49 | res_lst.append((one_path, browser)) 50 | 51 | if len(res_lst) == 0: 52 | logger.exception('Path to the Chrome Bookmarks was not found') 53 | return res_lst 54 | 55 | def find_rec(self, data, query, matches): 56 | 57 | if self.matches_len >= self.max_matches_len: 58 | return 59 | 60 | if data['type'] == 'folder': 61 | for child in data['children']: 62 | self.find_rec(child, query, matches) 63 | else: 64 | res = data['name'].lower().find(query.lower()) 65 | if res != -1: 66 | matches.append(data) 67 | self.matches_len += 1 68 | 69 | def get_items(self, query): 70 | 71 | items = [] 72 | self.matches_len = 0 73 | 74 | if query is None: 75 | query = '' 76 | 77 | for bookmarks_path, browser in self.bookmarks_paths: 78 | 79 | matches = [] 80 | 81 | with open(bookmarks_path) as data_file: 82 | data = json.load(data_file) 83 | self.find_rec(data['roots']['bookmark_bar'], query, matches) 84 | self.find_rec(data['roots']['other'], query, matches) 85 | 86 | for bookmark in matches: 87 | bookmark_name = bookmark['name'].encode('utf-8') 88 | bookmark_url = bookmark['url'].encode('utf-8') 89 | item = ExtensionResultItem( 90 | icon=browser_imgs.get(browser), 91 | name='%s' % bookmark_name.decode('utf-8'), 92 | description='%s' % bookmark_url.decode('utf-8'), 93 | on_enter=OpenUrlAction(bookmark_url.decode('utf-8')) 94 | ) 95 | items.append(item) 96 | 97 | return items 98 | --------------------------------------------------------------------------------