Support comment on bookmarks

This commit is contained in:
Arun Prakash Jana 2016-05-18 01:41:31 +05:30
parent cb48798c3d
commit c252981bd2
No known key found for this signature in database
GPG Key ID: C0A712ED95043DCB
3 changed files with 89 additions and 19 deletions

View File

@ -27,6 +27,7 @@ Copyright (C) 2015-2016 [Arun Prakash Jana](mailto:engineerarun@gmail.com).
- Add, tag, search, update, remove bookmarks
- Fetch page title from the web (default) or add a custom page title manually
- Add comments (description) to bookmarks
- Open search results in browser
- Manual password protection using AES256 encryption
- Handle piped input (combine with `xsel` and add bookmarks directly from browser)
@ -122,6 +123,8 @@ Shell completion scripts for Bash, Fish and Zsh can be found in respective subdi
title of bookmark at index N from web
-t, --title [...] manually set title, works with -a, -u
do not set title, if no arguments
-c, --comment [...] description of the bookmark, works with
-a, -u; clears comment, if no arguments
-d, --delete [N] delete bookmark at DB index N
delete all bookmarks, if no arguments
-h, --help show this information
@ -179,9 +182,9 @@ Shell completion scripts for Bash, Fish and Zsh can be found in respective subdi
- Encryption is optional and manual. AES256 algorithm is used. If you choose 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 <i>unencrypted on creation</i>.
# Examples
1. **Add** a bookmark with **tags** `linux news` and `open source`, **fetch page title** from the web:
1. **Add** a bookmark with **tags** `linux news` and `open source`, **comment** `Informative website on Linux and open source`, **fetch page title** from the web:
$ buku -a http://tuxdiary.com linux news, open source
$ buku -a http://tuxdiary.com linux news, open source -c Informative website on Linux and open source
Title: [TuxDiary | Linux, open source and a pinch of leisure.]
Added at index 15012014
2. **Add** a bookmark with tags `linux news` and `open source` & **custom title** `Linux magazine`:

90
buku
View File

@ -50,6 +50,7 @@ except ImportError:
update = False # Update a bookmark in DB
titleData = None # Title fetched from a page
titleManual = None # Manually add a title offline
description = None # Description of the bookmark
jsonOutput = False # Output json formatted result
showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag
debug = False # Enable debug logs
@ -163,12 +164,19 @@ def initdb():
# Create table if it doesn't exist
cur.execute('''CREATE TABLE if not exists bookmarks \
(id integer PRIMARY KEY, URL text NOT NULL UNIQUE, metadata text, tags text)''')
(id integer PRIMARY KEY, URL text NOT NULL UNIQUE, metadata text, tags text, desc text)''')
conn.commit()
except Exception as e:
print("\x1b[1mEXCEPTION\x1b[21m [initdb]: (%s) %s" % (type(e).__name__, e))
sys.exit(1)
# Add description column in existing DB (from version 2.1)
try:
cur.execute("""ALTER TABLE bookmarks ADD COLUMN desc text default \'\'""")
conn.commit()
except:
pass
return (conn, cur)
@ -348,6 +356,7 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0):
"""
global titleManual
global description
tags = ','
meta = ''
url = keywords[0]
@ -390,11 +399,14 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0):
print("Title: [%s]" % meta)
if updateindex == 0: # Add or insert a new entry
if description is None:
description = ''
try:
if insertindex == 0: # insertindex is index number to insert record at
cur.execute('INSERT INTO bookmarks(URL, metadata, tags) VALUES (?, ?, ?)', (url, meta, tags,))
cur.execute('INSERT INTO bookmarks(URL, metadata, tags, desc) VALUES (?, ?, ?, ?)', (url, meta, tags, description))
else:
cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (insertindex, url, meta, tags,))
cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags, desc) VALUES (?, ?, ?, ?, ?)', (insertindex, url, meta, tags, description))
conn.commit()
print("Added at index %d\n" % cur.lastrowid)
printdb(cur, cur.lastrowid)
@ -406,7 +418,10 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0):
print("Index %d exists" % insertindex)
else: # Update an existing entry
try:
cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, updateindex,))
if description is None:
cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, updateindex,))
else:
cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ?, desc = ? WHERE id = ?", (url, meta, tags, description, updateindex,))
conn.commit()
if cur.rowcount == 1:
print("Updated index %d\n" % updateindex)
@ -466,18 +481,20 @@ def searchdb(cur, keywords, all_keywords=False):
global jsonOutput
arguments = []
placeholder = "'%' || ? || '%'"
query = "SELECT id, url, metadata, tags FROM bookmarks WHERE"
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE"
if all_keywords == True: # Match all keywords in URL or Title
for token in keywords:
query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s)) AND" % (placeholder, placeholder, placeholder)
query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s)) AND" % (placeholder, placeholder, placeholder, placeholder)
arguments.append(token)
arguments.append(token)
arguments.append(token)
arguments.append(token)
query = query[:-4]
else: # Match any keyword in URL or Title
for token in keywords:
query += " tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR" % (placeholder, placeholder, placeholder)
query += " tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s) OR desc LIKE (%s) OR" % (placeholder, placeholder, placeholder, placeholder)
arguments.append(token)
arguments.append(token)
arguments.append(token)
arguments.append(token)
@ -495,7 +512,7 @@ def searchdb(cur, keywords, all_keywords=False):
count = 0
for row in results:
count += 1
print("\x1B[1m\x1B[93m%d. \x1B[0m\x1B[92m%s\x1B[0m [%d]\n\t%s\n\t\x1B[91m[TAGS]\x1B[0m %s\n" % (count, row[1], row[0], row[2], row[3][1:-1]))
printRecord(row, count)
if count == 0:
return
@ -537,12 +554,12 @@ def compactDB(conn, cur, index):
results = cur.fetchall()
for row in results:
if row[0] > index:
cur.execute('SELECT id, URL, metadata, tags FROM bookmarks WHERE id = ?', (row[0],))
cur.execute('SELECT id, URL, metadata, tags, desc FROM bookmarks WHERE id = ?', (row[0],))
results = cur.fetchall()
for row in results:
cur.execute('DELETE FROM bookmarks WHERE id = ?', (row[0],))
conn.commit()
cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (index, row[1], row[2], row[3],))
cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags, desc) VALUES (?, ?, ?, ?, ?)', (index, row[1], row[2], row[3], row[4],))
conn.commit()
print("Index %d moved to %d" % (row[0], index))
@ -575,6 +592,35 @@ def cleardb(conn, cur, index):
print("Index out of bound")
def printRecord(row, count=0):
"""Print a single DB record
Handles differently for search and print (count = 0)
"""
# Print index and URL
if count != 0:
print("\x1B[1m\x1B[93m%d. \x1B[0m\x1B[92m%s\x1B[0m\t[%d]" % (count, row[1], row[0]))
else:
print("\x1B[1m\x1B[93m%d. \x1B[0m\x1B[92m%s\x1B[0m" % (row[0], row[1]))
# Print title
if row[2] != '':
print(" %s" % row[2])
print("")
# Print description
if row[4] != '':
print(" %s" % row[4])
#print("\t\x1B[91m[DESC]\x1B[0m %s" % row[4])
# Print tags
if row[3] != ',':
print(" \x1B[91m[TAGS]\x1B[0m %s" % row[3][1:-1])
print("")
def printdb(cur, index, empty=False):
"""Print bookmark details at index or all bookmarks if index is None
Print only bookmarks with blank title or tag if empty is True
@ -599,7 +645,7 @@ def printdb(cur, index, empty=False):
if jsonOutput == False:
if showOpt == 0:
for row in resultset:
print("\x1B[1m\x1B[93m%s. \x1B[0m\x1B[92m%s\x1B[0m\n\t%s\n\t\x1B[91m[TAGS]\x1B[0m %s\n" % (row[0], row[1], row[2], row[3][1:-1]))
printRecord(row)
elif showOpt == 1:
for row in resultset:
print("%s %s" % (row[0], row[1]))
@ -618,7 +664,7 @@ def printdb(cur, index, empty=False):
if jsonOutput == False:
for row in resultset:
print("\x1B[1m\x1B[93m%s. \x1B[0m\x1B[92m%s\x1B[0m\n\t%s\n\t\x1B[91m[TAGS]\x1B[0m %s" % (row[0], row[1], row[2], row[3][1:-1]))
printRecord(row)
return
print("No matching index")
else:
@ -982,6 +1028,19 @@ class customTitleAction(argparse.Action):
setattr(args, self.dest, values)
class customDescAction(argparse.Action):
"""Class to capture if an optional param
is actually used, even if sans arguments
"""
def __call__(self, parser, args, values, option_string=None):
global description
description = ''
# NOTE: the following converts a None argument to an empty array []
setattr(args, self.dest, values)
class ExtendedArgumentParser(argparse.ArgumentParser):
"""Extend classic argument parser"""
@ -1029,7 +1088,7 @@ argparser = ExtendedArgumentParser(
description='A private command-line bookmark manager. Your mini web!',
formatter_class=argparse.RawTextHelpFormatter,
usage='''buku [-a URL [tags ...]] [-u [N [URL tags ...]]]
[-t [...]] [-d [N]] [-h]
[-t [...]] [-c [...]] [-d [N]] [-h]
[-s keyword [...]] [-S keyword [...]]
[-k [N]] [-l [N]] [-p [N]] [-f N]
[-r oldtag [newtag ...]] [-j] [-o N] [-z]''',
@ -1047,12 +1106,15 @@ general_group = argparser.add_argument_group(title="general options",
title of bookmark at index N from web
-t, --title [...] manually set title, works with -a, -u
do not set title, if no arguments
-c, --comment [...] description of the bookmark, works with
-a, -u; clears comment, if no arguments
-d, --delete [N] delete bookmark at DB index N
delete all bookmarks, if no arguments
-h, --help show this information''')
general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help=argparse.SUPPRESS)
general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customUpdateAction, metavar=('N', 'URL tags'), help=argparse.SUPPRESS)
general_group.add_argument('-t', '--title', nargs='*', dest='title', action=customTitleAction, metavar='title', help=argparse.SUPPRESS)
general_group.add_argument('-c', '--comment', nargs='*', dest='desc', type=str, action=customDescAction, metavar='desc', help=argparse.SUPPRESS)
general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help=argparse.SUPPRESS)
general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS)
@ -1121,6 +1183,8 @@ if args.showOpt is not None:
showOpt = args.showOpt
if titleManual is not None and len(args.title) > 0:
titleManual = " ".join(args.title)
if description is not None and len(args.desc) > 0:
description = " ".join(args.desc)
if args.jsonOutput:
import json
jsonOutput = args.jsonOutput

