refactor CLI command modules and synchronize docs

This commit is contained in:
windyboy
2026-02-16 12:31:26 +08:00
parent 0be802eac5
commit 2dd329cba9
41 changed files with 1066 additions and 753 deletions
+58 -2
View File
@@ -40,6 +40,42 @@ def _is_sample_path(path: Path) -> bool:
return bool(re.search(r"(^|[\s._-])sample($|[\s._-])", stem))
def _analyze_directory_impact(operations: list[FileOperation]) -> dict:
"""Analyze which directories will be emptied by the plan."""
# Get all source directories that have files being moved/renamed
source_dirs = set()
moved_files = set()
for operation in operations:
if operation.operation_type in ("move", "rename"):
source_dirs.add(operation.source_path.parent)
moved_files.add(operation.source_path)
emptied_dirs = []
for dir_path in source_dirs:
# Only analyze directories that actually exist
if not dir_path.exists():
continue
# Count files that will remain in this directory after operations
remaining_count = 0
try:
for item in dir_path.iterdir():
if item.is_file() and item not in moved_files:
remaining_count += 1
except (OSError, PermissionError):
# If we can't read the directory, skip analysis
continue
# If no files will remain, this directory will be emptied
if remaining_count == 0:
emptied_dirs.append(dir_path)
return {
"emptied_directories": emptied_dirs,
"warning_required": len(emptied_dirs) > 0
}
def generate_plan(
identities: list[tuple[VideoFile, Union[MovieIdentity, SeriesIdentity, None]]],
config: Config,
@@ -120,10 +156,29 @@ def generate_plan(
conflict_reason=None,
)
# Analyze directory impact and add preservation operations
directory_analysis = _analyze_directory_impact(operations)
if directory_analysis["warning_required"]:
# Add directory preservation operations for emptied directories
for emptied_dir in directory_analysis["emptied_directories"]:
operations.append(FileOperation(
operation_type="preserve-directory",
source_path=emptied_dir,
destination_path=None,
reason=f"Preserve empty source directory: {emptied_dir.name}",
has_conflict=False,
conflict_reason=None
))
summary = _generate_summary(operations)
summary_by_reason = _generate_summary_by_reason(operations)
human_summary = _generate_human_summary(operations, summary, summary_by_reason, metadata)
# Add directory warnings to metadata
if directory_analysis["warning_required"]:
metadata["emptied_directories"] = [str(d) for d in directory_analysis["emptied_directories"]]
metadata["directory_warning"] = True
return ExecutionPlan(
plan_id=str(uuid.uuid4()),
created_at=utc_now(),
@@ -445,9 +500,10 @@ def _generate_summary(operations: list[FileOperation]) -> dict:
"move": 0,
"rename": 0,
"quarantine": 0,
"no-op": 0
"no-op": 0,
"preserve-directory": 0
}
for operation in operations:
op_type = operation.operation_type
if op_type in summary: