#!/usr/bin/env python3 """Native messaging host bridging Chrome to KGet's D-Bus service.""" import json import os import re import shutil import struct import subprocess import sys def default_download_dir(): home = os.path.expanduser("~") try: result = subprocess.run( ["xdg-user-dir", "DOWNLOAD"], capture_output=True, text=True, timeout=5 ) downloads = result.stdout.strip() except (FileNotFoundError, subprocess.TimeoutExpired): downloads = "" # xdg-user-dir falls back to bare $HOME (not $HOME/Downloads) when # there's no ~/.config/user-dirs.dirs at all -- treat that as not found. if not downloads or downloads.rstrip("/") == home.rstrip("/"): return os.path.join(home, "Downloads") return downloads def read_message(): raw_length = sys.stdin.buffer.read(4) if len(raw_length) < 4: return None (length,) = struct.unpack("= 100: finished = True break time.sleep(2) if finished: subprocess.run( ["notify-send", "--app-name=KGet Capture", "--icon=kget", "Download complete", dest.rsplit("/", 1)[-1]], timeout=10, ) """ def spawn_notifier(transfer_path, dest): if not transfer_path or not shutil.which("notify-send"): return # Detached (new session, no inherited pipes) so it keeps running after # Chrome closes the native-messaging connection and kills this process -- # sendNativeMessage() tears down its native host as soon as it gets a # reply, which is long before a real download finishes. subprocess.Popen( [sys.executable, "-c", _NOTIFIER_SCRIPT, transfer_path, dest], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, ) def main(): message = read_message() if not message or message.get("action") != "addTransfer" or "url" not in message: write_message({"ok": False, "error": "invalid request"}) return ok, error, transfer_path, dest = add_transfer(message["url"], message.get("filename")) if ok: spawn_notifier(transfer_path, dest) write_message({"ok": True} if ok else {"ok": False, "error": error}) if __name__ == "__main__": main()