Support index with -w.
This commit is contained in:
parent
bbf7428818
commit
bd4b85ad32
29
README.md
29
README.md
@ -11,12 +11,12 @@
|
||||
</p>
|
||||
|
||||
<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>
|
||||
|
||||
`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.
|
||||
|
||||
@ -167,8 +167,10 @@ EDIT OPTIONS:
|
||||
-a: do not set title, -u: clear title
|
||||
-c, --comment [...] description of the bookmark, works with
|
||||
-a, -u; clears comment, if no arguments
|
||||
-w, --write [editor] open editor to edit a single bookmark
|
||||
works with -a (default), -u
|
||||
-w, --write [editor|index]
|
||||
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
|
||||
works with -a, -u
|
||||
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.
|
||||
- **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:
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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:
|
||||
|
||||
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):
|
||||
|
||||
$ 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
|
||||
6. **Fetch and update only title** for bookmark at 15012014:
|
||||
7. **Fetch and update only title** for bookmark at 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
|
||||
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:
|
||||
|
||||
$ buku -e bookmarks.html --tag tag 1, tag 2
|
||||
|
35
buku.1
35
buku.1
@ -73,8 +73,9 @@ Bookmarks with immutable titles are listed with bold '(L)' after the URL.
|
||||
.PP
|
||||
.IP 10. 4
|
||||
\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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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
|
||||
.IP 11. 4
|
||||
\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 " [...]"
|
||||
Add comment or description on the bookmark, works with --add, --update. Clears the comment, if no arguments passed.
|
||||
.TP
|
||||
.BI \-w " " \--write " [editor]"
|
||||
Open editor and edit a bookmark before adding or updating it. The edited bookmark is added if either --add or --update is not specified.
|
||||
.BI \-w " " \--write " [editor|index]"
|
||||
Open editor and edit a bookmark before adding or updating it.
|
||||
.TP
|
||||
.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.
|
||||
@ -327,6 +328,17 @@ Note that URL must precede tags.
|
||||
.EE
|
||||
.PP
|
||||
.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:
|
||||
.PP
|
||||
.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
|
||||
.EE
|
||||
.PP
|
||||
.IP 6. 4
|
||||
.IP 7. 4
|
||||
\fBFetch and update only title\fR for bookmark at 15012014:
|
||||
.PP
|
||||
.EX
|
||||
@ -342,7 +354,7 @@ Note that URL must precede tags.
|
||||
.B buku -u 15012014
|
||||
.EE
|
||||
.PP
|
||||
.IP 7. 4
|
||||
.IP 8. 4
|
||||
\fBUpdate only comment\fR for bookmark at 15012014:
|
||||
.PP
|
||||
.EX
|
||||
@ -353,17 +365,6 @@ Note that URL must precede tags.
|
||||
.IP "" 4
|
||||
Applies to --url, --title and --tag too.
|
||||
.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
|
||||
\fBExport\fR bookmarks tagged 'tag 1' or 'tag 2' to HTML and markdown:
|
||||
.PP
|
||||
|
157
buku.py
157
buku.py
@ -67,16 +67,6 @@ logger = logging.getLogger()
|
||||
logdbg = logger.debug
|
||||
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 to parse and fetch the title
|
||||
@ -2174,6 +2164,18 @@ def regexp(expr, item):
|
||||
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):
|
||||
'''Custom SIGINT handler'''
|
||||
|
||||
@ -2185,11 +2187,13 @@ def sigint_handler(signum, frame):
|
||||
# Do a hard exit from here
|
||||
os._exit(1)
|
||||
|
||||
|
||||
DEFAULT_HANDLER = signal.signal(signal.SIGINT, sigint_handler)
|
||||
|
||||
|
||||
def disable_sigint_handler():
|
||||
signal.signal(signal.SIGINT, DEFAULT_HANDLER)
|
||||
|
||||
|
||||
def enable_sigint_handler():
|
||||
signal.signal(signal.SIGINT, sigint_handler)
|
||||
|
||||
@ -2197,10 +2201,11 @@ def enable_sigint_handler():
|
||||
# Editor mode functions
|
||||
# ---------------------
|
||||
|
||||
|
||||
def get_system_editor():
|
||||
'''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):
|
||||
@ -2424,8 +2429,10 @@ POSITIONAL ARGUMENTS:
|
||||
-a: do not set title, -u: clear title
|
||||
-c, --comment [...] description of the bookmark, works with
|
||||
-a, -u; clears comment, if no arguments
|
||||
-w, --write [editor] open editor to edit a single bookmark
|
||||
works with -a (default), -u
|
||||
-w, --write [editor|index]
|
||||
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
|
||||
works with -a, -u
|
||||
N=0: mutable (default), N=1: immutable''')
|
||||
@ -2595,27 +2602,48 @@ POSITIONAL ARGUMENTS:
|
||||
bdb = BukuDb(args.json, args.format, not args.tacit,
|
||||
colorize=not args.nocolor)
|
||||
|
||||
# Editor mode without add and update
|
||||
if args.write == '0':
|
||||
logerr('EDITOR is not set')
|
||||
bdb.close_quit(1)
|
||||
# Editor mode
|
||||
if args.write is not None:
|
||||
if args.write == 'none':
|
||||
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:
|
||||
# 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:])
|
||||
if is_int(args.write):
|
||||
editor = get_system_editor()
|
||||
if editor == 'none':
|
||||
logerr('EDITOR must be set to use index with -w')
|
||||
bdb.close_quit()
|
||||
|
||||
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:
|
||||
tags = parse_tags(tags_in)
|
||||
else:
|
||||
tags = DELIM
|
||||
tags = DELIM
|
||||
|
||||
result = edit_rec(args.write, '', title_in, tags, desc_in)
|
||||
if result is not None:
|
||||
url, title_in, tags, desc_in = result
|
||||
bdb.add_rec(url, title_in, tags, desc_in, args.immutable)
|
||||
result = edit_rec(args.write, '', title_in, tags, desc_in)
|
||||
if result is not None:
|
||||
url, title_in, tags, desc_in = result
|
||||
bdb.add_rec(url, title_in, tags, desc_in, args.immutable)
|
||||
|
||||
# Add record
|
||||
if args.add is not None:
|
||||
@ -2638,7 +2666,7 @@ POSITIONAL ARGUMENTS:
|
||||
|
||||
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)
|
||||
if result is not None:
|
||||
url, title_in, tags, desc_in = result
|
||||
@ -2742,48 +2770,31 @@ POSITIONAL ARGUMENTS:
|
||||
|
||||
pos -= 1
|
||||
else:
|
||||
if args.write:
|
||||
# Allow single bookmark edits only
|
||||
if len(args.update) != 1 or not is_int(args.update[0]):
|
||||
print('Cannot edit multiple bookmarks at once')
|
||||
bdb.close_quit(1)
|
||||
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
|
||||
|
||||
idx = int(args.update[0])
|
||||
rec = bdb.get_rec_by_id(idx)
|
||||
if not rec:
|
||||
logerr('No matching index %d', idx)
|
||||
bdb.close_quit(1)
|
||||
# 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
|
||||
|
||||
result = edit_rec(args.write, 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)
|
||||
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
|
||||
if interrupted:
|
||||
break
|
||||
|
||||
# Delete record
|
||||
if args.delete is not None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user