--- name: resolve_obsidian_embed kind: function lang: py domain: obsidian version: "1.0.0" purity: impure signature: "def resolve_obsidian_embed(vault_dir: str, embed_name: str) -> str" description: "Resuelve el path absoluto real de un attachment embebido buscandolo por nombre dentro de un vault de Obsidian. Obsidian resuelve los embeds ![[...]] por nombre de archivo unico (no por path), asi que esta funcion recorre el vault recursivamente (una pasada con os.walk) y devuelve el primer archivo cuyo basename coincida (case-insensitive), excluyendo .obsidian/ y .trash/. Si el embed no trae extension, acepta cualquier match de ese basename. Devuelve cadena vacia si no existe (NO lanza). Lanza si vault_dir no existe. No depende de la app GUI." tags: [obsidian, embed, attachment, resolve, filesystem, migrate] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: ["os"] params: - name: vault_dir desc: "ruta a la raiz del vault Obsidian donde buscar el attachment" - name: embed_name desc: "nombre del attachment embebido (el target de un ![[...]], lo que devuelve extract_obsidian_embeds), con o sin extension" output: "string con el path absoluto del primer archivo del vault cuyo basename coincide (case-insensitive) con embed_name, o cadena vacia '' si ningun archivo coincide. Excluye .obsidian/ y .trash/." tested: true tests: - "match exacto en subcarpeta" - "match case insensitive" - "sin extension acepta cualquier match" - "no existe devuelve vacio" - "excluye obsidian y trash" - "vault inexistente lanza" test_file_path: "python/functions/obsidian/resolve_obsidian_embed_test.py" file_path: "python/functions/obsidian/resolve_obsidian_embed.py" --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from obsidian import extract_obsidian_embeds, resolve_obsidian_embed body = "Mi DNI: ![[dni enmanuel (2).jpg]]" for embed in extract_obsidian_embeds(body): path = resolve_obsidian_embed("/home/me/MiVault", embed) print(embed, "->", path or "(no encontrado)") # dni enmanuel (2).jpg -> /home/me/MiVault/attachments/dni enmanuel (2).jpg ``` ## Cuando usarla Usala junto a `extract_obsidian_embeds` cuando migres o extraigas un subgrafo de notas y necesites el path fisico real de cada attachment para copiarlo al nuevo destino. Replica la resolucion por-nombre de Obsidian sin abrir la app GUI: basta con el directorio del vault en disco. ## Gotchas - **Resuelve por NOMBRE de archivo, no por path.** Igual que Obsidian: busca el basename en TODO el vault. Si hay dos archivos con el mismo nombre en carpetas distintas (`fotos/logo.png` y `assets/logo.png`), devuelve el PRIMERO que encuentra `os.walk` — el orden de `os.walk` no esta garantizado entre sistemas, asi que con nombres duplicados el resultado puede no ser el embed que Obsidian mostraria. Para vaults con nombres ambiguos, deduplica los nombres antes de migrar. - Si el embed no existe en el vault devuelve `""` (cadena vacia), NO lanza excepcion. Comprueba el valor antes de usarlo como ruta. - SI lanza `FileNotFoundError` / `NotADirectoryError` si `vault_dir` no existe o no es un directorio. - Es impura: recorre el filesystem en cada llamada. Si vas a resolver muchos embeds del mismo vault, considera construir tu propio indice `basename -> path` con un unico `os.walk` en lugar de llamar a esta funcion en bucle.