feat: update download path template handling for model types and migrate old settings

This commit is contained in:
Will Miao
2025-08-13 19:23:37 +08:00
parent e4195f874d
commit b8aa7184bd
5 changed files with 53 additions and 12 deletions

View File

@@ -756,8 +756,8 @@ class BaseModelRoutes(ABC):
'error': 'No model roots configured' 'error': 'No model roots configured'
}, status=400) }, status=400)
# Check if flat structure is configured # Check if flat structure is configured for this model type
path_template = settings.get('download_path_template', '{base_model}/{first_tag}') path_template = settings.get_download_path_template(self.service.model_type)
is_flat_structure = not path_template is_flat_structure = not path_template
# Prepare results tracking # Prepare results tracking
@@ -832,7 +832,7 @@ class BaseModelRoutes(ABC):
target_dir = current_root target_dir = current_root
else: else:
# Calculate new relative path based on settings # Calculate new relative path based on settings
new_relative_path = calculate_relative_path_for_model(model) new_relative_path = calculate_relative_path_for_model(model, self.service.model_type)
# If no relative path calculated (insufficient metadata), skip # If no relative path calculated (insufficient metadata), skip
if not new_relative_path: if not new_relative_path:

View File

@@ -258,7 +258,7 @@ class DownloadManager:
save_dir = default_path save_dir = default_path
# Calculate relative path using template # Calculate relative path using template
relative_path = self._calculate_relative_path(version_info) relative_path = self._calculate_relative_path(version_info, model_type)
# Update save directory with relative path if provided # Update save directory with relative path if provided
if relative_path: if relative_path:
@@ -331,17 +331,18 @@ class DownloadManager:
return {'success': False, 'error': f"Early access restriction: {str(e)}. Please ensure you have purchased early access and are logged in to Civitai."} return {'success': False, 'error': f"Early access restriction: {str(e)}. Please ensure you have purchased early access and are logged in to Civitai."}
return {'success': False, 'error': str(e)} return {'success': False, 'error': str(e)}
def _calculate_relative_path(self, version_info: Dict) -> str: def _calculate_relative_path(self, version_info: Dict, model_type: str = 'lora') -> str:
"""Calculate relative path using template from settings """Calculate relative path using template from settings
Args: Args:
version_info: Version info from Civitai API version_info: Version info from Civitai API
model_type: Type of model ('lora', 'checkpoint', 'embedding')
Returns: Returns:
Relative path string Relative path string
""" """
# Get path template from settings, default to '{base_model}/{first_tag}' # Get path template from settings for specific model type
path_template = settings.get('download_path_template', '{base_model}/{first_tag}') path_template = settings.get_download_path_template(model_type)
# If template is empty, return empty path (flat structure) # If template is empty, return empty path (flat structure)
if not path_template: if not path_template:
@@ -350,6 +351,9 @@ class DownloadManager:
# Get base model name # Get base model name
base_model = version_info.get('baseModel', '') base_model = version_info.get('baseModel', '')
# Get author from creator data
author = version_info.get('creator', {}).get('username', 'Anonymous')
# Apply mapping if available # Apply mapping if available
base_model_mappings = settings.get('base_model_path_mappings', {}) base_model_mappings = settings.get('base_model_path_mappings', {})
mapped_base_model = base_model_mappings.get(base_model, base_model) mapped_base_model = base_model_mappings.get(base_model, base_model)
@@ -372,6 +376,7 @@ class DownloadManager:
formatted_path = path_template formatted_path = path_template
formatted_path = formatted_path.replace('{base_model}', mapped_base_model) formatted_path = formatted_path.replace('{base_model}', mapped_base_model)
formatted_path = formatted_path.replace('{first_tag}', first_tag) formatted_path = formatted_path.replace('{first_tag}', first_tag)
formatted_path = formatted_path.replace('{author}', author)
return formatted_path return formatted_path

View File

