mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
feat: Implement Git-based update functionality with nightly mode support and UI enhancements
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import logging
|
import logging
|
||||||
import toml
|
import toml
|
||||||
import subprocess
|
import git
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, List
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ class UpdateRoutes:
|
|||||||
"""Register update check routes"""
|
"""Register update check routes"""
|
||||||
app.router.add_get('/api/check-updates', UpdateRoutes.check_updates)
|
app.router.add_get('/api/check-updates', UpdateRoutes.check_updates)
|
||||||
app.router.add_get('/api/version-info', UpdateRoutes.get_version_info)
|
app.router.add_get('/api/version-info', UpdateRoutes.get_version_info)
|
||||||
|
app.router.add_post('/api/perform-update', UpdateRoutes.perform_update)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def check_updates(request):
|
async def check_updates(request):
|
||||||
@@ -25,6 +28,8 @@ class UpdateRoutes:
|
|||||||
Returns update status and version information
|
Returns update status and version information
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
nightly = request.query.get('nightly', 'false').lower() == 'true'
|
||||||
|
|
||||||
# Read local version from pyproject.toml
|
# Read local version from pyproject.toml
|
||||||
local_version = UpdateRoutes._get_local_version()
|
local_version = UpdateRoutes._get_local_version()
|
||||||
|
|
||||||
@@ -32,9 +37,17 @@ class UpdateRoutes:
|
|||||||
git_info = UpdateRoutes._get_git_info()
|
git_info = UpdateRoutes._get_git_info()
|
||||||
|
|
||||||
# Fetch remote version from GitHub
|
# Fetch remote version from GitHub
|
||||||
|
if nightly:
|
||||||
|
remote_version, changelog = await UpdateRoutes._get_nightly_version()
|
||||||
|
else:
|
||||||
remote_version, changelog = await UpdateRoutes._get_remote_version()
|
remote_version, changelog = await UpdateRoutes._get_remote_version()
|
||||||
|
|
||||||
# Compare versions
|
# Compare versions
|
||||||
|
if nightly:
|
||||||
|
# For nightly, compare commit hashes
|
||||||
|
update_available = UpdateRoutes._compare_nightly_versions(git_info, remote_version)
|
||||||
|
else:
|
||||||
|
# For stable, compare semantic versions
|
||||||
update_available = UpdateRoutes._compare_versions(
|
update_available = UpdateRoutes._compare_versions(
|
||||||
local_version.replace('v', ''),
|
local_version.replace('v', ''),
|
||||||
remote_version.replace('v', '')
|
remote_version.replace('v', '')
|
||||||
@@ -46,7 +59,8 @@ class UpdateRoutes:
|
|||||||
'latest_version': remote_version,
|
'latest_version': remote_version,
|
||||||
'update_available': update_available,
|
'update_available': update_available,
|
||||||
'changelog': changelog,
|
'changelog': changelog,
|
||||||
'git_info': git_info
|
'git_info': git_info,
|
||||||
|
'nightly': nightly
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -84,6 +98,168 @@ class UpdateRoutes:
|
|||||||
'error': str(e)
|
'error': str(e)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def perform_update(request):
|
||||||
|
"""
|
||||||
|
Perform Git-based update to latest release tag or main branch
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Parse request body
|
||||||
|
body = await request.json() if request.has_body else {}
|
||||||
|
nightly = body.get('nightly', False)
|
||||||
|
|
||||||
|
# Get current plugin directory
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
plugin_root = os.path.dirname(os.path.dirname(current_dir))
|
||||||
|
|
||||||
|
# Backup settings.json if it exists
|
||||||
|
settings_path = os.path.join(plugin_root, 'settings.json')
|
||||||
|
settings_backup = None
|
||||||
|
if os.path.exists(settings_path):
|
||||||
|
with open(settings_path, 'r', encoding='utf-8') as f:
|
||||||
|
settings_backup = f.read()
|
||||||
|
logger.info("Backed up settings.json")
|
||||||
|
|
||||||
|
# Perform Git update
|
||||||
|
success, new_version = await UpdateRoutes._perform_git_update(plugin_root, nightly)
|
||||||
|
|
||||||
|
# Restore settings.json if we backed it up
|
||||||
|
if settings_backup and success:
|
||||||
|
with open(settings_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(settings_backup)
|
||||||
|
logger.info("Restored settings.json")
|
||||||
|
|
||||||
|
if success:
|
||||||
|
return web.json_response({
|
||||||
|
'success': True,
|
||||||
|
'message': f'Successfully updated to {new_version}',
|
||||||
|
'new_version': new_version
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return web.json_response({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Failed to complete Git update'
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to perform update: {e}", exc_info=True)
|
||||||
|
return web.json_response({
|
||||||
|
'success': False,
|
||||||
|
'error': str(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _get_nightly_version() -> tuple[str, List[str]]:
|
||||||
|
"""
|
||||||
|
Fetch latest commit from main branch
|
||||||
|
"""
|
||||||
|
repo_owner = "willmiao"
|
||||||
|
repo_name = "ComfyUI-Lora-Manager"
|
||||||
|
|
||||||
|
# Use GitHub API to fetch the latest commit from main branch
|
||||||
|
github_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/commits/main"
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(github_url, headers={'Accept': 'application/vnd.github+json'}) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
logger.warning(f"Failed to fetch GitHub commit: {response.status}")
|
||||||
|
return "main", []
|
||||||
|
|
||||||
|
data = await response.json()
|
||||||
|
commit_sha = data.get('sha', '')[:7] # Short hash
|
||||||
|
commit_message = data.get('commit', {}).get('message', '')
|
||||||
|
|
||||||
|
# Format as "main-{short_hash}"
|
||||||
|
version = f"main-{commit_sha}"
|
||||||
|
|
||||||
|
# Use commit message as changelog
|
||||||
|
changelog = [commit_message] if commit_message else []
|
||||||
|
|
||||||
|
return version, changelog
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fetching nightly version: {e}", exc_info=True)
|
||||||
|
return "main", []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _compare_nightly_versions(local_git_info: Dict[str, str], remote_version: str) -> bool:
|
||||||
|
"""
|
||||||
|
Compare local commit hash with remote main branch
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
local_hash = local_git_info.get('short_hash', 'unknown')
|
||||||
|
if local_hash == 'unknown':
|
||||||
|
return True # Assume update available if we can't get local hash
|
||||||
|
|
||||||
|
# Extract remote hash from version string (format: "main-{hash}")
|
||||||
|
if '-' in remote_version:
|
||||||
|
remote_hash = remote_version.split('-')[-1]
|
||||||
|
return local_hash != remote_hash
|
||||||
|
|
||||||
|
return True # Default to update available
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error comparing nightly versions: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _perform_git_update(plugin_root: str, nightly: bool = False) -> tuple[bool, str]:
|
||||||
|
"""
|
||||||
|
Perform Git-based update using GitPython
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_root: Path to the plugin root directory
|
||||||
|
nightly: Whether to update to main branch or latest release
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (success, new_version)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Open the Git repository
|
||||||
|
repo = git.Repo(plugin_root)
|
||||||
|
|
||||||
|
# Fetch latest changes
|
||||||
|
origin = repo.remotes.origin
|
||||||
|
origin.fetch()
|
||||||
|
|
||||||
|
if nightly:
|
||||||
|
# Switch to main branch and pull latest
|
||||||
|
main_branch = 'main'
|
||||||
|
if main_branch not in [branch.name for branch in repo.branches]:
|
||||||
|
# Create local main branch if it doesn't exist
|
||||||
|
repo.create_head(main_branch, origin.refs.main)
|
||||||
|
|
||||||
|
repo.heads[main_branch].checkout()
|
||||||
|
origin.pull(main_branch)
|
||||||
|
|
||||||
|
# Get new commit hash
|
||||||
|
new_version = f"main-{repo.head.commit.hexsha[:7]}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Get latest release tag
|
||||||
|
tags = sorted(repo.tags, key=lambda t: t.commit.committed_datetime, reverse=True)
|
||||||
|
if not tags:
|
||||||
|
logger.error("No tags found in repository")
|
||||||
|
return False, ""
|
||||||
|
|
||||||
|
latest_tag = tags[0]
|
||||||
|
|
||||||
|
# Checkout to latest tag
|
||||||
|
repo.git.checkout(latest_tag.name)
|
||||||
|
|
||||||
|
new_version = latest_tag.name
|
||||||
|
|
||||||
|
logger.info(f"Successfully updated to {new_version}")
|
||||||
|
return True, new_version
|
||||||
|
|
||||||
|
except git.exc.GitError as e:
|
||||||
|
logger.error(f"Git error during update: {e}")
|
||||||
|
return False, ""
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error during Git update: {e}")
|
||||||
|
return False, ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_local_version() -> str:
|
def _get_local_version() -> str:
|
||||||
"""Get local plugin version from pyproject.toml"""
|
"""Get local plugin version from pyproject.toml"""
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ dependencies = [
|
|||||||
"requests",
|
"requests",
|
||||||
"toml",
|
"toml",
|
||||||
"natsort",
|
"natsort",
|
||||||
"msgpack"
|
"msgpack",
|
||||||
|
"GitPython"
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ numpy
|
|||||||
natsort
|
natsort
|
||||||
msgpack
|
msgpack
|
||||||
pyyaml
|
pyyaml
|
||||||
|
GitPython
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ html, body {
|
|||||||
--lora-border: oklch(90% 0.02 256 / 0.15);
|
--lora-border: oklch(90% 0.02 256 / 0.15);
|
||||||
--lora-text: oklch(95% 0.02 256);
|
--lora-text: oklch(95% 0.02 256);
|
||||||
--lora-error: oklch(75% 0.32 29);
|
--lora-error: oklch(75% 0.32 29);
|
||||||
--lora-warning: oklch(var(--lora-warning-l) var(--lora-warning-c) var(--lora-warning-h)); /* Modified to be used with oklch() */
|
--lora-warning: oklch(var(--lora-warning-l) var(--lora-warning-c) var(--lora-warning-h));
|
||||||
--lora-success: oklch(var(--lora-success-l) var(--lora-success-c) var(--lora-success-h)); /* New green success color */
|
--lora-success: oklch(var(--lora-success-l) var(--lora-success-c) var(--lora-success-h));
|
||||||
|
|
||||||
/* Spacing Scale */
|
/* Spacing Scale */
|
||||||
--space-1: calc(8px * 1);
|
--space-1: calc(8px * 1);
|
||||||
|
|||||||
@@ -223,11 +223,6 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update-badge.hidden,
|
|
||||||
.update-badge:not(.visible) {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile adjustments */
|
/* Mobile adjustments */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.app-title {
|
.app-title {
|
||||||
|
|||||||
@@ -172,6 +172,91 @@ body.modal-open {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update Modal specific styles */
|
||||||
|
.update-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
align-items: stretch;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-link {
|
||||||
|
color: var(--lora-accent);
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update progress styles */
|
||||||
|
.update-progress {
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
border: 1px solid var(--lora-border);
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
padding: var(--space-2);
|
||||||
|
margin: var(--space-2) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .update-progress {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: var(--text-color);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .progress-bar {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--lora-accent);
|
||||||
|
width: 0%;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update button states */
|
||||||
|
#updateBtn {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateBtn.updating {
|
||||||
|
background-color: var(--lora-warning);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateBtn.success {
|
||||||
|
background-color: var(--lora-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateBtn.error {
|
||||||
|
background-color: var(--lora-error);
|
||||||
|
}
|
||||||
|
|
||||||
/* Settings styles */
|
/* Settings styles */
|
||||||
.settings-toggle {
|
.settings-toggle {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
|||||||
|
|
||||||
export class UpdateService {
|
export class UpdateService {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.updateCheckInterval = 24 * 60 * 60 * 1000; // 24 hours
|
this.updateCheckInterval = 60 * 60 * 1000; // 1 hour
|
||||||
this.currentVersion = "v0.0.0"; // Initialize with default values
|
this.currentVersion = "v0.0.0"; // Initialize with default values
|
||||||
this.latestVersion = "v0.0.0"; // Initialize with default values
|
this.latestVersion = "v0.0.0"; // Initialize with default values
|
||||||
this.updateInfo = null;
|
this.updateInfo = null;
|
||||||
@@ -13,8 +13,10 @@ export class UpdateService {
|
|||||||
branch: "unknown",
|
branch: "unknown",
|
||||||
commit_date: "unknown"
|
commit_date: "unknown"
|
||||||
};
|
};
|
||||||
this.updateNotificationsEnabled = getStorageItem('show_update_notifications');
|
this.updateNotificationsEnabled = getStorageItem('show_update_notifications', true);
|
||||||
this.lastCheckTime = parseInt(getStorageItem('last_update_check') || '0');
|
this.lastCheckTime = parseInt(getStorageItem('last_update_check') || '0');
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.nightlyMode = getStorageItem('nightly_updates', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
@@ -29,22 +31,43 @@ export class UpdateService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateBtn = document.getElementById('updateBtn');
|
||||||
|
if (updateBtn) {
|
||||||
|
updateBtn.addEventListener('click', () => this.performUpdate());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register event listener for nightly update toggle
|
||||||
|
const nightlyCheckbox = document.getElementById('nightlyUpdateToggle');
|
||||||
|
if (nightlyCheckbox) {
|
||||||
|
nightlyCheckbox.checked = this.nightlyMode;
|
||||||
|
nightlyCheckbox.addEventListener('change', (e) => {
|
||||||
|
this.nightlyMode = e.target.checked;
|
||||||
|
setStorageItem('nightly_updates', e.target.checked);
|
||||||
|
this.updateNightlyWarning();
|
||||||
|
this.updateModalContent();
|
||||||
|
// Re-check for updates when switching channels
|
||||||
|
this.manualCheckForUpdates();
|
||||||
|
});
|
||||||
|
this.updateNightlyWarning();
|
||||||
|
}
|
||||||
|
|
||||||
// Perform update check if needed
|
// Perform update check if needed
|
||||||
this.checkForUpdates().then(() => {
|
this.checkForUpdates().then(() => {
|
||||||
// Ensure badges are updated after checking
|
// Ensure badges are updated after checking
|
||||||
this.updateBadgeVisibility();
|
this.updateBadgeVisibility();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up event listener for update button
|
|
||||||
// const updateToggle = document.getElementById('updateToggleBtn');
|
|
||||||
// if (updateToggle) {
|
|
||||||
// updateToggle.addEventListener('click', () => this.toggleUpdateModal());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Immediately update modal content with current values (even if from default)
|
// Immediately update modal content with current values (even if from default)
|
||||||
this.updateModalContent();
|
this.updateModalContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateNightlyWarning() {
|
||||||
|
const warning = document.getElementById('nightlyWarning');
|
||||||
|
if (warning) {
|
||||||
|
warning.style.display = this.nightlyMode ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async checkForUpdates() {
|
async checkForUpdates() {
|
||||||
// Check if we should perform an update check
|
// Check if we should perform an update check
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -59,8 +82,8 @@ export class UpdateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Call backend API to check for updates
|
// Call backend API to check for updates with nightly flag
|
||||||
const response = await fetch('/api/check-updates');
|
const response = await fetch(`/api/check-updates?nightly=${this.nightlyMode}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
@@ -137,8 +160,8 @@ export class UpdateService {
|
|||||||
const shouldShow = this.updateNotificationsEnabled && this.updateAvailable;
|
const shouldShow = this.updateNotificationsEnabled && this.updateAvailable;
|
||||||
|
|
||||||
if (updateBadge) {
|
if (updateBadge) {
|
||||||
updateBadge.classList.toggle('hidden', !shouldShow);
|
updateBadge.classList.toggle('visible', shouldShow);
|
||||||
console.log("Update badge visibility:", !shouldShow ? "hidden" : "visible");
|
console.log("Update badge visibility:", shouldShow ? "visible" : "hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +180,17 @@ export class UpdateService {
|
|||||||
const newVersionEl = modal.querySelector('.new-version .version-number');
|
const newVersionEl = modal.querySelector('.new-version .version-number');
|
||||||
|
|
||||||
if (currentVersionEl) currentVersionEl.textContent = this.currentVersion;
|
if (currentVersionEl) currentVersionEl.textContent = this.currentVersion;
|
||||||
if (newVersionEl) newVersionEl.textContent = this.latestVersion;
|
|
||||||
|
if (newVersionEl) {
|
||||||
|
newVersionEl.textContent = this.latestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update update button state
|
||||||
|
const updateBtn = modal.querySelector('#updateBtn');
|
||||||
|
if (updateBtn) {
|
||||||
|
updateBtn.classList.toggle('disabled', !this.updateAvailable || this.isUpdating);
|
||||||
|
updateBtn.disabled = !this.updateAvailable || this.isUpdating;
|
||||||
|
}
|
||||||
|
|
||||||
// Update git info
|
// Update git info
|
||||||
const gitInfoEl = modal.querySelector('.git-info');
|
const gitInfoEl = modal.querySelector('.git-info');
|
||||||
@@ -218,6 +251,131 @@ export class UpdateService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async performUpdate() {
|
||||||
|
if (!this.updateAvailable || this.isUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.isUpdating = true;
|
||||||
|
this.updateUpdateUI('updating', 'Updating...');
|
||||||
|
this.showUpdateProgress(true);
|
||||||
|
|
||||||
|
// Update progress
|
||||||
|
this.updateProgress(10, 'Preparing update...');
|
||||||
|
|
||||||
|
const response = await fetch('/api/perform-update', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
nightly: this.nightlyMode
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updateProgress(50, 'Installing update...');
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
this.updateProgress(100, 'Update completed successfully!');
|
||||||
|
this.updateUpdateUI('success', 'Updated!');
|
||||||
|
|
||||||
|
// Show success message and suggest restart
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showUpdateCompleteMessage(data.new_version);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(data.error || 'Update failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Update failed:', error);
|
||||||
|
this.updateUpdateUI('error', 'Update Failed');
|
||||||
|
this.updateProgress(0, `Update failed: ${error.message}`);
|
||||||
|
|
||||||
|
// Hide progress after error
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showUpdateProgress(false);
|
||||||
|
}, 3000);
|
||||||
|
} finally {
|
||||||
|
this.isUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUpdateUI(state, text) {
|
||||||
|
const updateBtn = document.getElementById('updateBtn');
|
||||||
|
const updateBtnText = document.getElementById('updateBtnText');
|
||||||
|
|
||||||
|
if (updateBtn && updateBtnText) {
|
||||||
|
// Remove existing state classes
|
||||||
|
updateBtn.classList.remove('updating', 'success', 'error', 'disabled');
|
||||||
|
|
||||||
|
// Add new state class
|
||||||
|
if (state !== 'normal') {
|
||||||
|
updateBtn.classList.add(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update button text
|
||||||
|
updateBtnText.textContent = text;
|
||||||
|
|
||||||
|
// Update disabled state
|
||||||
|
updateBtn.disabled = (state === 'updating' || state === 'disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showUpdateProgress(show) {
|
||||||
|
const progressContainer = document.getElementById('updateProgress');
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.style.display = show ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(percentage, text) {
|
||||||
|
const progressFill = document.getElementById('updateProgressFill');
|
||||||
|
const progressText = document.getElementById('updateProgressText');
|
||||||
|
|
||||||
|
if (progressFill) {
|
||||||
|
progressFill.style.width = `${percentage}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressText) {
|
||||||
|
progressText.textContent = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showUpdateCompleteMessage(newVersion) {
|
||||||
|
const modal = document.getElementById('updateModal');
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
// Update the modal content to show completion
|
||||||
|
const progressText = document.getElementById('updateProgressText');
|
||||||
|
if (progressText) {
|
||||||
|
progressText.innerHTML = `
|
||||||
|
<div style="text-align: center; color: var(--lora-success);">
|
||||||
|
<i class="fas fa-check-circle" style="margin-right: 8px;"></i>
|
||||||
|
Successfully updated to ${newVersion}!
|
||||||
|
<br><br>
|
||||||
|
<small style="opacity: 0.8;">
|
||||||
|
Please restart ComfyUI to complete the update process.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update current version display
|
||||||
|
this.currentVersion = newVersion;
|
||||||
|
this.updateAvailable = false;
|
||||||
|
|
||||||
|
// Refresh the modal content
|
||||||
|
setTimeout(() => {
|
||||||
|
this.updateModalContent();
|
||||||
|
this.showUpdateProgress(false);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
// Simple markdown parser for changelog items
|
// Simple markdown parser for changelog items
|
||||||
parseMarkdown(text) {
|
parseMarkdown(text) {
|
||||||
if (!text) return '';
|
if (!text) return '';
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="update-toggle" id="updateToggleBtn" title="Check Updates">
|
<div class="update-toggle" id="updateToggleBtn" title="Check Updates">
|
||||||
<i class="fas fa-bell"></i>
|
<i class="fas fa-bell"></i>
|
||||||
<span class="update-badge hidden"></span>
|
<span class="update-badge"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="support-toggle" id="supportToggleBtn" title="Support">
|
<div class="support-toggle" id="supportToggleBtn" title="Support">
|
||||||
<i class="fas fa-heart"></i>
|
<i class="fas fa-heart"></i>
|
||||||
|
|||||||
@@ -476,9 +476,26 @@
|
|||||||
<span class="version-number">v0.0.0</span>
|
<span class="version-number">v0.0.0</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="update-actions">
|
||||||
<a href="https://github.com/willmiao/ComfyUI-Lora-Manager" target="_blank" class="update-link">
|
<a href="https://github.com/willmiao/ComfyUI-Lora-Manager" target="_blank" class="update-link">
|
||||||
<i class="fas fa-external-link-alt"></i> View on GitHub
|
<i class="fas fa-external-link-alt"></i> View on GitHub
|
||||||
</a>
|
</a>
|
||||||
|
<button id="updateBtn" class="primary-btn disabled">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
<span id="updateBtnText">Update Now</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Update Progress Section -->
|
||||||
|
<div class="update-progress" id="updateProgress" style="display: none;">
|
||||||
|
<div class="progress-info">
|
||||||
|
<div class="progress-text" id="updateProgressText">Preparing update...</div>
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="progress-fill" id="updateProgressFill"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="changelog-section">
|
<div class="changelog-section">
|
||||||
|
|||||||
Reference in New Issue
Block a user