feat: add hidden files configuration with per-vault settings for includeHidden and hiddenWhitelist, supporting environment variables and UI controls for selective indexing of dot-prefixed files and folders

This commit is contained in:
2026-03-25 09:54:34 -04:00
parent 8c30b0d238
commit 9e42fb072b
8 changed files with 790 additions and 13 deletions
+15 -7
View File
@@ -1,8 +1,10 @@
import asyncio
import logging
import threading
from pathlib import Path
from typing import Dict, List, Optional, Set
import threading
from backend.indexer import _should_include_path
logger = logging.getLogger("obsigate.attachment_indexer")
@@ -34,7 +36,7 @@ def clear_resolution_cache(vault_name: Optional[str] = None) -> None:
del _resolution_cache[key]
def _scan_vault_attachments(vault_name: str, vault_path: str) -> Dict[str, List[Path]]:
def _scan_vault_attachments(vault_name: str, vault_path: str, vault_cfg: dict = None) -> Dict[str, List[Path]]:
"""Synchronously scan a vault directory for image attachments.
Walks the vault tree and builds a filename -> absolute path mapping
@@ -43,6 +45,7 @@ def _scan_vault_attachments(vault_name: str, vault_path: str) -> Dict[str, List[
Args:
vault_name: Display name of the vault.
vault_path: Absolute filesystem path to the vault root.
vault_cfg: Optional vault configuration dict with hidden files settings.
Returns:
Dict mapping lowercase filenames to lists of absolute paths.
@@ -50,6 +53,10 @@ def _scan_vault_attachments(vault_name: str, vault_path: str) -> Dict[str, List[
vault_root = Path(vault_path)
index: Dict[str, List[Path]] = {}
# Default config if not provided
if vault_cfg is None:
vault_cfg = {"includeHidden": False, "hiddenWhitelist": []}
if not vault_root.exists():
logger.warning(f"Vault path does not exist for attachment scan: {vault_path}")
return index
@@ -58,9 +65,9 @@ def _scan_vault_attachments(vault_name: str, vault_path: str) -> Dict[str, List[
try:
for fpath in vault_root.rglob("*"):
# Skip hidden files and directories
# Check if path should be included based on hidden files configuration
rel_parts = fpath.relative_to(vault_root).parts
if any(part.startswith(".") for part in rel_parts):
if not _should_include_path(rel_parts, vault_cfg):
continue
# Only process files
@@ -121,7 +128,7 @@ async def build_attachment_index(vault_config: Dict[str, Dict[str, any]]) -> Non
new_index[name] = {}
continue
tasks.append((name, loop.run_in_executor(None, _scan_vault_attachments, name, vault_path)))
tasks.append((name, loop.run_in_executor(None, _scan_vault_attachments, name, vault_path, config)))
for name, task in tasks:
new_index[name] = await task
@@ -136,18 +143,19 @@ async def build_attachment_index(vault_config: Dict[str, Dict[str, any]]) -> Non
logger.info(f"Attachment index built: {len(attachment_index)} vaults, {total_attachments} total attachments")
async def rescan_vault_attachments(vault_name: str, vault_path: str) -> int:
async def rescan_vault_attachments(vault_name: str, vault_path: str, vault_cfg: dict = None) -> int:
"""Rescan attachments for a single vault.
Args:
vault_name: Name of the vault to rescan.
vault_path: Absolute path to the vault root.
vault_cfg: Optional vault configuration dict with hidden files settings.
Returns:
Number of attachments indexed.
"""
loop = asyncio.get_event_loop()
new_vault_index = await loop.run_in_executor(None, _scan_vault_attachments, vault_name, vault_path)
new_vault_index = await loop.run_in_executor(None, _scan_vault_attachments, vault_name, vault_path, vault_cfg)
with _attachment_lock:
attachment_index[vault_name] = new_vault_index