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
+39
View File
@@ -169,6 +169,45 @@ class TestHardlinkManager:
assert (temp_output_dir / "- Dle roku" / "1968" / "file1.txt").exists()
assert not (temp_output_dir / "Dle roku").exists()
def test_sync_replaces_orphan_squatting_the_expected_name(
self, temp_source_dir, temp_output_dir, tag_manager
):
"""An orphan link with the *expected name* but a stale inode is replaced.
Regression: when a movie is re-imported (new inode), the old hardlink
keeps the correct name but points at the dead inode. Name-only obsolete
detection spared it and forced the real link to a ``_1`` suffix, which
was then swept every run — an endless created==removed churn. Obsolete
detection now matches the inode too, so the orphan is removed and the
base name is restored, stably.
"""
f = File(temp_source_dir / "file1.txt", tag_manager)
f.tags.clear()
f.add_tag(Tag("žánr", "Drama"))
roots = {"žánr": ""}
manager = HardlinkManager(temp_output_dir)
manager.sync_structure([f], category_roots=roots)
link = temp_output_dir / "Drama" / "file1.txt"
assert link.stat().st_ino == f.file_path.stat().st_ino
# Simulate a re-import: the expected name now points at a *different*
# (orphan) inode, exactly as a replaced movie leaves behind.
orphan_src = temp_source_dir / "orphan.txt"
orphan_src.write_text("old content")
link.unlink()
os.link(orphan_src, link)
assert link.stat().st_ino != f.file_path.stat().st_ino
created, _, removed, _ = manager.sync_structure([f], category_roots=roots)
# The orphan was swept and the correct link recreated under the base name.
assert removed >= 1 and created >= 1
assert link.stat().st_ino == f.file_path.stat().st_ino
assert not (temp_output_dir / "Drama" / "file1_1.txt").exists()
# And it is now stable: a second run touches nothing (no churn).
created2, _, removed2, _ = manager.sync_structure([f], category_roots=roots)
assert (created2, removed2) == (0, 0)
def test_sync_removes_root_genre_folder_when_last_movie_drops_tag(
self, temp_source_dir, temp_output_dir, tag_manager
):