Add comprehensive Obsidian image rendering with multi-strategy resolution, attachment indexing, and API endpoints for image serving with MIME type detection and rescan functionality

This commit is contained in:
2026-03-23 10:00:48 -04:00
parent 29bdc52cef
commit 175ac3dea8
8 changed files with 1309 additions and 17 deletions
+36 -11
View File
@@ -9,13 +9,15 @@ from typing import Dict, List, Optional, Any
import frontmatter
from backend.attachment_indexer import build_attachment_index
logger = logging.getLogger("obsigate.indexer")
# Global in-memory index
index: Dict[str, Dict[str, Any]] = {}
# Vault config: {name: path}
vault_config: Dict[str, str] = {}
# Vault config: {name: {path, attachmentsPath, scanAttachmentsOnStartup}}
vault_config: Dict[str, Dict[str, Any]] = {}
# Thread-safe lock for index updates
_index_lock = threading.Lock()
@@ -42,24 +44,40 @@ SUPPORTED_EXTENSIONS = {
}
def load_vault_config() -> Dict[str, str]:
"""Read VAULT_N_NAME / VAULT_N_PATH env vars and return {name: path}.
def load_vault_config() -> Dict[str, Dict[str, Any]]:
"""Read VAULT_N_* env vars and return vault configuration.
Scans environment variables ``VAULT_1_NAME``/``VAULT_1_PATH``,
``VAULT_2_NAME``/``VAULT_2_PATH``, etc. in sequential order.
Stops at the first missing pair.
Also reads optional configuration:
- VAULT_N_ATTACHMENTS_PATH: relative path to attachments folder
- VAULT_N_SCAN_ATTACHMENTS: "true"/"false" to enable/disable scanning
Returns:
Dict mapping vault display names to filesystem paths.
Dict mapping vault names to configuration dicts with keys:
- path: filesystem path (required)
- attachmentsPath: relative attachments folder (optional)
- scanAttachmentsOnStartup: boolean (default True)
"""
vaults: Dict[str, str] = {}
vaults: Dict[str, Dict[str, Any]] = {}
n = 1
while True:
name = os.environ.get(f"VAULT_{n}_NAME")
path = os.environ.get(f"VAULT_{n}_PATH")
if not name or not path:
break
vaults[name] = path
# Optional configuration
attachments_path = os.environ.get(f"VAULT_{n}_ATTACHMENTS_PATH")
scan_attachments = os.environ.get(f"VAULT_{n}_SCAN_ATTACHMENTS", "true").lower() == "true"
vaults[name] = {
"path": path,
"attachmentsPath": attachments_path,
"scanAttachmentsOnStartup": scan_attachments,
}
n += 1
return vaults
@@ -251,7 +269,7 @@ def _scan_vault(vault_name: str, vault_path: str) -> Dict[str, Any]:
continue
logger.info(f"Vault '{vault_name}': indexed {len(files)} files, {len(paths)} paths, {len(tag_counts)} unique tags")
return {"files": files, "tags": tag_counts, "path": vault_path, "paths": paths}
return {"files": files, "tags": tag_counts, "path": vault_path, "paths": paths, "config": {}}
async def build_index() -> None:
@@ -272,11 +290,15 @@ async def build_index() -> None:
new_index: Dict[str, Dict[str, Any]] = {}
tasks = []
for name, path in vault_config.items():
tasks.append((name, loop.run_in_executor(None, _scan_vault, name, path)))
for name, config in vault_config.items():
vault_path = config["path"]
tasks.append((name, loop.run_in_executor(None, _scan_vault, name, vault_path)))
for name, task in tasks:
new_index[name] = await task
vault_data = await task
# Store vault config in the index
vault_data["config"] = vault_config[name]
new_index[name] = vault_data
# Build O(1) lookup table for wikilink resolution
new_lookup: Dict[str, List[Dict[str, str]]] = {}
@@ -306,6 +328,9 @@ async def build_index() -> None:
total_files = sum(len(v["files"]) for v in index.values())
logger.info(f"Index built: {len(index)} vaults, {total_files} total files")
# Build attachment index
await build_attachment_index(vault_config)
async def reload_index() -> Dict[str, Any]: