diff --git a/bin/command-list b/bin/command-list index 215f33a..6fa59db 100755 --- a/bin/command-list +++ b/bin/command-list @@ -1,5 +1,6 @@ #!/usr/bin/python3 -import os, subprocess, json +import os, subprocess, json, sqlite3, shutil, tempfile +from pathlib import Path def readfile(path): fd = open(path, "r") @@ -7,6 +8,9 @@ def readfile(path): fd.close() return result +# +# GTK apps +# vals = {} home = os.environ["HOME"] appsdir=["/usr/share/applications", f"{home}/.local/share/applications"] @@ -50,7 +54,9 @@ for appdir in appsdir: vals[_name] = {"kind": "app", "exec": _exec, "path": path, "name": appfile[:-8]} - +# +# Bin +# bindir = f"{home}/bin" for file in os.listdir(bindir): path = f"{bindir}/{file}" @@ -60,6 +66,9 @@ for file in os.listdir(bindir): continue vals[file] = {"kind": "bin", "path": path} +# +# Chromium bookmarks +# def walk_bookmarks(node, folder=""): if isinstance(node, dict): if node.get("type") == "url" and node.get("url"): @@ -81,6 +90,74 @@ if os.path.isfile(bookmarkfile): for root in roots.values(): walk_bookmarks(root) +# +# Firefox bookmarks +# +SEARCH_DIRS = [ + Path.home() / ".config" / "librewolf", +] + +def find_places_databases(): + seen = set() + for base in SEARCH_DIRS: + if not base.exists(): + continue + for db in base.glob("**/places.sqlite"): + resolved = db.resolve() + if resolved not in seen: + seen.add(resolved) + yield db + +def read_firefox_bookmarks(db_path): + # Copy the DB first so this also works while Firefox/LibreWolf is running. + with tempfile.TemporaryDirectory() as tmp: + tmp_db = Path(tmp) / "places.sqlite" + shutil.copy2(db_path, tmp_db) + + for suffix in ("-wal", "-shm"): + sidecar = Path(str(db_path) + suffix) + if sidecar.exists(): + shutil.copy2(sidecar, Path(str(tmp_db) + suffix)) + + con = sqlite3.connect(f"file:{tmp_db}?mode=ro", uri=True) + cur = con.cursor() + query = """ + WITH RECURSIVE folders(id, path) AS ( + SELECT id, COALESCE(title, '') + FROM moz_bookmarks + WHERE parent = 0 + + UNION ALL + + SELECT b.id, + CASE + WHEN folders.path = '' THEN COALESCE(b.title, '') + WHEN COALESCE(b.title, '') = '' THEN folders.path + ELSE folders.path || '/' || b.title + END + FROM moz_bookmarks b + JOIN folders ON b.parent = folders.id + WHERE b.type = 2 + ) + SELECT COALESCE(folders.path, ''), COALESCE(b.title, ''), p.url + FROM moz_bookmarks b + JOIN moz_places p ON b.fk = p.id + LEFT JOIN folders ON b.parent = folders.id + WHERE b.type = 1 + AND p.url IS NOT NULL + ORDER BY b.dateAdded + """ + + for folder, title, url in cur.execute(query): + name = title or url + label = f"{folder}/{name}" if folder else name + vals[label] = {"kind": "url", "url": url} + + con.close() + +for it in find_places_databases(): + read_firefox_bookmarks(it) + entries = [] for it in vals.items(): entries.append(it[0])