"""Desregistra un vault de la app de escritorio Obsidian. Quita la entrada del vault de la clave "vaults" del archivo de configuracion de la app (~/.config/obsidian/obsidian.json). NO borra la carpeta del vault en disco: solo hace que la app Obsidian deje de conocerlo. Preserva el resto del JSON intacto. Funcion impura: lee y escribe la config de la app y hace backup .bak antes de sobreescribir. """ import json import os import shutil def _default_config_path() -> str: """Ruta por defecto del obsidian.json de la app de escritorio Obsidian.""" return os.path.expanduser("~/.config/obsidian/obsidian.json") def _load_config(config_path: str) -> dict: """Carga obsidian.json. Si no existe devuelve la estructura vacia base.""" if not os.path.exists(config_path): return {"vaults": {}} with open(config_path, "r", encoding="utf-8") as f: data = json.load(f) if not isinstance(data, dict): raise ValueError(f"obsidian config is not a JSON object: {config_path}") if "vaults" not in data or not isinstance(data.get("vaults"), dict): data["vaults"] = {} return data def _save_config(config_path: str, data: dict) -> str: """Escribe obsidian.json haciendo backup .bak previo si ya existia. Devuelve la ruta del backup creado (cadena vacia si no habia archivo previo). """ parent = os.path.dirname(config_path) if parent: os.makedirs(parent, exist_ok=True) backup_path = "" if os.path.exists(config_path): backup_path = config_path + ".bak" shutil.copy2(config_path, backup_path) with open(config_path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) return backup_path def unregister_obsidian_vault(vault_ref: str, config_path: str = "") -> dict: """Quita un vault de la lista de vaults conocidos por la app Obsidian. Args: vault_ref: id exacto de la entrada (hex de 16 chars) O una ruta al vault. Si parece una ruta se normaliza a absoluta y se compara con "path". Primero se intenta match por id; si no hay, por path. config_path: ruta al obsidian.json de la app. Vacio -> default ~/.config/obsidian/obsidian.json. Returns: dict con: - removed: True si se quito una entrada, False si no se encontro. - id: id de la entrada quitada ("" si no se encontro). - path: path de la entrada quitada ("" si no se encontro). - config_path: ruta del obsidian.json usado. - backup_path: ruta del backup .bak escrito ("" si no se escribio nada). Raises: ValueError: si el obsidian.json existente no es un objeto JSON valido. OSError: si la lectura/escritura del archivo falla por I/O. """ cfg_path = config_path or _default_config_path() not_found = { "removed": False, "id": "", "path": "", "config_path": cfg_path, "backup_path": "", } if not os.path.exists(cfg_path): return not_found data = _load_config(cfg_path) vaults = data["vaults"] target_id = None # 1) Match directo por id. if vault_ref in vaults: target_id = vault_ref else: # 2) Match por path normalizado. abs_ref = os.path.abspath(os.path.expanduser(vault_ref)) for vid, entry in vaults.items(): if isinstance(entry, dict) and entry.get("path") == abs_ref: target_id = vid break if target_id is None: return not_found removed_path = "" entry = vaults.get(target_id) if isinstance(entry, dict): removed_path = entry.get("path", "") del vaults[target_id] backup_path = _save_config(cfg_path, data) return { "removed": True, "id": target_id, "path": removed_path, "config_path": cfg_path, "backup_path": backup_path, } if __name__ == "__main__": import tempfile import time tmp = tempfile.mkdtemp() cfg = os.path.join(tmp, "obsidian.json") payload = { "extra": "preservar", "vaults": { "aaaaaaaaaaaaaaaa": {"path": "/a/Alpha", "ts": int(time.time() * 1000), "open": True}, "bbbbbbbbbbbbbbbb": {"path": "/b/Beta", "ts": int(time.time() * 1000), "open": False}, }, } with open(cfg, "w", encoding="utf-8") as f: json.dump(payload, f) r = unregister_obsidian_vault("/a/Alpha", config_path=cfg) assert r["removed"] is True and r["id"] == "aaaaaaaaaaaaaaaa", r r2 = unregister_obsidian_vault("bbbbbbbbbbbbbbbb", config_path=cfg) assert r2["removed"] is True and r2["path"] == "/b/Beta", r2 with open(cfg, "r", encoding="utf-8") as f: final = json.load(f) assert final["extra"] == "preservar", final assert final["vaults"] == {}, final assert unregister_obsidian_vault("nope", config_path=cfg)["removed"] is False print("unregister_obsidian_vault smoke OK")