fix(paths): fixed symlinks not being followed
This commit is contained in:
+66
-37
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user