Skip to content

Instantly share code, notes, and snippets.

@amane-katagiri
Last active April 24, 2019 07:22
Show Gist options
  • Save amane-katagiri/495a85ab72188915c12100d0b72c0b42 to your computer and use it in GitHub Desktop.
Save amane-katagiri/495a85ab72188915c12100d0b72c0b42 to your computer and use it in GitHub Desktop.
Extract your jsonlz4 bookmark to json.
#!/bin/bash -Cue
lz4jsoncat $(ls $HOME/.mozilla/firefox/*.default/bookmarkbackups/*.jsonlz4 | tail -n1) > ${HOME}/.bookmarks.json
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from collections import namedtuple, UserDict
import json
import os
from pathlib import Path
import random
import sys
from typing import Callable, List, Optional
__author__ = "Amane Katagiri"
__contact__ = "amane@ama.ne.jp"
__copyright__ = "Copyright (C) 2019 Amane Katagiri"
__credits__ = ["Amane Katagiri", ]
__date__ = "2019-04-04"
__license__ = "MIT License"
__version__ = "0.0.1"
Bookmark = namedtuple("Bookmark", ["title", "url"])
def _find_http(x) -> List[Bookmark]:
result = list()
for a in x if type(x) is list else [x]:
c = a.get("children")
if c:
result.extend(_find_http(c))
elif a.get("uri", "").startswith("http"):
result.append(Bookmark(a.get("title", ""), a.get("uri", "")))
return result
def get_bookmarks_from_json(bookmarks_json: dict) -> List[Bookmark]:
return [x for x in _find_http([bookmarks_json])]
def search_bookmarks(bookmarks: List[Bookmark], query: str) -> List[Bookmark]:
return [x for x in bookmarks if query in x[0] or query in x[1]]
class Interface(object):
def __init__(self):
pass
def read_query(self, queries: List[str]) -> str:
raise NotImplementedError
def read_number(self, bookmarks: List[Bookmark]) -> int:
raise NotImplementedError
def read_confirm(self, bookmark: Bookmark) -> bool:
raise NotImplementedError
def print_items(self, bookmarks: List[Bookmark]) -> None:
raise NotImplementedError
def print_not_found_error(self, queries: List[str]) -> None:
raise NotImplementedError
class CommandLineInterface(Interface):
def __init__(self, read: Callable[[str], str]=input, write: [[str], None]=print,
write_error: [[str], None]=lambda x: sys.stderr.write(x + "\n"),
read_prompt: str="{}> "):
super().__init__()
self.read = read
self.write = write
self.write_error = write_error
self._read_query_prompt = read_prompt.format("query({})")
self._read_number_prompt = read_prompt.format("select number 0-{}")
self._read_confirm_prompt = read_prompt.format("open item 0? (yes/no)")
self._message_not_found = "Not found any bookmark with {}."
def read_query(self, queries: List[str]) -> str:
items = map(lambda x: repr(x), filter(None, queries))
return self.read(self._read_query_prompt.format(" ".join(items)))
def read_number(self, bookmarks: List[Bookmark]) -> int:
return int(self.read(self._read_number_prompt.format(len(bookmarks) - 1)))
def read_confirm(self, bookmark: Bookmark) -> bool:
while True:
buf = self.read(self._read_confirm_prompt)
if buf[:1] in ["y", "Y"]:
return True
elif buf[:1] in ["n", "N"]:
return False
def print_items(self, bookmarks: List[Bookmark]) -> None:
self.write("\n".join(["{}: {} {}".format(k, v.title, v.url)
for k, v in enumerate(bookmarks)]))
def print_not_found_error(self, queries: List[str]) -> None:
items = map(lambda x: repr(x), filter(None, queries))
self.write_error(self._message_not_found.format(", ".join(items)))
class History(UserDict):
def __init__(self, bookmarks: List[Bookmark], init_query: Optional[str]=None,
search: Callable[[List[Bookmark], str], List[Bookmark]]=search_bookmarks):
if init_query:
result = search(bookmarks, init_query)
if not result:
raise KeyError
super().__init__({init_query: result})
else:
super().__init__({"": bookmarks})
self.search = search
def add_item(self, query: str) -> Optional[Bookmark]:
item = self.search(self.get_last_item(), query)
if query in self.data:
return item
elif len(item) == 0:
return None
else:
self.data[query] = item
return item
def get_last_item(self) -> Bookmark:
*_, item = self.data.values()
return item
def pop_last_item(self) -> Bookmark:
*_, key = self.data.keys()
return self.data.pop(key)
def _select_item(bookmarks: List[Bookmark], query: Optional[str],
ui: CommandLineInterface, quick: bool=False) -> Bookmark:
history = History(bookmarks, query)
if quick:
try:
return random.choice(history.get_last_item())
except IndexError:
raise KeyError
while True:
if query:
ui.print_items(history.get_last_item())
if len(history.get_last_item()) == 1:
if query:
return history.get_last_item()[0]
else:
if ui.read_confirm(history.get_last_item()[0]):
return history.get_last_item()[0]
else:
raise KeyboardInterrupt
else:
_query = ui.read_query(history.keys()) if not query else None
if not _query:
try:
return history.get_last_item()[ui.read_number(history.get_last_item())]
except (IndexError, ValueError):
pass
else:
if not history.add_item(_query):
ui.print_not_found_error([*history.keys(), _query])
else:
ui.print_items(history.get_last_item())
def _main() -> int:
import argparse
import readline
parser = argparse.ArgumentParser(description="Search and open bookmarks.")
parser.add_argument("-b", "--bookmark", dest="bookmark_file", action="store")
parser.add_argument("-1", "--quick", dest="quick", action="store_true")
parser.add_argument("--w3m", dest="w3m", action="store", default="/usr/bin/w3m")
parser.add_argument("query", type=str, nargs="?")
args = parser.parse_args()
ui = CommandLineInterface()
if args.bookmark_file and Path(args.bookmark_file).is_file():
with open(args.bookmark_file) as f:
j = json.load(f)
elif (Path.home() / ".bookmarks.json").is_file():
with open(Path.home() / ".bookmarks.json") as f:
j = json.load(f)
else:
ui.write_error("Not found '~/.bookmarks.json' or -b/--bookmark.")
return 1
try:
bookmark = _select_item(get_bookmarks_from_json(j), args.query, ui, args.quick)
except KeyError:
ui.write_error("Not found with {}.".format(repr(args.query)))
return 1
except (EOFError, KeyboardInterrupt):
ui.write_error("Bye.")
return 1
except Exception as ex:
ui.write_error(str(ex))
return 1
os.execl(args.w3m, "w3m", bookmark.url)
if __name__ == "__main__":
sys.exit(_main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment