Skip to content

Instantly share code, notes, and snippets.

@shollingsworth
Last active March 30, 2022 15:42
Show Gist options
  • Save shollingsworth/9d5806afcf7531ce8fc6e945619a1c93 to your computer and use it in GitHub Desktop.
Save shollingsworth/9d5806afcf7531ce8fc6e945619a1c93 to your computer and use it in GitHub Desktop.
script to use fzf to quickly open / search for chrome bookmarks
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os
import datetime
import json
from collections import deque
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
import subprocess
import webbrowser
# from pyfzf.pyfzf import FzfPrompt
_BMFILE = Path("~/.config/google-chrome/Default/Bookmarks").expanduser().resolve()
DATA = json.loads(_BMFILE.read_text())
WEBKIT_EPOCH = datetime.datetime(1601, 1, 1)
def which(pgm:str, raise_err: bool = True):
path = os.getenv("PATH", "")
if not path:
raise RuntimeError("$PATH environment variable not set")
for p in path.split(os.path.pathsep):
p = os.path.join(p, pgm)
if os.path.exists(p) and os.access(p, os.X_OK):
return p
if raise_err:
raise RuntimeError(f"{pgm} not found, exiting")
return ''
def fzf(content: str):
fzf = which("fzf")
try:
output = subprocess.check_output([fzf], input=content.encode())
except subprocess.CalledProcessError:
raise SystemExit("fzf aborted...")
val = output.decode("utf-8").strip()
return val
@dataclass
class Bookmark:
"""Bookmark."""
id: Optional[str] = None
date_added: Optional[str] = None
guid: Optional[str] = None
name: Optional[str] = ""
type: Optional[str] = None
url: Optional[str] = ""
children: Optional[list] = None
date_modified: Optional[str] = None
checksum: Optional[str] = None
roots: Optional[dict] = None
sync_metadata: Optional[dict] = None
version: Optional[str] = None
meta_info: Optional[dict] = None
@property
def hash(self) -> str:
"""Search string."""
val = self.name + " " + self.url # type: ignore
return val
# return hashlib.sha256(val.encode("utf-8")).hexdigest()
@property
def date(self):
"""Return date."""
if not self.date_added:
return None
WEBKIT_DELTA = datetime.timedelta(microseconds=int(self.date_added)) # type: ignore
return WEBKIT_EPOCH + WEBKIT_DELTA
def to_dict(self):
"""Return dict."""
return {
"id": self.id,
"date": str(self.date),
"hash": self.hash,
"guid": self.guid,
"name": self.name,
"type": self.type,
"url": self.url,
"meta_info": self.meta_info,
}
CACHE = {}
def _genchildren():
tree = DATA
queue = deque()
queue.append((tree, None))
while queue:
node, parent = queue.pop()
try:
if not isinstance(node, Bookmark):
node = Bookmark(**node)
except:
print("Error item", node)
raise
if node.roots:
for root in node.roots.values():
root = Bookmark(**root)
queue.append((root, node))
elif node.children:
for child in node.children:
child = Bookmark(**child)
queue.append((child, node))
else:
key = parent.name + " " + node.hash
CACHE[key] = node
yield parent, node
def iterbookmarks():
"""Iterate bookmarks."""
for parent, node in sorted(
_genchildren(), key=lambda x: (x[1].date or ""), reverse=True
):
key = parent.name + " " + node.hash
yield key
def main():
"""Run main function."""
try:
select = fzf("\n".join(iterbookmarks()))
print(select)
val = CACHE[select]
webbrowser.open(val.url)
except KeyboardInterrupt:
print("Bye")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment