Add MP4 scan progress visibility in UI
- Add broadcast_scan_started, broadcast_scan_progress, broadcast_scan_completed to WebSocketService - Inject WebSocketService into AnimeService for real-time scan progress broadcasts - Add CSS styles for scan progress overlay with spinner, stats, and completion state - Update app.js to handle scan events and display progress overlay - Add unit tests for new WebSocket broadcast methods - All 1022 tests passing
This commit is contained in:
@@ -202,19 +202,22 @@ class AniWorldApp {
|
||||
this.updateConnectionStatus();
|
||||
});
|
||||
|
||||
// Scan events
|
||||
this.socket.on('scan_started', () => {
|
||||
this.showStatus('Scanning series...', true);
|
||||
// Scan events - handle new detailed scan progress overlay
|
||||
this.socket.on('scan_started', (data) => {
|
||||
console.log('Scan started:', data);
|
||||
this.showScanProgressOverlay(data);
|
||||
this.updateProcessStatus('rescan', true);
|
||||
});
|
||||
|
||||
this.socket.on('scan_progress', (data) => {
|
||||
this.updateStatus(`Scanning: ${data.folder} (${data.counter})`);
|
||||
console.log('Scan progress:', data);
|
||||
this.updateScanProgressOverlay(data);
|
||||
});
|
||||
|
||||
// Handle both 'scan_completed' (legacy) and 'scan_complete' (new backend)
|
||||
const handleScanComplete = () => {
|
||||
this.hideStatus();
|
||||
const handleScanComplete = (data) => {
|
||||
console.log('Scan completed:', data);
|
||||
this.hideScanProgressOverlay(data);
|
||||
this.showToast('Scan completed successfully', 'success');
|
||||
this.updateProcessStatus('rescan', false);
|
||||
this.loadSeries();
|
||||
@@ -1074,6 +1077,157 @@ class AniWorldApp {
|
||||
document.getElementById('status-panel').classList.add('hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the scan progress overlay with spinner and initial state
|
||||
* @param {Object} data - Scan started event data
|
||||
*/
|
||||
showScanProgressOverlay(data) {
|
||||
// Remove existing overlay if present
|
||||
this.removeScanProgressOverlay();
|
||||
|
||||
// Create overlay element
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'scan-progress-overlay';
|
||||
overlay.className = 'scan-progress-overlay';
|
||||
overlay.innerHTML = `
|
||||
<div class="scan-progress-container">
|
||||
<div class="scan-progress-header">
|
||||
<h3>
|
||||
<span class="scan-progress-spinner"></span>
|
||||
<i class="fas fa-check-circle scan-completed-icon"></i>
|
||||
<span class="scan-title-text">Scanning Library</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="scan-progress-stats">
|
||||
<div class="scan-stat">
|
||||
<span class="scan-stat-value" id="scan-directories-count">0</span>
|
||||
<span class="scan-stat-label">Directories</span>
|
||||
</div>
|
||||
<div class="scan-stat">
|
||||
<span class="scan-stat-value" id="scan-files-count">0</span>
|
||||
<span class="scan-stat-label">Series Found</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scan-current-directory" id="scan-current-directory">
|
||||
<span class="scan-current-directory-label">Scanning:</span>
|
||||
<span id="scan-current-path">${this.escapeHtml(data?.directory || 'Initializing...')}</span>
|
||||
</div>
|
||||
<div class="scan-elapsed-time hidden" id="scan-elapsed-time">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span id="scan-elapsed-value">0.0s</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Trigger animation by adding visible class after a brief delay
|
||||
requestAnimationFrame(() => {
|
||||
overlay.classList.add('visible');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the scan progress overlay with current progress
|
||||
* @param {Object} data - Scan progress event data
|
||||
*/
|
||||
updateScanProgressOverlay(data) {
|
||||
const overlay = document.getElementById('scan-progress-overlay');
|
||||
if (!overlay) return;
|
||||
|
||||
// Update directories count
|
||||
const dirCount = document.getElementById('scan-directories-count');
|
||||
if (dirCount && data.directories_scanned !== undefined) {
|
||||
dirCount.textContent = data.directories_scanned;
|
||||
}
|
||||
|
||||
// Update files/series count
|
||||
const filesCount = document.getElementById('scan-files-count');
|
||||
if (filesCount && data.files_found !== undefined) {
|
||||
filesCount.textContent = data.files_found;
|
||||
}
|
||||
|
||||
// Update current directory (truncate if too long)
|
||||
const currentPath = document.getElementById('scan-current-path');
|
||||
if (currentPath && data.current_directory) {
|
||||
const maxLength = 50;
|
||||
let displayPath = data.current_directory;
|
||||
if (displayPath.length > maxLength) {
|
||||
displayPath = '...' + displayPath.slice(-maxLength + 3);
|
||||
}
|
||||
currentPath.textContent = displayPath;
|
||||
currentPath.title = data.current_directory; // Full path on hover
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the scan progress overlay with completion summary
|
||||
* @param {Object} data - Scan completed event data
|
||||
*/
|
||||
hideScanProgressOverlay(data) {
|
||||
const overlay = document.getElementById('scan-progress-overlay');
|
||||
if (!overlay) return;
|
||||
|
||||
const container = overlay.querySelector('.scan-progress-container');
|
||||
if (container) {
|
||||
container.classList.add('completed');
|
||||
}
|
||||
|
||||
// Update title
|
||||
const titleText = overlay.querySelector('.scan-title-text');
|
||||
if (titleText) {
|
||||
titleText.textContent = 'Scan Complete';
|
||||
}
|
||||
|
||||
// Update final stats
|
||||
if (data) {
|
||||
const dirCount = document.getElementById('scan-directories-count');
|
||||
if (dirCount && data.total_directories !== undefined) {
|
||||
dirCount.textContent = data.total_directories;
|
||||
}
|
||||
|
||||
const filesCount = document.getElementById('scan-files-count');
|
||||
if (filesCount && data.total_files !== undefined) {
|
||||
filesCount.textContent = data.total_files;
|
||||
}
|
||||
|
||||
// Show elapsed time
|
||||
const elapsedTimeEl = document.getElementById('scan-elapsed-time');
|
||||
const elapsedValueEl = document.getElementById('scan-elapsed-value');
|
||||
if (elapsedTimeEl && elapsedValueEl && data.elapsed_seconds !== undefined) {
|
||||
elapsedValueEl.textContent = `${data.elapsed_seconds.toFixed(1)}s`;
|
||||
elapsedTimeEl.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Update current directory to show completion message
|
||||
const currentPath = document.getElementById('scan-current-path');
|
||||
if (currentPath) {
|
||||
currentPath.textContent = 'Scan finished successfully';
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-dismiss after 3 seconds
|
||||
setTimeout(() => {
|
||||
this.removeScanProgressOverlay();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the scan progress overlay from the DOM
|
||||
*/
|
||||
removeScanProgressOverlay() {
|
||||
const overlay = document.getElementById('scan-progress-overlay');
|
||||
if (overlay) {
|
||||
overlay.classList.remove('visible');
|
||||
// Wait for fade out animation before removing
|
||||
setTimeout(() => {
|
||||
if (overlay.parentElement) {
|
||||
overlay.remove();
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
document.getElementById('loading-overlay').classList.remove('hidden');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user