A web browser-independent bookmark manager that uses rofi as an interface to save, open or type out URL bookmarks.
git clone https://mcol.xyz/code/bkmkfi
Log | Files | Refs | README | LICENSE

commit 90fa933a16a1d3122ce265bdad9053d44680af9a
parent 6ebba8641b285737c0223d90708333a467bfe4ef
Author: mclgn <mlv@posteo.net>
Date:   Sun,  2 Jun 2019 12:02:48 +0100

structure as package

Diffstat:
AREADME.md | 8++++++++
Dbkmkfi | 317-------------------------------------------------------------------------------
Asetup.py | 21+++++++++++++++++++++
Asrc/__init__.py | 1+
Asrc/bkmkfi | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/bookmarks | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 356 insertions(+), 317 deletions(-)

diff --git a/README.md b/README.md @@ -0,0 +1,8 @@ +bkmkfi + +Bookmarks manager with rofi interface + +Features: + - rofi interface is fast and easy to use + - save, copy, or type out bookmarks + - independent of browsers diff --git a/bkmkfi b/bkmkfi @@ -1,317 +0,0 @@ -#!/usr/bin/env python3 -# -# Manage web page bookmarks using rofi -# - - -## Setup -from subprocess import PIPE, run -from json import load, dump -import os -from sys import exit -import argparse -from pykeyboard import PyKeyboard -from re import search - -home = os.path.expanduser('~') -path = home + '/.config/bkmks.json' - - -## Launch rofi instance -def rofi(stdin='', prompt=None): - """ - Execute rofi with specified inputs and prompt - """ - kwargs = {} - kwargs['stdout'] = PIPE - kwargs['universal_newlines'] = True - - args = ['rofi', '-dmenu'] - if prompt: - args.append('-p') - args.append(prompt) - - result = run(args, input=stdin, **kwargs) - - # escape or empty input is cancellation - if result.returncode or not result.stdout: - exit(1) - - # strip newline - result.stdout = result.stdout[0:-1] - return result.stdout - - -## Parse inputs -def parse_args(): - """ Parse command line arguments """ - - parser = argparse.ArgumentParser( - usage = 'bkmkfi [-h | -o [COMMAND] | -d | -c | -t] [-b BOOKMARKS]', - description = 'rofi bookmark manager', - ) - - parser.add_argument('-b', '--bookmarks', nargs=1, - help='bookmarks JSON (default ~/.config/bkmks.json)', - default=[home + '/.config/bkmks.json'], - type=str) - - parser.add_argument('-o', '--open', nargs=1, - help='open command (e.g. "tor-browser --allow-remote %%s")', - default=[''], - type=str) - - parser.add_argument('-d', '--delete', - help='delete bookmark', - action='store_true') - - parser.add_argument('-c', '--copy', - help='copy bookmark URL to clipboard', - action='store_true') - - parser.add_argument('-t', '--type', - help='type bookmark URL with keyboard', - action='store_true') - - args = parser.parse_args() - return args - - -## Manage bookmarks in class -class Bookmarks(object): - """ - Manage Bookmarks - - Bookmarks are saved in a json file with the following structure: - - url1 - [[ folder1 ]] - url2 - url3 - - URLs can be in folders or at the top level - Folders are indicated by the format '[[ folder title ]]' - """ - - def __init__(self, p): - """ Load bookmarks from file """ - path = p.bookmarks[0] - - if os.path.isfile(path): - try: - with open(path, 'r') as bk_file: - bookmarks = load(bk_file) - except: - print("%s could not be loaded." % path) - exit(1) - - else: - print("Bookmarks database %s does not exist" % path) - print("Making a new database") - bookmarks = [] - - self.path = path - self.bookmarks = bookmarks - self.selected = None - self.selected_isnew = False - - -## Select bookmark - def select(self): - """ - Select or add bookmark - This is the first action that selects the URL for further processing - """ - - bookmarks = self.bookmarks - - stdin='' - if bookmarks: - prompt = 'Select or add new bookmark' - for entry in bookmarks: - if isinstance(entry, dict): - folder_name = list(entry.keys())[0] - stdin += folder_name + '\n' - else: - stdin += entry + '\n' - else: - prompt = 'Add a new bookmark' - - selected = [ - rofi( - stdin=stdin, - prompt=prompt - ) - ] - - # continue selecting into folders - if search("^\#\#.*\#\#$", selected[-1]): - while search("^\#\#.*\#\#$", selected[-1]): - # traverse all levels - current_level = bookmarks - for i in range(len(selected)): - for entry in current_level: - if isinstance(entry, dict) and selected[i] in entry: - current_level = entry[selected[i]] - break - else: - current_level = [] - stdin='' - for entry in current_level: - if isinstance(entry, dict): - folder_name = list(entry.keys())[0] - stdin += folder_name + '\n' - else: - stdin += entry + '\n' - selected.append( - rofi( - stdin=stdin, - prompt="> ".join(selected) + "> " - ) - ) - if not selected[-1] in current_level: - self.selected_isnew = True - current_level.append(selected[-1]) - last_level = current_level - current_level = bookmarks - bookmarks[ - - - else: - # selection is at top level - if not selected[-1] in bookmarks: - self.selected_isnew = True - - exit(1) - - self.selected = selected - self.bookmarks = bookmarks - self.get_url() - - -## Get URL from entry - def get_url(self): - """ - Extract URL string from bookmark entry - This will attempt to find a URL, looking for: - http[s]:// www. .co .net - It finds the first match, in the order shown here - """ - http_url = search("(?P<url>https?://[^\s]+)", self.selected) - if http_url: - self.url = http_url.group("url") - return - - www_url = search("(?P<url>www\.[^\s]+)", self.selected) - if www_url: - self.url = www_url.group("url") - return - - co_url = search("(?P<url>\.co[^\s]+)", self.selected) - if co_url: - self.url = co_url.group("url") - return - - net_url = search("(?P<url>\.net[^\s]+)", self.selected) - if net_url: - self.url = net_url.group("url") - return - - self.url = self.selected - - -## Open URL in new tab - def open(self, open_cmd): - """ - Open URL in web browser new tab - Usage: bkmkfi -o "command %s" - %s is replaced with the URL - """ - if os.fork() == 0: - os.system(open_cmd % self.url) - - -## Type URL with keyboard - def type(self): - """ - Type selected URL - This depends on PyKeyboard - """ - k = PyKeyboard() - k.type_string(self.url) - - -## Delete entry - def delete(self): - """ - Delete bookmark entry - """ - self.bookmarks.remove(self.selected) - self.save() - - -## Copy URL to clipboard - def copy(self): - """ - Copy bookmark URL to clipboard - This copies by piping the URL to xclip - """ - - cmd = 'printf ' + self.url + ' | xclip -selection clipboard' - os.system(cmd) - - -## Save bookmarks file - def save(self): - """ Save bookmarks """ - selected = self.selected - current_level = self.bookmarks - for i in range(len(selected)): - if search("^\#\#.*\#\#$", selected[i]): - for entry in current_level - if isinstance(entry, dict) and selected[i] in entry: - current_level = entry[selected[i]] - # TODO HERE - - - else: - if not selected[i] in current_level: - self.bookmarks.append(selected[i]) - - with open(self.path, 'w') as bk_file: - dump(self.bookmarks, bk_file, indent=4) - - -## Main -def main(): - p = parse_args() - - # Create bookmarks class - bkmks = Bookmarks(p) - - # Select entry - bkmks.select() - - # Decide what to do with it - if bkmks.selected_isnew: - # just adding an entry - bkmks.save() - - elif p.open[0]: - # open URL in new tab - bkmks.open(p.open[0]) - - elif p.type: - # type out URL - bkmks.type() - - elif p.delete: - # delete entry - bkmks.delete() - - elif p.copy: - # copy entry to clipboard - bkmks.copy() - -if __name__ == '__main__': - main() diff --git a/setup.py b/setup.py @@ -0,0 +1,21 @@ +import setuptools + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name="bkmkfi", + version="0.0.1", + author="mcol", + author_email="mcol@posteo.net", + description="rofi-based bookmarks manager", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/m-col/bkmkfi", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Operating System :: POSIX", + ], +) diff --git a/src/__init__.py b/src/__init__.py @@ -0,0 +1 @@ +name = "bkmkfi" diff --git a/src/bkmkfi b/src/bkmkfi @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# +# bkmkfi +# Manage web page bookmarks using rofi +# Main script +# + + +## Setup +from subprocess import PIPE, run +from json import load, dump +import os +from sys import exit +import argparse +from pykeyboard import PyKeyboard +from re import search + +home = os.path.expanduser('~') +path = home + '/.config/bkmks.json' + + +## Launch rofi instance +def rofi(stdin='', prompt=None): + """ + Execute rofi with specified inputs and prompt + """ + kwargs = {} + kwargs['stdout'] = PIPE + kwargs['universal_newlines'] = True + + args = ['rofi', '-dmenu'] + if prompt: + args.append('-p') + args.append(prompt) + + result = run(args, input=stdin, **kwargs) + + # escape or empty input is cancellation + if result.returncode or not result.stdout: + exit(1) + + # strip newline + result.stdout = result.stdout[0:-1] + return result.stdout + + +## Parse inputs +def parse_args(): + """ Parse command line arguments """ + + parser = argparse.ArgumentParser( + usage = 'bkmkfi [-h | -o [COMMAND] | -d | -c | -t] [-b BOOKMARKS]', + description = 'rofi bookmark manager', + ) + + parser.add_argument('-b', '--bookmarks', nargs=1, + help='bookmarks JSON (default ~/.config/bkmks.json)', + default=[home + '/.config/bkmks.json'], + type=str) + + parser.add_argument('-o', '--open', nargs=1, + help='open command (e.g. "tor-browser --allow-remote %%s")', + default=[''], + type=str) + + parser.add_argument('-d', '--delete', + help='delete bookmark', + action='store_true') + + parser.add_argument('-c', '--copy', + help='copy bookmark URL to clipboard', + action='store_true') + + parser.add_argument('-t', '--type', + help='type bookmark URL with keyboard', + action='store_true') + + args = parser.parse_args() + return args + + +## Main +def main(): + p = parse_args() + + # Create bookmarks class + bkmks = Bookmarks(p) + + # Select entry + bkmks.select() + + # Decide what to do with it + if bkmks.selected_isnew: + # just adding an entry + bkmks.save() + + elif p.open[0]: + # open URL in new tab + bkmks.open(p.open[0]) + + elif p.type: + # type out URL + bkmks.type() + + elif p.delete: + # delete entry + bkmks.delete() + + elif p.copy: + # copy entry to clipboard + bkmks.copy() + +if __name__ == '__main__': + main() diff --git a/src/bookmarks b/src/bookmarks @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# +# bkmkfi +# Manage web page bookmarks using rofi +# bookmarks class +# + +## Manage bookmarks in class +class Bookmarks(object): + """ + Manage Bookmarks + + Bookmarks are saved in a json file with the following structure: + + url1 + [[ folder1 ]] + url2 + url3 + + URLs can be in folders or at the top level + Folders are indicated by the format '[[ folder title ]]' + """ + + def __init__(self, p): + """ Load bookmarks from file """ + path = p.bookmarks[0] + + if os.path.isfile(path): + try: + with open(path, 'r') as bk_file: + bookmarks = load(bk_file) + except: + print("%s could not be loaded." % path) + exit(1) + + else: + print("Bookmarks database %s does not exist" % path) + print("Making a new database") + bookmarks = [] + + self.path = path + self.bookmarks = bookmarks + self.selected = None + self.selected_isnew = False + + +## Select bookmark + def select(self): + """ + Select or add bookmark + This is the first action that selects the URL for further processing + """ + + bookmarks = self.bookmarks + + stdin='' + if bookmarks: + prompt = 'Select or add new bookmark' + for entry in bookmarks: + if isinstance(entry, dict): + folder_name = list(entry.keys())[0] + stdin += folder_name + '\n' + else: + stdin += entry + '\n' + else: + prompt = 'Add a new bookmark' + + selected = [ + rofi( + stdin=stdin, + prompt=prompt + ) + ] + + # continue selecting into folders + if search("^\#\#.*\#\#$", selected[-1]): + while search("^\#\#.*\#\#$", selected[-1]): + # traverse all levels + current_level = bookmarks + for i in range(len(selected)): + for entry in current_level: + if isinstance(entry, dict) and selected[i] in entry: + current_level = entry[selected[i]] + break + else: + current_level = [] + stdin='' + for entry in current_level: + if isinstance(entry, dict): + folder_name = list(entry.keys())[0] + stdin += folder_name + '\n' + else: + stdin += entry + '\n' + selected.append( + rofi( + stdin=stdin, + prompt="> ".join(selected) + "> " + ) + ) + if not selected[-1] in current_level: + self.selected_isnew = True + current_level.append(selected[-1]) + last_level = current_level + current_level = bookmarks + bookmarks[ + + + else: + # selection is at top level + if not selected[-1] in bookmarks: + self.selected_isnew = True + + exit(1) + + self.selected = selected + self.bookmarks = bookmarks + self.get_url() + + +## Get URL from entry + def get_url(self): + """ + Extract URL string from bookmark entry + This will attempt to find a URL, looking for: + http[s]:// www. .co .net + It finds the first match, in the order shown here + """ + http_url = search("(?P<url>https?://[^\s]+)", self.selected) + if http_url: + self.url = http_url.group("url") + return + + www_url = search("(?P<url>www\.[^\s]+)", self.selected) + if www_url: + self.url = www_url.group("url") + return + + co_url = search("(?P<url>\.co[^\s]+)", self.selected) + if co_url: + self.url = co_url.group("url") + return + + net_url = search("(?P<url>\.net[^\s]+)", self.selected) + if net_url: + self.url = net_url.group("url") + return + + self.url = self.selected + + +## Open URL in new tab + def open(self, open_cmd): + """ + Open URL in web browser new tab + Usage: bkmkfi -o "command %s" + %s is replaced with the URL + """ + if os.fork() == 0: + os.system(open_cmd % self.url) + + +## Type URL with keyboard + def type(self): + """ + Type selected URL + This depends on PyKeyboard + """ + k = PyKeyboard() + k.type_string(self.url) + + +## Delete entry + def delete(self): + """ + Delete bookmark entry + """ + self.bookmarks.remove(self.selected) + self.save() + + +## Copy URL to clipboard + def copy(self): + """ + Copy bookmark URL to clipboard + This copies by piping the URL to xclip + """ + + cmd = 'printf ' + self.url + ' | xclip -selection clipboard' + os.system(cmd) + + +## Save bookmarks file + def save(self): + """ Save bookmarks """ + selected = self.selected + current_level = self.bookmarks + for i in range(len(selected)): + if search("^\#\#.*\#\#$", selected[i]): + for entry in current_level + if isinstance(entry, dict) and selected[i] in entry: + current_level = entry[selected[i]] + # TODO HERE + + + else: + if not selected[i] in current_level: + self.bookmarks.append(selected[i]) + + with open(self.path, 'w') as bk_file: + dump(self.bookmarks, bk_file, indent=4) + +