11
buku.1
View File

@ -5,7 +5,7 @@ buku \- A private command-line bookmark manager. Your mini web!
.B buku
[-a URL [tags ...]] [-u [N [URL tags ...]]]
.br
[-t [...]] [-d [N]] [-h]
[-t [...]] [-c [...]] [-d [N]] [-h]
.br
[-s keyword [...]] [-S keyword [...]]
.br
@ -60,7 +60,10 @@ is refreshed from the web. If
is omitted, all titles are refreshed from the web.
.TP
.BI \-t " " \--title " [...]"
Manually specify the title, works with -a, -i, -u. `-m none` clears title.
Manually specify the title, works with -a, -u. Omits or clears the title, if no arguments passed.
.TP
.BI \-c " " \--comment " [...]"
Add comment or description on the bookmark, works with -a, -u. Clears the comment, if no arguments passed.
.TP
.BI \-d " " \--delete " [N]"
Delete bookmark at index
@ -151,11 +154,11 @@ Overrides the default browser. Ref:
.SH EXAMPLES
.PP
.IP 1. 4
\fBAdd\fR a bookmark with \fBtags\fR 'linux news' and 'open source', \fBfetch page title\fR from the web:
\fBAdd\fR a bookmark with \fBtags\fR 'linux news' and 'open source', \fBcomment\fR 'Informative website on Linux and open source', \fBfetch page title\fR from the web:
.PP
.EX
.IP
.B buku -a http://tuxdiary.com linux news, open source
.B buku -a http://tuxdiary.com linux news, open source -c Informative website on Linux and open source
.PP
.IP 2. 4
\fBAdd\fR a bookmark with tags 'linux news' and 'open source' & \fBcustom title\fR 'Linux magazine':