Add Chrome extension and Windows native host
This commit is contained in:
1 parent
6f2b238988
commit
f889c6129d
8 files changed
+201
No files matched your search
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
py -3 "%~dp0history_archive_host.py"
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Chrome Native Messaging host: append browser visits to local SQLite."""
|
||||
import json, os, sqlite3, struct, sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ARCHIVE_DIR = Path(os.environ.get("LOCAL_HISTORY_ARCHIVE_DIR", Path.home() / "ChromeHistoryArchive"))
|
||||
DB_PATH = ARCHIVE_DIR / "history.sqlite3"
|
||||
|
||||
def read_message():
|
||||
raw = sys.stdin.buffer.read(4)
|
||||
if not raw: return None
|
||||
length = struct.unpack("=I", raw)[0]
|
||||
if length > 20_000_000: raise ValueError("Message too large")
|
||||
return json.loads(sys.stdin.buffer.read(length).decode("utf-8"))
|
||||
|
||||
def send_message(value):
|
||||
body = json.dumps(value, ensure_ascii=False).encode("utf-8")
|
||||
sys.stdout.buffer.write(struct.pack("=I", len(body)))
|
||||
sys.stdout.buffer.write(body)
|
||||
sys.stdout.buffer.flush()
|
||||
|
||||
def db():
|
||||
ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn.execute("""CREATE TABLE IF NOT EXISTS visits (
|
||||
id INTEGER PRIMARY KEY, url TEXT NOT NULL, title TEXT NOT NULL DEFAULT '',
|
||||
visit_time TEXT NOT NULL, transition TEXT, source TEXT NOT NULL,
|
||||
archived_at TEXT NOT NULL, UNIQUE(url, visit_time, source))""")
|
||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_visits_time ON visits(visit_time)")
|
||||
return conn
|
||||
|
||||
def handle(message):
|
||||
if message.get("type") != "visits" or not isinstance(message.get("visits"), list):
|
||||
return {"ok": False, "error": "Expected a visits message"}
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
rows = []
|
||||
for visit in message["visits"]:
|
||||
url, when = visit.get("url"), visit.get("visit_time")
|
||||
if isinstance(url, str) and url and isinstance(when, str) and when:
|
||||
rows.append((url, str(visit.get("title") or ""), when,
|
||||
str(visit.get("transition") or "observed"),
|
||||
str(visit.get("source") or "chrome-extension"), now))
|
||||
with db() as conn:
|
||||
conn.executemany("INSERT OR IGNORE INTO visits (url,title,visit_time,transition,source,archived_at) VALUES (?,?,?,?,?,?)", rows)
|
||||
return {"ok": True, "archived": len(rows), "database": str(DB_PATH)}
|
||||
|
||||
try:
|
||||
while message := read_message():
|
||||
try: send_message(handle(message))
|
||||
except Exception as exc: send_message({"ok": False, "error": str(exc)})
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "si.komelt.local_history_archive",
|
||||
"description": "Local Chrome History Archive native host",
|
||||
"path": "C:\\LOCAL_HISTORY_ARCHIVE\\history_archive_host.bat",
|
||||
"type": "stdio",
|
||||
"allowed_origins": ["chrome-extension://YOUR_EXTENSION_ID/"]
|
||||
}
|
||||
Reference in new issue
Block a user