fix(paths): fixed symlinks not being followed

This commit is contained in:
2026-01-13 13:43:33 +01:00
parent 41d4e6d7f0
commit f738bcb1a4
2 changed files with 119 additions and 60 deletions
+66 -37
View File
@@ -112,24 +112,44 @@ def scan(root: Path, progress_cb=None) -> List[Item]:
prev = _load_warm_index_map()
# Track visited directories to prevent infinite loops from circular symlinks
visited_inodes = set()
# Collect directories first (skip root itself)
for dirpath, dirnames, filenames in os.walk(root):
for dirpath, dirnames, filenames in os.walk(root, followlinks=True):
dpath = Path(dirpath)
# Prevent infinite loops from circular symlinks
try:
stat_info = dpath.stat()
inode = (stat_info.st_dev, stat_info.st_ino)
if inode in visited_inodes:
dirnames.clear() # Don't descend into this directory
continue
visited_inodes.add(inode)
except (OSError, PermissionError):
# Skip inaccessible directories
dirnames.clear()
continue
if dpath == root:
# Don't add root as an item
pass
else:
rel = _relpath(root, dpath)
st = dpath.stat()
items.append(Item(
path=dpath,
rel=rel,
name=dpath.name,
is_dir=True,
size=0,
mtime=st.st_mtime,
meta=None
))
try:
rel = _relpath(root, dpath)
items.append(Item(
path=dpath,
rel=rel,
name=dpath.name,
is_dir=True,
size=0,
mtime=stat_info.st_mtime,
meta=None
))
except Exception:
# Skip if we can't process this directory
continue
# Files in this folder
for fn in filenames:
@@ -137,32 +157,41 @@ def scan(root: Path, progress_cb=None) -> List[Item]:
ext = p.suffix.lower()
if ext not in VALID_EXTS:
continue
rel = _relpath(root, p)
st = p.stat()
key = rel
meta = None
prev_rec = prev.get(key)
if prev_rec and prev_rec.get("size") == st.st_size and int(prev_rec.get("mtime", 0)) == int(st.st_mtime):
# unchanged — reuse cached meta
meta = prev_rec.get("meta") or {}
else:
meta = _read_comicinfo_from_cbz(p)
it = Item(
path=p,
rel=rel,
name=p.stem,
is_dir=False,
size=st.st_size,
mtime=st.st_mtime,
meta=meta or {}
)
items.append(it)
if progress_cb:
try:
progress_cb({"rel": it.rel, "size": it.size, "mtime": it.mtime})
except Exception:
pass
try:
# Check if file exists and is accessible (handles broken symlinks)
if not p.exists():
continue
rel = _relpath(root, p)
st = p.stat()
key = rel
meta = None
prev_rec = prev.get(key)
if prev_rec and prev_rec.get("size") == st.st_size and int(prev_rec.get("mtime", 0)) == int(st.st_mtime):
# unchanged — reuse cached meta
meta = prev_rec.get("meta") or {}
else:
meta = _read_comicinfo_from_cbz(p)
it = Item(
path=p,
rel=rel,
name=p.stem,
is_dir=False,
size=st.st_size,
mtime=st.st_mtime,
meta=meta or {}
)
items.append(it)
if progress_cb:
try:
progress_cb({"rel": it.rel, "size": it.size, "mtime": it.mtime})
except Exception:
pass
except (OSError, PermissionError):
# Skip inaccessible files
continue
# Save warm index
_save_warm_index(items)
+53 -23
View File
@@ -174,38 +174,68 @@ def _run_scan():
total = _count_cbz(LIBRARY_DIR)
_set_status(total=total, phase="indexing")
for dirpath, dirnames, filenames in os.walk(LIBRARY_DIR):
# Track visited directories to prevent infinite loops from circular symlinks
visited_inodes = set()
for dirpath, dirnames, filenames in os.walk(LIBRARY_DIR, followlinks=True):
dpath = Path(dirpath)
# Prevent infinite loops from circular symlinks
try:
stat_info = dpath.stat()
inode = (stat_info.st_dev, stat_info.st_ino)
if inode in visited_inodes:
dirnames.clear() # Don't descend into this directory
continue
visited_inodes.add(inode)
except (OSError, PermissionError) as e:
app_logger.warning(f"Skipping inaccessible directory {dpath}: {e}")
dirnames.clear()
continue
if dpath != LIBRARY_DIR:
rel_d = dpath.relative_to(LIBRARY_DIR).as_posix()
db.upsert_dir(
conn,
rel=rel_d,
name=dpath.name,
parent=_parent_rel(rel_d),
mtime=dpath.stat().st_mtime,
)
try:
db.upsert_dir(
conn,
rel=rel_d,
name=dpath.name,
parent=_parent_rel(rel_d),
mtime=stat_info.st_mtime,
)
except Exception as e:
app_logger.warning(f"Failed to index directory {rel_d}: {e}")
for fn in filenames:
p = dpath / fn
if p.suffix.lower() != ".cbz":
continue
rel = p.relative_to(LIBRARY_DIR).as_posix()
st = p.stat()
db.upsert_file(
conn,
rel=rel,
name=p.stem,
size=st.st_size,
mtime=st.st_mtime,
parent=_parent_rel(rel),
ext="cbz",
)
meta = _read_comicinfo(p)
if meta:
db.upsert_meta(conn, rel=rel, meta=meta)
_index_progress(rel)
try:
# Check if file exists and is accessible (handles broken symlinks)
if not p.exists():
app_logger.warning(f"Skipping broken symlink or inaccessible file: {p}")
continue
rel = p.relative_to(LIBRARY_DIR).as_posix()
st = p.stat()
db.upsert_file(
conn,
rel=rel,
name=p.stem,
size=st.st_size,
mtime=st.st_mtime,
parent=_parent_rel(rel),
ext="cbz",
)
meta = _read_comicinfo(p)
if meta:
db.upsert_meta(conn, rel=rel, meta=meta)
_index_progress(rel)
except (OSError, PermissionError) as e:
app_logger.warning(f"Failed to index file {p}: {e}")
continue
db.prune_stale(conn)