mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
feat: add open settings location endpoint
- Add `open_settings_location` method to `FileSystemHandler` to open OS file explorer at settings file location - Register new POST route `/api/lm/settings/open-location` for settings file access - Inject `SettingsManager` dependency into `FileSystemHandler` constructor - Add cross-platform support for Windows, macOS, and Linux file explorers - Include error handling for missing settings files and system exceptions
This commit is contained in:
@@ -853,6 +853,9 @@ class MetadataArchiveHandler:
|
|||||||
|
|
||||||
|
|
||||||
class FileSystemHandler:
|
class FileSystemHandler:
|
||||||
|
def __init__(self, settings_service=None) -> None:
|
||||||
|
self._settings = settings_service or get_settings_manager()
|
||||||
|
|
||||||
async def open_file_location(self, request: web.Request) -> web.Response:
|
async def open_file_location(self, request: web.Request) -> web.Response:
|
||||||
try:
|
try:
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
@@ -877,6 +880,30 @@ class FileSystemHandler:
|
|||||||
logger.error("Failed to open file location: %s", exc, exc_info=True)
|
logger.error("Failed to open file location: %s", exc, exc_info=True)
|
||||||
return web.json_response({"success": False, "error": str(exc)}, status=500)
|
return web.json_response({"success": False, "error": str(exc)}, status=500)
|
||||||
|
|
||||||
|
async def open_settings_location(self, request: web.Request) -> web.Response:
|
||||||
|
try:
|
||||||
|
settings_file = getattr(self._settings, "settings_file", None)
|
||||||
|
if not settings_file:
|
||||||
|
return web.json_response({"success": False, "error": "Settings file not found"}, status=404)
|
||||||
|
|
||||||
|
settings_file = os.path.abspath(settings_file)
|
||||||
|
if not os.path.isfile(settings_file):
|
||||||
|
return web.json_response({"success": False, "error": "Settings file does not exist"}, status=404)
|
||||||
|
|
||||||
|
if os.name == "nt":
|
||||||
|
subprocess.Popen(["explorer", "/select,", settings_file])
|
||||||
|
elif os.name == "posix":
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
subprocess.Popen(["open", "-R", settings_file])
|
||||||
|
else:
|
||||||
|
folder = os.path.dirname(settings_file)
|
||||||
|
subprocess.Popen(["xdg-open", folder])
|
||||||
|
|
||||||
|
return web.json_response({"success": True, "message": f"Opened settings folder: {settings_file}"})
|
||||||
|
except Exception as exc: # pragma: no cover - defensive logging
|
||||||
|
logger.error("Failed to open settings location: %s", exc, exc_info=True)
|
||||||
|
return web.json_response({"success": False, "error": str(exc)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
class NodeRegistryHandler:
|
class NodeRegistryHandler:
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -1103,6 +1130,7 @@ class MiscHandlerSet:
|
|||||||
"get_metadata_archive_status": self.metadata_archive.get_metadata_archive_status,
|
"get_metadata_archive_status": self.metadata_archive.get_metadata_archive_status,
|
||||||
"get_model_versions_status": self.model_library.get_model_versions_status,
|
"get_model_versions_status": self.model_library.get_model_versions_status,
|
||||||
"open_file_location": self.filesystem.open_file_location,
|
"open_file_location": self.filesystem.open_file_location,
|
||||||
|
"open_settings_location": self.filesystem.open_settings_location,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ MISC_ROUTE_DEFINITIONS: tuple[RouteDefinition, ...] = (
|
|||||||
RouteDefinition("POST", "/api/lm/remove-metadata-archive", "remove_metadata_archive"),
|
RouteDefinition("POST", "/api/lm/remove-metadata-archive", "remove_metadata_archive"),
|
||||||
RouteDefinition("GET", "/api/lm/metadata-archive-status", "get_metadata_archive_status"),
|
RouteDefinition("GET", "/api/lm/metadata-archive-status", "get_metadata_archive_status"),
|
||||||
RouteDefinition("GET", "/api/lm/model-versions-status", "get_model_versions_status"),
|
RouteDefinition("GET", "/api/lm/model-versions-status", "get_model_versions_status"),
|
||||||
|
RouteDefinition("POST", "/api/lm/settings/open-location", "open_settings_location"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class MiscRoutes:
|
|||||||
settings_service=self._settings,
|
settings_service=self._settings,
|
||||||
metadata_provider_updater=self._metadata_provider_updater,
|
metadata_provider_updater=self._metadata_provider_updater,
|
||||||
)
|
)
|
||||||
filesystem = FileSystemHandler()
|
filesystem = FileSystemHandler(settings_service=self._settings)
|
||||||
node_registry_handler = NodeRegistryHandler(
|
node_registry_handler = NodeRegistryHandler(
|
||||||
node_registry=self._node_registry,
|
node_registry=self._node_registry,
|
||||||
prompt_server=self._prompt_server,
|
prompt_server=self._prompt_server,
|
||||||
|
|||||||
@@ -883,6 +883,7 @@ class SettingsManager:
|
|||||||
|
|
||||||
if os.path.abspath(previous_path) != os.path.abspath(target_path):
|
if os.path.abspath(previous_path) != os.path.abspath(target_path):
|
||||||
self._copy_model_cache_directory(previous_dir, target_dir)
|
self._copy_model_cache_directory(previous_dir, target_dir)
|
||||||
|
logger.info("Switching settings file to: %s", target_path)
|
||||||
|
|
||||||
self._pending_portable_switch = {"other_path": other_path}
|
self._pending_portable_switch = {"other_path": other_path}
|
||||||
self.settings_file = target_path
|
self.settings_file = target_path
|
||||||
@@ -929,7 +930,12 @@ class SettingsManager:
|
|||||||
and os.path.abspath(source_cache_dir) != os.path.abspath(target_cache_dir)
|
and os.path.abspath(source_cache_dir) != os.path.abspath(target_cache_dir)
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
shutil.copytree(source_cache_dir, target_cache_dir, dirs_exist_ok=True)
|
shutil.copytree(
|
||||||
|
source_cache_dir,
|
||||||
|
target_cache_dir,
|
||||||
|
dirs_exist_ok=True,
|
||||||
|
ignore=shutil.ignore_patterns("*.sqlite-shm", "*.sqlite-wal"),
|
||||||
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Failed to copy model_cache directory from %s to %s: %s",
|
"Failed to copy model_cache directory from %s to %s: %s",
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export class SettingsManager {
|
|||||||
this.initializationPromise = null;
|
this.initializationPromise = null;
|
||||||
this.availableLibraries = {};
|
this.availableLibraries = {};
|
||||||
this.activeLibrary = '';
|
this.activeLibrary = '';
|
||||||
this.settingsFilePath = null;
|
|
||||||
this.registeredStartupBannerIds = new Set();
|
this.registeredStartupBannerIds = new Set();
|
||||||
|
|
||||||
// Add initialization to sync with modal state
|
// Add initialization to sync with modal state
|
||||||
@@ -56,7 +55,6 @@ export class SettingsManager {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.success && data.settings) {
|
if (data.success && data.settings) {
|
||||||
state.global.settings = this.mergeSettingsWithDefaults(data.settings);
|
state.global.settings = this.mergeSettingsWithDefaults(data.settings);
|
||||||
this.settingsFilePath = data.settings.settings_file || this.settingsFilePath;
|
|
||||||
this.registerStartupMessages(data.messages);
|
this.registerStartupMessages(data.messages);
|
||||||
console.log('Settings synced from backend');
|
console.log('Settings synced from backend');
|
||||||
} else {
|
} else {
|
||||||
@@ -177,10 +175,6 @@ export class SettingsManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.settingsFilePath && typeof message.settings_file === 'string') {
|
|
||||||
this.settingsFilePath = message.settings_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bannerId = `startup-${message.code || index}`;
|
const bannerId = `startup-${message.code || index}`;
|
||||||
if (this.registeredStartupBannerIds.has(bannerId)) {
|
if (this.registeredStartupBannerIds.has(bannerId)) {
|
||||||
return;
|
return;
|
||||||
@@ -316,12 +310,8 @@ export class SettingsManager {
|
|||||||
|
|
||||||
const openSettingsLocationButton = document.querySelector('.settings-open-location-trigger');
|
const openSettingsLocationButton = document.querySelector('.settings-open-location-trigger');
|
||||||
if (openSettingsLocationButton) {
|
if (openSettingsLocationButton) {
|
||||||
if (openSettingsLocationButton.dataset.settingsPath) {
|
|
||||||
this.settingsFilePath = openSettingsLocationButton.dataset.settingsPath;
|
|
||||||
}
|
|
||||||
openSettingsLocationButton.addEventListener('click', () => {
|
openSettingsLocationButton.addEventListener('click', () => {
|
||||||
const filePath = openSettingsLocationButton.dataset.settingsPath;
|
this.openSettingsFileLocation();
|
||||||
this.openSettingsFileLocation(filePath);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,29 +354,16 @@ export class SettingsManager {
|
|||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async openSettingsFileLocation(filePath) {
|
async openSettingsFileLocation() {
|
||||||
const targetPath = filePath || this.settingsFilePath || document.querySelector('.settings-open-location-trigger')?.dataset.settingsPath;
|
|
||||||
|
|
||||||
if (!targetPath) {
|
|
||||||
showToast('settings.openSettingsFileLocation.failed', {}, 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/lm/open-file-location', {
|
const response = await fetch('/api/lm/settings/open-location', {
|
||||||
method: 'POST',
|
method: 'POST'
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ file_path: targetPath }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Request failed with status ${response.status}`);
|
throw new Error(`Request failed with status ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.settingsFilePath = targetPath;
|
|
||||||
|
|
||||||
showToast('settings.openSettingsFileLocation.success', {}, 'success');
|
showToast('settings.openSettingsFileLocation.success', {}, 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to open settings file location:', error);
|
console.error('Failed to open settings file location:', error);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="settings-action-link settings-open-location-trigger"
|
class="settings-action-link settings-open-location-trigger"
|
||||||
data-settings-path="{{ settings.settings_file }}"
|
|
||||||
aria-label="{{ t('settings.openSettingsFileLocation.tooltip') }}"
|
aria-label="{{ t('settings.openSettingsFileLocation.tooltip') }}"
|
||||||
title="{{ t('settings.openSettingsFileLocation.tooltip') }}">
|
title="{{ t('settings.openSettingsFileLocation.tooltip') }}">
|
||||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||||
|
|||||||
Reference in New Issue
Block a user