fix loading icon
This commit is contained in:
parent
7cc0d7c7a5
commit
f9102d7bcd
@ -69,17 +69,41 @@ RESCAN_LOCK = "rescan"
|
||||
DOWNLOAD_LOCK = "download"
|
||||
CLEANUP_LOCK = "cleanup"
|
||||
|
||||
# Simple in-memory process lock system
|
||||
_active_locks = {}
|
||||
|
||||
def is_process_running(lock_name):
|
||||
"""Placeholder function for process lock checking."""
|
||||
return False
|
||||
"""Check if a process is currently running (locked)."""
|
||||
return lock_name in _active_locks
|
||||
|
||||
def acquire_lock(lock_name, locked_by="system"):
|
||||
"""Acquire a process lock."""
|
||||
if lock_name in _active_locks:
|
||||
raise ProcessLockError(f"Process {lock_name} is already running")
|
||||
_active_locks[lock_name] = {
|
||||
'locked_by': locked_by,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
def release_lock(lock_name):
|
||||
"""Release a process lock."""
|
||||
if lock_name in _active_locks:
|
||||
del _active_locks[lock_name]
|
||||
|
||||
def with_process_lock(lock_name, timeout_minutes=30):
|
||||
"""Placeholder decorator for process locking."""
|
||||
"""Decorator for process locking."""
|
||||
def decorator(f):
|
||||
from functools import wraps
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
# Extract locked_by from kwargs if provided
|
||||
locked_by = kwargs.pop('_locked_by', 'system')
|
||||
|
||||
try:
|
||||
acquire_lock(lock_name, locked_by)
|
||||
return f(*args, **kwargs)
|
||||
finally:
|
||||
release_lock(lock_name)
|
||||
return decorated_function
|
||||
return decorator
|
||||
|
||||
|
||||
@ -869,10 +869,6 @@ body {
|
||||
.process-status {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@ -1441,16 +1437,22 @@ body {
|
||||
}
|
||||
|
||||
.status-indicator i {
|
||||
font-size: 12px;
|
||||
font-size: 24px; /* 2x bigger: 12px -> 24px */
|
||||
transition: all var(--animation-duration-normal) var(--animation-easing-standard);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
margin-left: 2px;
|
||||
/* Rescan icon specific styling */
|
||||
#rescan-status i {
|
||||
color: var(--color-text-disabled); /* Gray when idle */
|
||||
}
|
||||
|
||||
#rescan-status.running i {
|
||||
color: #22c55e; /* Green when running */
|
||||
animation: iconPulse 2s infinite;
|
||||
}
|
||||
|
||||
/* Status text removed - using tooltips only */
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
@ -1472,7 +1474,6 @@ body {
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
@ -1485,6 +1486,19 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes iconPulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1) rotate(0deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
transform: scale(1.1) rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process status in mobile view */
|
||||
@media (max-width: 768px) {
|
||||
.process-status {
|
||||
@ -1499,12 +1513,8 @@ body {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status-indicator i {
|
||||
font-size: 14px;
|
||||
font-size: 20px; /* Maintain 2x scale for mobile: was 14px -> 20px */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -896,30 +896,45 @@ class AniWorldApp {
|
||||
const statusDot = statusElement.querySelector('.status-dot');
|
||||
if (!statusDot) return;
|
||||
|
||||
// Remove all status classes
|
||||
// Remove all status classes from both dot and element
|
||||
statusDot.classList.remove('idle', 'running', 'error');
|
||||
statusElement.classList.remove('running', 'error', 'idle');
|
||||
|
||||
// Capitalize process name for display
|
||||
const displayName = processName.charAt(0).toUpperCase() + processName.slice(1);
|
||||
|
||||
if (hasError) {
|
||||
statusDot.classList.add('error');
|
||||
statusElement.title = `${processName} error - click for details`;
|
||||
statusElement.classList.add('error');
|
||||
statusElement.title = `${displayName} error - click for details`;
|
||||
} else if (isRunning) {
|
||||
statusDot.classList.add('running');
|
||||
statusElement.title = `${processName} is running...`;
|
||||
statusElement.classList.add('running');
|
||||
statusElement.title = `${displayName} is running...`;
|
||||
} else {
|
||||
statusDot.classList.add('idle');
|
||||
statusElement.title = `${processName} is idle`;
|
||||
statusElement.classList.add('idle');
|
||||
statusElement.title = `${displayName} is idle`;
|
||||
}
|
||||
}
|
||||
|
||||
async checkProcessLocks() {
|
||||
try {
|
||||
const response = await this.makeAuthenticatedRequest('/api/process/locks/status');
|
||||
if (!response) return;
|
||||
if (!response) {
|
||||
// If no response, set status as idle
|
||||
this.updateProcessStatus('rescan', false);
|
||||
this.updateProcessStatus('download', false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if response is actually JSON and not HTML (login page)
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (!contentType || !contentType.includes('application/json')) {
|
||||
console.warn('Process locks API returned non-JSON response, likely authentication issue');
|
||||
// Set status as idle if we can't get proper response
|
||||
this.updateProcessStatus('rescan', false);
|
||||
this.updateProcessStatus('download', false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -935,25 +950,40 @@ class AniWorldApp {
|
||||
if (rescanBtn) {
|
||||
if (locks.rescan?.is_locked) {
|
||||
rescanBtn.disabled = true;
|
||||
rescanBtn.querySelector('span').textContent = 'Scanning...';
|
||||
const span = rescanBtn.querySelector('span');
|
||||
if (span) span.textContent = 'Scanning...';
|
||||
} else {
|
||||
rescanBtn.disabled = false;
|
||||
rescanBtn.querySelector('span').textContent = 'Rescan';
|
||||
const span = rescanBtn.querySelector('span');
|
||||
if (span) span.textContent = 'Rescan';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If API returns error, set status as idle
|
||||
console.warn('Process locks API returned error:', data.error);
|
||||
this.updateProcessStatus('rescan', false);
|
||||
this.updateProcessStatus('download', false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking process locks:', error);
|
||||
// On error, set status as idle
|
||||
this.updateProcessStatus('rescan', false);
|
||||
this.updateProcessStatus('download', false);
|
||||
}
|
||||
}
|
||||
|
||||
startProcessStatusMonitoring() {
|
||||
// Initial check on page load
|
||||
this.checkProcessLocks();
|
||||
|
||||
// Check process status every 5 seconds
|
||||
setInterval(() => {
|
||||
if (this.isConnected) {
|
||||
this.checkProcessLocks();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
console.log('Process status monitoring started');
|
||||
}
|
||||
|
||||
async showConfigModal() {
|
||||
|
||||
@ -24,14 +24,12 @@
|
||||
<div class="header-actions">
|
||||
<!-- Process Status Indicators -->
|
||||
<div class="process-status" id="process-status">
|
||||
<div class="status-indicator" id="rescan-status">
|
||||
<div class="status-indicator" id="rescan-status" title="Scan is idle">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
<span class="status-text">Scan</span>
|
||||
<div class="status-dot idle"></div>
|
||||
</div>
|
||||
<div class="status-indicator" id="download-status">
|
||||
<div class="status-indicator" id="download-status" title="Download is idle">
|
||||
<i class="fas fa-download"></i>
|
||||
<span class="status-text">Download</span>
|
||||
<div class="status-dot idle"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user