@@ -9,6 +9,7 @@ class SettingsManager:
def __init__(self): def __init__(self):
self.settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'settings.json') self.settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'settings.json')
self.settings = self._load_settings() self.settings = self._load_settings()
self._migrate_download_path_template()
self._auto_set_default_roots() self._auto_set_default_roots()
self._check_environment_variables() self._check_environment_variables()
@@ -22,6 +23,24 @@ class SettingsManager:
logger.error(f"Error loading settings: {e}") logger.error(f"Error loading settings: {e}")
return self._get_default_settings() return self._get_default_settings()
def _migrate_download_path_template(self):
"""Migrate old download_path_template to new download_path_templates"""
old_template = self.settings.get('download_path_template')
templates = self.settings.get('download_path_templates')
# If old template exists and new templates don't exist, migrate
if old_template is not None and not templates:
logger.info("Migrating download_path_template to download_path_templates")
self.settings['download_path_templates'] = {
'lora': old_template,
'checkpoint': old_template,
'embedding': old_template
}
# Remove old setting
del self.settings['download_path_template']
self._save_settings()
logger.info("Migration completed")
def _auto_set_default_roots(self): def _auto_set_default_roots(self):
"""Auto set default root paths if only one folder is present and default is empty.""" """Auto set default root paths if only one folder is present and default is empty."""
folder_paths = self.settings.get('folder_paths', {}) folder_paths = self.settings.get('folder_paths', {})
@@ -81,4 +100,16 @@ class SettingsManager:
except Exception as e: except Exception as e:
logger.error(f"Error saving settings: {e}") logger.error(f"Error saving settings: {e}")
def get_download_path_template(self, model_type: str) -> str:
"""Get download path template for specific model type
Args:
model_type: The type of model ('lora', 'checkpoint', 'embedding')
Returns:
Template string for the model type, defaults to '{base_model}/{first_tag}'
"""
templates = self.settings.get('download_path_templates', {})
return templates.get(model_type, '{base_model}/{first_tag}')
settings = SettingsManager() settings = SettingsManager()

View File

@@ -132,17 +132,18 @@ def calculate_recipe_fingerprint(loras):
return fingerprint return fingerprint
def calculate_relative_path_for_model(model_data: Dict) -> str: def calculate_relative_path_for_model(model_data: Dict, model_type: str = 'lora') -> str:
"""Calculate relative path for existing model using template from settings """Calculate relative path for existing model using template from settings
Args: Args:
model_data: Model data from scanner cache model_data: Model data from scanner cache
model_type: Type of model ('lora', 'checkpoint', 'embedding')
Returns: Returns:
Relative path string (empty string for flat structure) Relative path string (empty string for flat structure)
""" """
# Get path template from settings, default to '{base_model}/{first_tag}' # Get path template from settings for specific model type
path_template = settings.get('download_path_template', '{base_model}/{first_tag}') path_template = settings.get_download_path_template(model_type)
# If template is empty, return empty path (flat structure) # If template is empty, return empty path (flat structure)
if not path_template: if not path_template:
@@ -154,9 +155,12 @@ def calculate_relative_path_for_model(model_data: Dict) -> str:
# For CivitAI models, prefer civitai data only if 'id' exists; for non-CivitAI models, use model_data directly # For CivitAI models, prefer civitai data only if 'id' exists; for non-CivitAI models, use model_data directly
if civitai_data and civitai_data.get('id') is not None: if civitai_data and civitai_data.get('id') is not None:
base_model = civitai_data.get('baseModel', '') base_model = civitai_data.get('baseModel', '')
# Get author from civitai creator data
author = civitai_data.get('creator', {}).get('username', 'Anonymous')
else: else:
# Fallback to model_data fields for non-CivitAI models # Fallback to model_data fields for non-CivitAI models
base_model = model_data.get('base_model', '') base_model = model_data.get('base_model', '')
author = 'Anonymous' # Default for non-CivitAI models
model_tags = model_data.get('tags', []) model_tags = model_data.get('tags', [])
@@ -182,6 +186,7 @@ def calculate_relative_path_for_model(model_data: Dict) -> str:
formatted_path = path_template formatted_path = path_template
formatted_path = formatted_path.replace('{base_model}', mapped_base_model) formatted_path = formatted_path.replace('{base_model}', mapped_base_model)
formatted_path = formatted_path.replace('{first_tag}', first_tag) formatted_path = formatted_path.replace('{first_tag}', first_tag)
formatted_path = formatted_path.replace('{author}', author)
return formatted_path return formatted_path

View File

@@ -92,7 +92,7 @@ export class SettingsManager {
// Ensure all model types have templates // Ensure all model types have templates
Object.keys(DEFAULT_PATH_TEMPLATES).forEach(modelType => { Object.keys(DEFAULT_PATH_TEMPLATES).forEach(modelType => {
if (!state.global.settings.download_path_templates[modelType]) { if (typeof state.global.settings.download_path_templates[modelType] === 'undefined') {
state.global.settings.download_path_templates[modelType] = DEFAULT_PATH_TEMPLATES[modelType]; state.global.settings.download_path_templates[modelType] = DEFAULT_PATH_TEMPLATES[modelType];
} }
}); });
@@ -586,7 +586,7 @@ export class SettingsManager {
// Find matching preset // Find matching preset
const matchingPreset = this.findMatchingPreset(template); const matchingPreset = this.findMatchingPreset(template);
if (matchingPreset) { if (matchingPreset !== null) {
presetSelect.value = matchingPreset; presetSelect.value = matchingPreset;
if (customRow) customRow.style.display = 'none'; if (customRow) customRow.style.display = 'none';
} else { } else {