fix: handle files in newly moved monitor directories

This commit is contained in:
jxxghp
2026-07-21 08:22:46 +08:00
parent 4ca3e40507
commit 053e1b7562
2 changed files with 55 additions and 0 deletions

View File

@@ -159,6 +159,7 @@ class LocalDirectoryWatcher:
将 watchfiles 原始变更转换为目录监控事件。
:param changes: watchfiles 返回的变更集合
"""
changes = self._expand_added_directories(changes)
for change_type, path_str in sorted(changes, key=lambda item: item[1]):
if change_type not in self._HANDLE_CHANGES:
continue
@@ -180,6 +181,30 @@ class LocalDirectoryWatcher:
except Exception as err:
logger.error(f"处理本地目录监控事件失败: {path_str} - {err}")
def _expand_added_directories(self, changes: set[tuple[Change, str]]) -> set[tuple[Change, str]]:
"""
将整体移入监控范围的新增目录展开为内部文件事件。
:param changes: watchfiles 返回的变更集合
:return: 包含目录内新增文件的变更集合
"""
expanded_changes = set(changes)
for change_type, path_str in changes:
if change_type != Change.added:
continue
event_path = Path(path_str)
try:
if not event_path.is_dir():
continue
for nested_path in event_path.rglob("*"):
if not nested_path.is_file():
continue
nested_path_str = nested_path.as_posix()
if self._watch_filter(Change.added, nested_path_str):
expanded_changes.add((Change.added, nested_path_str))
except OSError as err:
logger.debug(f"扫描新增目录失败: {event_path} - {err}")
return expanded_changes
@staticmethod
def _build_event(change_type: Change, event_path: Path) -> Optional[DirectoryChangeEvent]:
"""

View File

@@ -86,6 +86,36 @@ def test_handle_changes_skips_missing_paths(tmp_path):
assert callback.events == []
def test_handle_changes_expands_added_directory_files(tmp_path):
"""
整体移入的新增目录应递归转换成内部文件事件且不重复分发。
"""
added_dir = tmp_path / "task"
nested_dir = added_dir / "season"
nested_dir.mkdir(parents=True)
movie_file = added_dir / "movie.mkv"
episode_file = nested_dir / "episode.mkv"
ignored_file = added_dir / ".DS_Store"
movie_file.write_bytes(b"movie")
episode_file.write_bytes(b"episode")
ignored_file.write_bytes(b"ignored")
callback = CallbackRecorder()
watcher = LocalDirectoryWatcher(tmp_path, callback=callback, force_polling=False)
watcher._handle_changes({
(Change.added, added_dir.as_posix()),
(Change.added, movie_file.as_posix()),
})
assert [
(event.change_type, event_path, file_size)
for event, _, event_path, file_size in callback.events
] == [
(Change.added, movie_file.as_posix(), 5),
(Change.added, episode_file.as_posix(), 7),
]
def test_event_handler_routes_file_events_to_transfer_handler():
"""
文件事件应继续按 local 存储交给整理流程。