Files
dotfiles/bin/command-list

183 lines
5.3 KiB
Python
Executable File

#!/usr/bin/python3
import os, subprocess, json, sqlite3, shutil, tempfile
from pathlib import Path
def readfile(path):
fd = open(path, "r")
result = fd.read()
fd.close()
return result
#
# GTK apps
#
vals = {}
home = os.environ["HOME"]
appsdir=["/usr/share/applications", f"{home}/.local/share/applications"]
for appdir in appsdir:
if not os.path.isdir(appdir):
continue
for appfile in os.listdir(appdir):
if not appfile.endswith(".desktop"):
continue
path = appdir + "/" + appfile
content = readfile(path)
_name = None
_exec = None
_type = None
_nodisplay = ""
_hidden = ""
for line in content.splitlines(content):
line = line.strip()
if line.startswith("Exec="):
_exec = line[5:]
elif line.startswith("Type="):
_type = line[5:]
elif line.startswith("Name=") and _name is None:
_name = line[5:]
elif line.startswith("NoDisplay="):
_nodisplay = line[10:]
elif line.startswith("Hidden="):
_hidden = line[7:]
if _name is None or _exec is None:
continue
if _type is None or _type != "Application":
continue
if _nodisplay == "true" or _hidden == "true":
continue
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}"
if os.path.isdir(path):
continue
if file.startswith("."):
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"):
name = node.get("name") or node["url"]
label = f"{folder}/{name}" if folder else name
vals[label] = {"kind": "url", "url": node["url"]}
elif "children" in node:
name = node.get("name", "")
next_folder = f"{folder}/{name}" if folder and name else name or folder
for child in node.get("children", []):
walk_bookmarks(child, next_folder)
bookmarkfile = f"{home}/.config/chromium/Default/Bookmarks"
if os.path.isfile(bookmarkfile):
content = readfile(bookmarkfile)
data = json.loads(content)
roots = data.get("roots", {})
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])
entries_string = "\n".join(entries)
result = subprocess.run(["wmenu", "-l", "20", "-i"], input=entries_string, text=True, stdout=subprocess.PIPE)
if result.returncode != 0:
exit(result.returncode)
choice = result.stdout.strip()
if choice[0] == '@':
choice = choice[1:]
val = vals.get(choice)
if val is not None:
if val["kind"] == "bin":
subprocess.run([val["path"]])
elif val["kind"] == "app":
subprocess.run(["gtk-launch", val["name"]])
elif val["kind"] == "url":
subprocess.run(["xdg-open", val["url"]])
else:
subprocess.run(["xdg-open", "https://www.google.com/search?q=" + choice])