mirror of
https://git.srhtnin.garden/sarah/SaraFem.git
synced 2026-10-13 18:18:11 +02:00
84 lines
2.9 KiB
Python
84 lines
2.9 KiB
Python
"""Read-only SaraTag Book-sidecar support, mirroring SaraView's Backend/BookReader.py
|
|
so all three Sara* apps talk to SaraTag the same way (dynamically loaded, opt-in via
|
|
Config.toml's [SaraTag] section, since SaraTag isn't an installable package)."""
|
|
|
|
import fnmatch
|
|
import importlib.util
|
|
import tomllib
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
from typing import Any, Optional
|
|
|
|
from Core import AppConfig
|
|
|
|
_CachedSaraTagCore: Optional[ModuleType] = None
|
|
_CachedSaraTagCoreLoaded = False
|
|
|
|
|
|
def ClearSaraTagCache() -> None:
|
|
"""Called after Options is saved so an Enabled/CoreModulePath change takes
|
|
effect on the next Book read, instead of needing a restart."""
|
|
global _CachedSaraTagCore, _CachedSaraTagCoreLoaded
|
|
_CachedSaraTagCore = None
|
|
_CachedSaraTagCoreLoaded = False
|
|
|
|
|
|
def GetSaraTagCore(Config: AppConfig) -> Optional[ModuleType]:
|
|
global _CachedSaraTagCore, _CachedSaraTagCoreLoaded
|
|
|
|
if _CachedSaraTagCoreLoaded:
|
|
return _CachedSaraTagCore
|
|
|
|
_CachedSaraTagCoreLoaded = True
|
|
|
|
if not Config.SaraTag.Enabled or not Config.SaraTag.CoreModulePath:
|
|
return None
|
|
|
|
CoreFilePath = Path(Config.SaraTag.CoreModulePath).expanduser().resolve() / "Core.py"
|
|
if not CoreFilePath.is_file():
|
|
return None
|
|
|
|
ModuleSpec = importlib.util.spec_from_file_location("SaraTagCore", CoreFilePath)
|
|
if ModuleSpec is None or ModuleSpec.loader is None:
|
|
return None
|
|
|
|
SaraTagCoreModule = importlib.util.module_from_spec(ModuleSpec)
|
|
ModuleSpec.loader.exec_module(SaraTagCoreModule)
|
|
|
|
_CachedSaraTagCore = SaraTagCoreModule
|
|
return _CachedSaraTagCore
|
|
|
|
|
|
def IsInternalSaraTagFile(EntryPath: Path, Config: AppConfig) -> bool:
|
|
"""Whether this entry is SaraTag plumbing (a Book sidecar, its data dir, ...)
|
|
that should stay out of the file listing, matching SaraView's HideInternalFiles."""
|
|
if not Config.SaraTag.Enabled or not Config.SaraTag.HideInternalFiles:
|
|
return False
|
|
return any(fnmatch.fnmatch(EntryPath.name, Pattern) for Pattern in Config.SaraTag.InternalPatterns)
|
|
|
|
|
|
def ReadBookForFile(FilePath: Path, Config: AppConfig) -> Optional[dict[str, Any]]:
|
|
"""Return {"Parent": ..., "Tags": {...}, "Definitions": {...}} or None."""
|
|
SaraTagCore = GetSaraTagCore(Config)
|
|
if SaraTagCore is None:
|
|
return None
|
|
|
|
BookPath = SaraTagCore.GetBookPath(FilePath)
|
|
if not BookPath.is_file():
|
|
return None
|
|
|
|
with BookPath.open("rb") as BookFile:
|
|
Metadata = tomllib.load(BookFile)
|
|
|
|
ParentValue = Metadata.get("Parent")
|
|
Definitions: dict[str, Any] = {}
|
|
if isinstance(ParentValue, str):
|
|
try:
|
|
LexiconPath = SaraTagCore.ResolveBookLexiconPath(FilePath.parent, ParentValue)
|
|
Definitions = SaraTagCore.ResolveLexiconTags(LexiconPath)
|
|
except Exception:
|
|
Definitions = {}
|
|
|
|
Tags = {Key: Value for Key, Value in Metadata.items() if Key != "Parent"}
|
|
return {"Parent": ParentValue, "Tags": Tags, "Definitions": Definitions}
|