feat(auto-organize): improve exclusion handling and progress reporting

- Add auto_organize_exclusions to settings handler proxy keys
- Refactor model file service to handle exclusions relative to model roots
- Improve auto-organize progress reporting for empty operations
- Fix exclusion pattern matching to consider relative paths within model roots
- Ensure proper validation when no model roots are configured
- Add comprehensive cleanup reporting for empty auto-organize operations
This commit is contained in:
Will Miao
2025-11-20 18:33:48 +08:00
parent c533a8e7bf
commit 26e4895807
15 changed files with 140 additions and 79 deletions

View File

@@ -202,6 +202,7 @@ class SettingsHandler:
"model_card_footer_action",
"model_name_display",
"update_flag_strategy",
"auto_organize_exclusions",
)
_PROXY_KEYS = {"proxy_enabled", "proxy_host", "proxy_port", "proxy_username", "proxy_password", "proxy_type"}

View File

@@ -117,19 +117,18 @@ class ModelFileService:
else:
result.operation_type = 'all'
model_roots = self.get_model_roots()
if not model_roots:
raise ValueError('No model roots configured')
if normalized_exclusions:
all_models = [
model
for model in all_models
if not self._should_exclude_model(
model.get('file_path'), normalized_exclusions
model.get('file_path'), normalized_exclusions, model_roots
)
]
# Get model roots for this scanner
model_roots = self.get_model_roots()
if not model_roots:
raise ValueError('No model roots configured')
# Check if flat structure is configured for this model type
settings_manager = get_settings_manager()
@@ -151,7 +150,34 @@ class ModelFileService:
'skipped': 0,
'operation_type': result.operation_type
})
if result.total == 0:
if progress_callback:
await asyncio.sleep(0.1)
payload = {
'type': 'auto_organize_progress',
'total': 0,
'processed': 0,
'success': 0,
'failures': 0,
'skipped': 0,
'operation_type': result.operation_type
}
await progress_callback.on_progress({**payload, 'status': 'processing'})
await progress_callback.on_progress({
**payload,
'status': 'cleaning',
'message': 'Cleaning up empty directories...'
})
result.cleanup_counts = {}
await progress_callback.on_progress({
**payload,
'status': 'completed',
'cleanup': result.cleanup_counts
})
return result
# Process models in batches
await self._process_models_in_batches(
all_models,
@@ -325,16 +351,35 @@ class ModelFileService:
return None
def _should_exclude_model(
self, file_path: Optional[str], patterns: Sequence[str]
self,
file_path: Optional[str],
patterns: Sequence[str],
model_roots: Sequence[str],
) -> bool:
if not file_path or not patterns:
return False
normalized_path = os.path.normpath(file_path).replace(os.sep, '/')
filename = os.path.basename(normalized_path)
relative_path = None
if model_roots:
root = self._find_model_root(file_path, list(model_roots))
if root:
normalized_root = os.path.normpath(root)
try:
relative = os.path.relpath(file_path, normalized_root)
except ValueError:
relative = None
if relative is not None:
relative_path = relative.replace(os.sep, '/')
for pattern in patterns:
if fnmatch.fnmatch(filename, pattern) or fnmatch.fnmatch(normalized_path, pattern):
if fnmatch.fnmatch(filename, pattern):
return True
if relative_path and fnmatch.fnmatch(relative_path, pattern):
return True
if fnmatch.fnmatch(normalized_path, pattern):
return True
return False
@@ -493,4 +538,4 @@ class ModelMoveService:
'results': [],
'success_count': 0,
'failure_count': len(file_paths)
}
}