Fix endless Filmotéka hardlink churn and show pool/output paths in the status bar

This commit is contained in:
2026-07-07 16:18:24 +02:00
parent 7ae0709f38
commit 97a39709e8
7 changed files with 97 additions and 66 deletions
+27 -10
View File
@@ -478,15 +478,27 @@ class HardlinkManager:
except OSError:
continue
# Every link the tag tree *should* contain, given current files+tags.
expected_all: set[Path] = set()
# Every link the tag tree *should* contain, mapped to the inode(s) it may
# point at. A path can be wanted by more than one movie (same rendered
# name in the same folder), so the value is a *set* of valid inodes.
# Matching by inode too — not just the path — is what catches an **orphan
# that squats the expected name**: a link whose name is right but which
# points to an old inode (movie re-imported/replaced) is still obsolete,
# while the correctly re-created link (forced to a ``_1`` suffix because
# the orphan holds the base name) is *not* wrongly swept.
expected: dict[Path, set[int]] = {}
for file_obj in files:
try:
file_inode = file_obj.file_path.stat().st_ino
except OSError:
continue
for tag in file_obj.tags:
target_dir = self._target_dir(tag, roots, category_transforms)
if target_dir is None:
continue
expected_all.add(target_dir / self._link_name(
file_obj, tag, category_filename_templates))
path = target_dir / self._link_name(
file_obj, tag, category_filename_templates)
expected.setdefault(path, set()).add(file_inode)
# Scan only the tag-tree's own top-level folders (skip copy-as-is mirrors).
# Inside them, any file that isn't an expected link is obsolete — whether
@@ -503,13 +515,18 @@ class HardlinkManager:
for link_file in top.rglob("*"):
if not link_file.is_file():
continue
if link_file in expected_all:
continue
try:
source = inode_to_file[link_file.stat().st_ino].file_path
except (OSError, KeyError):
source = link_file # orphan (no current pool movie)
obsolete.append((link_file, source))
link_inode = link_file.stat().st_ino
except OSError:
continue
# Valid only when both the path *and* the inode match a current
# movie; a name-only match (orphan squatting the name) is stale.
if link_inode in expected.get(link_file, ()):
continue
source = inode_to_file.get(link_inode)
obsolete.append(
(link_file, source.file_path if source else link_file)
)
return obsolete