Support index with -w.

This commit is contained in:
Arun Prakash Jana 2017-02-07 23:33:51 +05:30
parent bbf7428818
commit bd4b85ad32
3 changed files with 118 additions and 103 deletions

View File

@ -11,12 +11,12 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://asciinema.org/a/8h26q3ezkm15h1ql8m7onuuzx"><img src="https://asciinema.org/a/9l6s2ppivpo661nu5slwk2t6y.png" alt="Asciicast" width="734"/></a> <a href="https://asciinema.org/a/0ya8tyzm94cgi6mv4dhxnnem6"><img src="https://asciinema.org/a/9l6s2ppivpo661nu5slwk2t6y.png" alt="Asciicast" width="734"/></a>
</p> </p>
`buku` is a powerful bookmark management utility written in Python3 and SQLite3. When I started writing it, I couldn't find a flexible cmdline solution with a private, portable, merge-able database along with browser integration. Hence, `buku` (after my son's nickname). `buku` is a powerful bookmark management utility written in Python3 and SQLite3. When I started writing it, I couldn't find a flexible cmdline solution with a private, portable, merge-able database along with browser integration. Hence, `buku` (after my son's nickname).
`buku` fetches the title of a bookmarked web page and stores it along with any additional comments and tags. With multiple options to search bookmarks, including regex and a deep scan mode (particularly for URLs), finding a bookmark is very easy. Multiple search results can be opened in the browser at once. `buku` fetches the title of a bookmarked web page and stores it along with any additional comments and tags. You can use your favourite editor to compose and update bookmarks. With multiple options to search bookmarks, including regex and a deep scan mode (particularly for URLs), finding a bookmark is very easy. Multiple search results can be opened in the browser at once.
Though a terminal utility, it's possible to add bookmarks to `buku` without touching the terminal! Refer to the section on [GUI integration](#gui-integration). If you prefer the terminal, thanks to the shell completion scripts, you don't need to memorize any of the options. There's an Easter egg to revisit random forgotten bookmarks too. Though a terminal utility, it's possible to add bookmarks to `buku` without touching the terminal! Refer to the section on [GUI integration](#gui-integration). If you prefer the terminal, thanks to the shell completion scripts, you don't need to memorize any of the options. There's an Easter egg to revisit random forgotten bookmarks too.
@ -167,8 +167,10 @@ EDIT OPTIONS:
-a: do not set title, -u: clear title -a: do not set title, -u: clear title
-c, --comment [...] description of the bookmark, works with -c, --comment [...] description of the bookmark, works with
-a, -u; clears comment, if no arguments -a, -u; clears comment, if no arguments
-w, --write [editor] open editor to edit a single bookmark -w, --write [editor|index]
works with -a (default), -u open editor to edit a single bookmark
works with -a; if an index is passed to
edit and update, EDITOR must be set
--immutable N disable title fetch from web on update --immutable N disable title fetch from web on update
works with -a, -u works with -a, -u
N=0: mutable (default), N=1: immutable N=0: mutable (default), N=1: immutable
@ -261,8 +263,9 @@ SYMBOLS:
- Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown in bold within `[]` after the URL. - Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown in bold within `[]` after the URL.
- **Encryption** is optional and manual. AES256 algorithm is used. To use encryption, the database file should be unlocked (-k) before using `buku` and locked (-l) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is *unencrypted on creation*. - **Encryption** is optional and manual. AES256 algorithm is used. To use encryption, the database file should be unlocked (-k) before using `buku` and locked (-l) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is *unencrypted on creation*.
- **Editor** support: - **Editor** support:
- A single bookmark can be edited to add or update. The editor can be set using the environment variable *EDITOR* or by explicitly specifying the editor. The latter takes preference. - A single bookmark can be edited before adding. The editor can be set using the environment variable *EDITOR* or by explicitly specifying the editor. The latter takes preference. If -a is used along with -w, the details are populated in the editor template.
- All lines beginning with "#" will be striped. Then line 1 will be treated as the URL, line 2 will be the title, line 3 will be comma separated tags, and the rest of the lines will be parsed as descriptions. - In case of edit and update (a single bookmark), the existing record details are fetched from DB and populated in the editor template. The environment variable EDITOR must be set Note that -u works independently of -w.
- All lines beginning with "#" will be stripped. Then line 1 will be treated as the URL, line 2 will be the title, line 3 will be comma separated tags, and the rest of the lines will be parsed as descriptions.
- **Proxy** support: environment variable *https_proxy*, if defined, is used to tunnel data for both http and https connections. The supported format is: - **Proxy** support: environment variable *https_proxy*, if defined, is used to tunnel data for both http and https connections. The supported format is:
http[s]://[username:password@]proxyhost:proxyport/ http[s]://[username:password@]proxyhost:proxyport/
@ -379,20 +382,20 @@ Note that URL must precede tags.
4. **Add** a bookmark **without a title** (works for update too): 4. **Add** a bookmark **without a title** (works for update too):
$ buku -a https://ddg.gg search engine, privacy -t $ buku -a https://ddg.gg search engine, privacy -t
5. **Update** existing bookmark at index 15012014 with new URL, tags and comments, fetch title from the web: 5. **Edit and update** a bookmark from editor:
$ buku -w 15012014
This will open the existing bookmark's details in the editor for modifications. Environment variable `EDITOR` must be set.
6. **Update** existing bookmark at index 15012014 with new URL, tags and comments, fetch title from the web:
$ buku -u 15012014 --url http://ddg.gg/ --tag web search, utilities -c Private search engine $ buku -u 15012014 --url http://ddg.gg/ --tag web search, utilities -c Private search engine
6. **Fetch and update only title** for bookmark at 15012014: 7. **Fetch and update only title** for bookmark at 15012014:
$ buku -u 15012014 $ buku -u 15012014
7. **Update only comment** for bookmark at 15012014: 8. **Update only comment** for bookmark at 15012014:
$ buku -u 15012014 -c this is a new comment $ buku -u 15012014 -c this is a new comment
Applies to --url, --title and --tag too. Applies to --url, --title and --tag too.
8. **Edit and update** a bookmark from editor:
$ buku -w -u 15012014
This will open the existing bookmark's details in the editor for modifications.
9. **Export** bookmarks tagged `tag 1` or `tag 2` to HTML and markdown: 9. **Export** bookmarks tagged `tag 1` or `tag 2` to HTML and markdown:
$ buku -e bookmarks.html --tag tag 1, tag 2 $ buku -e bookmarks.html --tag tag 1, tag 2

35
buku.1
View File

@ -73,8 +73,9 @@ Bookmarks with immutable titles are listed with bold '(L)' after the URL.
.PP .PP
.IP 10. 4 .IP 10. 4
\fBEditor\fR support: \fBEditor\fR support:
- A single bookmark can be edited to add or update. The editor can be set using the environment variable *EDITOR* or by explicitly specifying the editor. The latter takes precedence. - A single bookmark can be edited before adding. The editor can be set using the environment variable *EDITOR* or by explicitly specifying the editor. The latter takes precedence. If -a is used along with -w, the details are populated in the editor template.
- All lines beginning with "#" will be striped. Then line 1 will be treated as the URL, line 2 will be the title, line 3 will be comma separated tags, and the rest of the lines will be parsed as descriptions. - In case of edit and update (a single bookmark), the existing record details are fetched from DB and populated in the editor template. The environment variable EDITOR must be set. Note that -u works independently of -w.
- All lines beginning with "#" will be stripped. Then line 1 will be treated as the URL, line 2 will be the title, line 3 will be comma separated tags, and the rest of the lines will be parsed as descriptions.
.PP .PP
.IP 11. 4 .IP 11. 4
\fBProxy\fR support: please refer to the \fBENVIRONMENT\fR section. \fBProxy\fR support: please refer to the \fBENVIRONMENT\fR section.
@ -110,8 +111,8 @@ Manually specify the title, works with --add, --update. Omits or clears the titl
.BI \-c " " \--comment " [...]" .BI \-c " " \--comment " [...]"
Add comment or description on the bookmark, works with --add, --update. Clears the comment, if no arguments passed. Add comment or description on the bookmark, works with --add, --update. Clears the comment, if no arguments passed.
.TP .TP
.BI \-w " " \--write " [editor]" .BI \-w " " \--write " [editor|index]"
Open editor and edit a bookmark before adding or updating it. The edited bookmark is added if either --add or --update is not specified. Open editor and edit a bookmark before adding or updating it.
.TP .TP
.BI \--immutable " N" .BI \--immutable " N"
Set the title of a bookmark immutable during updates. Works with --add, --update. N=1 sets the immutable flag, N=0 removes it. If omitted, bookmarks are added with N=0. Set the title of a bookmark immutable during updates. Works with --add, --update. N=1 sets the immutable flag, N=0 removes it. If omitted, bookmarks are added with N=0.
@ -327,6 +328,17 @@ Note that URL must precede tags.
.EE .EE
.PP .PP
.IP 5. 4 .IP 5. 4
\fBEdit and update\fR a bookmark from editor:
.PP
.EX
.IP
.B buku -w 15012014
.EE
.PP
.IP "" 4
This will open the existing bookmark's details in the editor for modifications. Environment variable \fIEDITOR\fR must be set.
.PP
.IP 6. 4
\fBUpdate\fR existing bookmark at index 15012014 with new URL, tags and comments, fetch title from the web: \fBUpdate\fR existing bookmark at index 15012014 with new URL, tags and comments, fetch title from the web:
.PP .PP
.EX .EX
@ -334,7 +346,7 @@ Note that URL must precede tags.
.B buku -u 15012014 --url http://ddg.gg/ --tag web search, utilities -c Private search engine .B buku -u 15012014 --url http://ddg.gg/ --tag web search, utilities -c Private search engine
.EE .EE
.PP .PP
.IP 6. 4 .IP 7. 4
\fBFetch and update only title\fR for bookmark at 15012014: \fBFetch and update only title\fR for bookmark at 15012014:
.PP .PP
.EX .EX
@ -342,7 +354,7 @@ Note that URL must precede tags.
.B buku -u 15012014 .B buku -u 15012014
.EE .EE
.PP .PP
.IP 7. 4 .IP 8. 4
\fBUpdate only comment\fR for bookmark at 15012014: \fBUpdate only comment\fR for bookmark at 15012014:
.PP .PP
.EX .EX
@ -353,17 +365,6 @@ Note that URL must precede tags.
.IP "" 4 .IP "" 4
Applies to --url, --title and --tag too. Applies to --url, --title and --tag too.
.PP .PP
.IP 8. 4
\fBEdit and update\fR a bookmark from editor:
.PP
.EX
.IP
.B buku -w -u 15012014
.EE
.PP
.IP "" 4
This will open the existing bookmark's details in the editor for modifications.
.PP
.IP 9. 4 .IP 9. 4
\fBExport\fR bookmarks tagged 'tag 1' or 'tag 2' to HTML and markdown: \fBExport\fR bookmarks tagged 'tag 1' or 'tag 2' to HTML and markdown:
.PP .PP

157
buku.py
View File

@ -67,16 +67,6 @@ logger = logging.getLogger()
logdbg = logger.debug logdbg = logger.debug
logerr = logger.error logerr = logger.error
def read_in(msg):
disable_sigint_handler()
message = None
try:
message = input(msg)
except KeyboardInterrupt:
print('Interrupted.')
enable_sigint_handler()
return message
class BukuHTMLParser(HTMLParser.HTMLParser): class BukuHTMLParser(HTMLParser.HTMLParser):
'''Class to parse and fetch the title '''Class to parse and fetch the title
@ -2174,6 +2164,18 @@ def regexp(expr, item):
return re.search(expr, item, re.IGNORECASE) is not None return re.search(expr, item, re.IGNORECASE) is not None
def read_in(msg):
disable_sigint_handler()
message = None
try:
message = input(msg)
except KeyboardInterrupt:
print('Interrupted.')
enable_sigint_handler()
return message
def sigint_handler(signum, frame): def sigint_handler(signum, frame):
'''Custom SIGINT handler''' '''Custom SIGINT handler'''
@ -2185,11 +2187,13 @@ def sigint_handler(signum, frame):
# Do a hard exit from here # Do a hard exit from here
os._exit(1) os._exit(1)
DEFAULT_HANDLER = signal.signal(signal.SIGINT, sigint_handler) DEFAULT_HANDLER = signal.signal(signal.SIGINT, sigint_handler)
def disable_sigint_handler(): def disable_sigint_handler():
signal.signal(signal.SIGINT, DEFAULT_HANDLER) signal.signal(signal.SIGINT, DEFAULT_HANDLER)
def enable_sigint_handler(): def enable_sigint_handler():
signal.signal(signal.SIGINT, sigint_handler) signal.signal(signal.SIGINT, sigint_handler)
@ -2197,10 +2201,11 @@ def enable_sigint_handler():
# Editor mode functions # Editor mode functions
# --------------------- # ---------------------
def get_system_editor(): def get_system_editor():
'''Returns default system editor is $EDITOR is set''' '''Returns default system editor is $EDITOR is set'''
return os.environ.get('EDITOR', '0') return os.environ.get('EDITOR', 'none')
def to_temp_file_content(url, title_in, tags_in, desc): def to_temp_file_content(url, title_in, tags_in, desc):
@ -2424,8 +2429,10 @@ POSITIONAL ARGUMENTS:
-a: do not set title, -u: clear title -a: do not set title, -u: clear title
-c, --comment [...] description of the bookmark, works with -c, --comment [...] description of the bookmark, works with
-a, -u; clears comment, if no arguments -a, -u; clears comment, if no arguments
-w, --write [editor] open editor to edit a single bookmark -w, --write [editor|index]
works with -a (default), -u open editor to edit a single bookmark
works with -a; if an index is passed to
edit and update, EDITOR must be set
--immutable N disable title fetch from web on update --immutable N disable title fetch from web on update
works with -a, -u works with -a, -u
N=0: mutable (default), N=1: immutable''') N=0: mutable (default), N=1: immutable''')
@ -2595,27 +2602,48 @@ POSITIONAL ARGUMENTS:
bdb = BukuDb(args.json, args.format, not args.tacit, bdb = BukuDb(args.json, args.format, not args.tacit,
colorize=not args.nocolor) colorize=not args.nocolor)
# Editor mode without add and update # Editor mode
if args.write == '0': if args.write is not None:
logerr('EDITOR is not set') if args.write == 'none':
bdb.close_quit(1) logerr('EDITOR is not set')
bdb.close_quit(1)
elif args.write == '0':
logerr('Cannot edit index 0')
bdb.close_quit(1)
if args.write is not None and args.update is None and args.add is None: if is_int(args.write):
# Parse tags into a comma-separated string editor = get_system_editor()
if tags_in: if editor == 'none':
if tags_in[0] == '+': logerr('EDITOR must be set to use index with -w')
tags = '+%s' % parse_tags(tags_in[1:]) bdb.close_quit()
elif tags_in[0] == '-':
tags = '-%s' % parse_tags(tags_in[1:]) idx = int(args.write)
rec = bdb.get_rec_by_id(idx)
if not rec:
logerr('No matching index %d', idx)
bdb.close_quit(1)
result = edit_rec(editor, rec[1], rec[2], rec[3], rec[4])
if result is not None:
url, title, tags, desc = result
bdb.update_rec(idx, url, title, tags, desc)
elif args.add is None:
# Edit and add a new bookmark
# Parse tags into a comma-separated string
if tags_in:
if tags_in[0] == '+':
tags = '+%s' % parse_tags(tags_in[1:])
elif tags_in[0] == '-':
tags = '-%s' % parse_tags(tags_in[1:])
else:
tags = parse_tags(tags_in)
else: else:
tags = parse_tags(tags_in) tags = DELIM
else:
tags = DELIM
result = edit_rec(args.write, '', title_in, tags, desc_in) result = edit_rec(args.write, '', title_in, tags, desc_in)
if result is not None: if result is not None:
url, title_in, tags, desc_in = result url, title_in, tags, desc_in = result
bdb.add_rec(url, title_in, tags, desc_in, args.immutable) bdb.add_rec(url, title_in, tags, desc_in, args.immutable)
# Add record # Add record
if args.add is not None: if args.add is not None:
@ -2638,7 +2666,7 @@ POSITIONAL ARGUMENTS:
url = args.add[0] url = args.add[0]
if args.write: if args.write and not is_int(arg.write):
result = edit_rec(args.write, url, title_in, tags, desc_in) result = edit_rec(args.write, url, title_in, tags, desc_in)
if result is not None: if result is not None:
url, title_in, tags, desc_in = result url, title_in, tags, desc_in = result
@ -2742,48 +2770,31 @@ POSITIONAL ARGUMENTS:
pos -= 1 pos -= 1
else: else:
if args.write: for idx in args.update:
# Allow single bookmark edits only if is_int(idx):
if len(args.update) != 1 or not is_int(args.update[0]): bdb.update_rec(int(idx), url_in, title_in, tags,
print('Cannot edit multiple bookmarks at once') desc_in, args.immutable, args.threads)
bdb.close_quit(1) elif '-' in idx and is_int(idx.split('-')[0]) \
and is_int(idx.split('-')[1]):
lower = int(idx.split('-')[0])
upper = int(idx.split('-')[1])
if lower > upper:
lower, upper = upper, lower
idx = int(args.update[0]) # Update only once if range starts from 0 (all)
rec = bdb.get_rec_by_id(idx) if lower == 0:
if not rec: bdb.update_rec(0, url_in, title_in, tags, desc_in,
logerr('No matching index %d', idx) args.immutable, args.threads)
bdb.close_quit(1) else:
for _id in range(lower, upper + 1):
bdb.update_rec(_id, url_in, title_in, tags,
desc_in, args.immutable,
args.threads)
if interrupted:
break
result = edit_rec(args.write, rec[1], rec[2], rec[3], rec[4]) if interrupted:
if result is not None: break
url, title, tags, desc = result
bdb.update_rec(idx, url, title, tags, desc)
else:
for idx in args.update:
if is_int(idx):
bdb.update_rec(int(idx), url_in, title_in, tags,
desc_in, args.immutable, args.threads)
elif '-' in idx and is_int(idx.split('-')[0]) \
and is_int(idx.split('-')[1]):
lower = int(idx.split('-')[0])
upper = int(idx.split('-')[1])
if lower > upper:
lower, upper = upper, lower
# Update only once if range starts from 0 (all)
if lower == 0:
bdb.update_rec(0, url_in, title_in, tags, desc_in,
args.immutable, args.threads)
else:
for _id in range(lower, upper + 1):
bdb.update_rec(_id, url_in, title_in, tags,
desc_in, args.immutable,
args.threads)
if interrupted:
break
if interrupted:
break
# Delete record # Delete record
if args.delete is not None: if args.delete is not None: