Update analysis and plan files to enhance duplicate handling and reporting

- Updated `analysis.json` with a new generation timestamp.
- Modified `plan.json` to include a new plan ID and created timestamp, and changed operation types from "no-op" to "quarantine" for specific files needing manual review.
- Enhanced the README.md to document the new `--analysis` option for generating execution plans, which now includes a human-readable summary and duplicate handling strategies.
- Introduced a new `duplicate_resolve.py` module to manage duplicate file resolution strategies.
- Improved the execution engine to support quarantine operations and added rollback functionality for quarantined files.

These changes improve the functionality of the Video Library Manager by providing better duplicate management and clearer reporting capabilities.
This commit is contained in:
windyboy
2026-02-10 18:07:38 +08:00
parent dcd87754cf
commit 79f5ddf1f5
14 changed files with 882 additions and 456 deletions
+55 -7
View File
@@ -14,21 +14,32 @@ from pathlib import Path
from typing import Optional
from uuid import uuid4
from .config import Config
from .logging_config import get_logger, log_operation
from .models import ExecutionPlan, FileOperation, OperationResult, RollbackLog
from .quarantine import QuarantineManager
from .utils import ensure_utc, utc_now
class ExecutionEngine:
"""Engine for executing file operations safely with dry-run support."""
def __init__(self, logger: Optional[logging.Logger] = None):
def __init__(
self,
logger: Optional[logging.Logger] = None,
config: Optional[Config] = None,
):
"""Initialize the execution engine.
Args:
logger: Optional logger instance (uses default if not provided)
config: Optional config (required for quarantine operations)
"""
self.logger = logger or get_logger()
self.config = config
self._quarantine_manager: Optional[QuarantineManager] = (
QuarantineManager(config, self.logger) if config else None
)
def execute_plan(
self,
@@ -134,7 +145,40 @@ class ExecutionEngine:
error_message=None,
executed_at=executed_at
)
# Handle quarantine operations (no destination_path; use QuarantineManager)
if operation.operation_type == "quarantine":
if not self._quarantine_manager:
return OperationResult(
operation=operation,
success=False,
error_message="Config required for quarantine operations",
executed_at=executed_at
)
if mode == "dry-run":
log_operation(
self.logger,
logging.INFO,
f"[DRY-RUN] Would quarantine: {operation.source_path} ({operation.reason})",
operation_type="execute",
file_path=operation.source_path
)
return OperationResult(
operation=operation,
success=True,
error_message=None,
executed_at=executed_at
)
result = self._quarantine_manager.quarantine_file(
operation.source_path, operation.reason
)
return OperationResult(
operation=operation,
success=result.success,
error_message=result.error_message,
executed_at=result.executed_at
)
# Handle conflicted operations
if operation.has_conflict:
log_operation(
@@ -171,11 +215,16 @@ class ExecutionEngine:
Returns:
OperationResult indicating what would happen
"""
dest = operation.destination_path
msg = (
f"[DRY-RUN] Would quarantine: {operation.source_path} ({operation.reason})"
if operation.operation_type == "quarantine"
else f"[DRY-RUN] Would {operation.operation_type}: {operation.source_path} -> {dest}"
)
log_operation(
self.logger,
logging.INFO,
f"[DRY-RUN] Would {operation.operation_type}: "
f"{operation.source_path} -> {operation.destination_path}",
msg,
operation_type="execute",
file_path=operation.source_path
)
@@ -589,4 +638,3 @@ class ExecutionEngine:
summary_msg,
operation_type="rollback"
)