From 61ab69060476d21faf61c28e5e0a266c51d43a4a Mon Sep 17 00:00:00 2001 From: Nose Date: Sun, 11 Jan 2026 01:04:39 -0800 Subject: [PATCH] f --- API/folder.py | 36 +++++++++++++++++++++++------------- SYS/utils.py | 10 +++++++++- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/API/folder.py b/API/folder.py index 1ba46f8..ea27a4b 100644 --- a/API/folder.py +++ b/API/folder.py @@ -208,7 +208,7 @@ class API_folder_store: Args: library_root: Path to the local library root directory """ - self.library_root = expand_path(library_root) + self.library_root = expand_path(library_root).resolve() self.db_path = self.library_root / self.DB_NAME self.connection: Optional[sqlite3.Connection] = None # sqlite3 connections are not safe for concurrent use across threads. @@ -218,14 +218,24 @@ class API_folder_store: self._init_db() def _normalize_input_path(self, file_path: Path) -> Path: - p = expand_path(file_path) - if not p.is_absolute(): - # Check if it already seems to start with library_root but just wasn't absolute - # (e.g. library_root is "C:\foo" and p is "foo\bar" which might happen in some cases) - # though usually it's better to just join. - # But the recursive case happened because library_root was "$home/files" (not absolute) - # and p was "$home/files/..." (not absolute). - p = self.library_root / p + p = expand_path(file_path).resolve() + # If the path is relative to the current working directory, we check if it's meant to be in the library_root. + # However, because we call .resolve() above, it's already absolute relative to CWD if it was relative. + # But we want it to be absolute relative to library_root if it's not absolute or if it exists in library_root. + + # If it's already under library_root, we are done. + try: + p.relative_to(self.library_root) + return p + except ValueError: + pass + + # If it was a relative path (unresolved), we should have joined it before resolving. + # Let's re-expand without resolve to check if it's absolute. + raw_p = expand_path(file_path) + if not raw_p.is_absolute(): + return (self.library_root / raw_p).resolve() + return p def _to_db_file_path(self, file_path: Path) -> str: @@ -2349,8 +2359,8 @@ class DatabaseAPI: """Query API wrapper for LocalLibraryDB providing specialized search methods.""" def __init__(self, search_dir: Path): - self.search_dir = search_dir - self.db = API_folder_store(search_dir) + self.search_dir = expand_path(search_dir).resolve() + self.db = API_folder_store(self.search_dir) def __enter__(self): self.db.__enter__() @@ -2749,8 +2759,8 @@ class LocalLibraryInitializer: def __init__(self, library_root: Path): """Initialize the database scanner.""" - self.library_root = Path(library_root) - self.db = API_folder_store(library_root) + self.library_root = expand_path(library_root).resolve() + self.db = API_folder_store(self.library_root) self.stats = { "files_scanned": 0, "files_new": 0, diff --git a/SYS/utils.py b/SYS/utils.py index 3b22ec9..b651588 100644 --- a/SYS/utils.py +++ b/SYS/utils.py @@ -37,7 +37,15 @@ def expand_path(p: str | Path | None) -> Path: """Expand ~ and environment variables in path.""" if p is None: return None # type: ignore - expanded = os.path.expandvars(str(p)) + s = str(p) + # Courtesy check for $home -> $HOME if we're on a POSIX-like system + # (where env vars are case-sensitive) + if os.name != 'nt' and '$home' in s and '$HOME' not in os.environ: + # If $home is literally used in config but only HOME is defined + if 'HOME' in os.environ: + s = s.replace('$home', '$HOME') + + expanded = os.path.expandvars(s) return Path(expanded).expanduser()