web: update to interface
This commit is contained in:
@@ -51,6 +51,24 @@
|
||||
<main class="main-content">
|
||||
<!-- Dashboard Tab -->
|
||||
<div class="tab-content active" id="dashboard">
|
||||
<!-- Scan Status Banner -->
|
||||
<div class="scan-status-banner" id="dashboard-scan-status" style="display: none;">
|
||||
<div class="scan-status-content">
|
||||
<div class="scan-status-icon">
|
||||
<i class="fas fa-sync fa-spin"></i>
|
||||
</div>
|
||||
<div class="scan-status-info">
|
||||
<h4>Scan in Progress</h4>
|
||||
<p id="dashboard-scan-text">Processing media files...</p>
|
||||
<div class="scan-progress-mini">
|
||||
<div class="progress-bar-mini">
|
||||
<div class="progress-fill-mini" id="dashboard-scan-progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon movies">
|
||||
@@ -298,7 +316,70 @@
|
||||
<h2><i class="fas fa-tools"></i> Database Tools</h2>
|
||||
</div>
|
||||
|
||||
<!-- Scan Status Display -->
|
||||
<div class="scan-status-card" id="scan-status" style="display: none;">
|
||||
<div class="scan-status-header">
|
||||
<h3><i class="fas fa-sync fa-spin"></i> Scan in Progress</h3>
|
||||
<div class="scan-progress">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="scan-progress"></div>
|
||||
</div>
|
||||
<span class="progress-text" id="scan-progress-text">Initializing...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tools-grid">
|
||||
<!-- Manual Scan Tools -->
|
||||
<div class="tool-card">
|
||||
<h3><i class="fas fa-search"></i> Manual Scan</h3>
|
||||
<p>Run manual scans on your media library</p>
|
||||
<form id="manual-scan-form">
|
||||
<div class="form-group">
|
||||
<label>Scan Type:</label>
|
||||
<select id="scan-type" required>
|
||||
<option value="both">Both (Movies & TV)</option>
|
||||
<option value="movies">Movies Only</option>
|
||||
<option value="tv">TV Series Only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Scan Mode:</label>
|
||||
<select id="scan-mode" required>
|
||||
<option value="smart">Smart (Recommended)</option>
|
||||
<option value="full">Full Scan</option>
|
||||
<option value="incomplete">Incomplete Items Only</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fas fa-play"></i> Start Scan
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tool-card">
|
||||
<h3><i class="fas fa-folder"></i> Custom Directory Scan</h3>
|
||||
<p>Scan a specific directory or path</p>
|
||||
<form id="custom-scan-form">
|
||||
<div class="form-group">
|
||||
<label>Directory Path:</label>
|
||||
<input type="text" id="scan-path" placeholder="/media/Movies/specific-folder" />
|
||||
<small>Enter the full path to scan (will be auto-formatted)</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Scan Type:</label>
|
||||
<select id="custom-scan-type" required>
|
||||
<option value="both">Auto-detect</option>
|
||||
<option value="movies">Movies</option>
|
||||
<option value="tv">TV Series</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fas fa-search"></i> Scan Directory
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tool-card">
|
||||
<h3><i class="fas fa-exchange-alt"></i> Bulk Source Update</h3>
|
||||
<p>Change source for multiple items at once</p>
|
||||
|
||||
@@ -76,6 +76,13 @@ function initializeEventListeners() {
|
||||
// Forms
|
||||
document.getElementById('edit-form').addEventListener('submit', handleEditSubmit);
|
||||
document.getElementById('bulk-update-form').addEventListener('submit', handleBulkUpdate);
|
||||
|
||||
// Manual scan forms
|
||||
document.getElementById('manual-scan-form').addEventListener('submit', handleManualScan);
|
||||
document.getElementById('custom-scan-form').addEventListener('submit', handleCustomScan);
|
||||
|
||||
// Custom directory input auto-formatting
|
||||
document.getElementById('custom-directory').addEventListener('input', handleDirectoryFormatting);
|
||||
}
|
||||
|
||||
// API calls
|
||||
@@ -1435,3 +1442,186 @@ async function logout() {
|
||||
showToast('❌ Logout error', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// Manual Scan Functions
|
||||
// ===========================
|
||||
|
||||
async function handleManualScan(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const scanType = document.getElementById('scan-type').value;
|
||||
const scanMode = document.getElementById('scan-mode').value;
|
||||
|
||||
if (!scanType || !scanMode) {
|
||||
showToast('Please select both scan type and mode', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showToast('🔍 Starting manual scan...', 'info');
|
||||
updateScanStatus('Starting manual scan...', true);
|
||||
|
||||
const result = await apiCall('/manual/scan', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
scan_type: scanType,
|
||||
scan_mode: scanMode
|
||||
})
|
||||
});
|
||||
|
||||
showToast(`✅ Manual scan initiated: ${result.message}`, 'success');
|
||||
|
||||
// Start polling for scan status
|
||||
startScanStatusPolling();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Manual scan failed:', error);
|
||||
showToast(`❌ Manual scan failed: ${error.message}`, 'error');
|
||||
updateScanStatus('Scan failed', false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCustomScan(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const customDirectory = document.getElementById('custom-directory').value.trim();
|
||||
const customScanMode = document.getElementById('custom-scan-mode').value;
|
||||
|
||||
if (!customDirectory) {
|
||||
showToast('Please enter a directory path', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!customScanMode) {
|
||||
showToast('Please select a scan mode', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showToast('🔍 Starting custom directory scan...', 'info');
|
||||
updateScanStatus('Starting custom directory scan...', true);
|
||||
|
||||
const result = await apiCall('/manual/scan', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
directory: customDirectory,
|
||||
scan_mode: customScanMode
|
||||
})
|
||||
});
|
||||
|
||||
showToast(`✅ Custom scan initiated: ${result.message}`, 'success');
|
||||
|
||||
// Start polling for scan status
|
||||
startScanStatusPolling();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Custom scan failed:', error);
|
||||
showToast(`❌ Custom scan failed: ${error.message}`, 'error');
|
||||
updateScanStatus('Scan failed', false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDirectoryFormatting(event) {
|
||||
const input = event.target;
|
||||
let value = input.value;
|
||||
|
||||
// Auto-format common directory patterns
|
||||
if (value && !value.startsWith('/')) {
|
||||
// Add leading slash for absolute paths
|
||||
value = '/' + value;
|
||||
input.value = value;
|
||||
}
|
||||
|
||||
// Remove multiple consecutive slashes
|
||||
value = value.replace(/\/+/g, '/');
|
||||
|
||||
// Remove trailing slash unless it's just '/'
|
||||
if (value.length > 1 && value.endsWith('/')) {
|
||||
value = value.slice(0, -1);
|
||||
}
|
||||
|
||||
input.value = value;
|
||||
}
|
||||
|
||||
// ===========================
|
||||
// Scan Status Functions
|
||||
// ===========================
|
||||
|
||||
let scanStatusInterval = null;
|
||||
|
||||
function updateScanStatus(message, isActive = false) {
|
||||
const statusBanner = document.getElementById('scan-status-banner');
|
||||
const statusText = document.getElementById('scan-status-text');
|
||||
|
||||
if (!statusBanner || !statusText) {
|
||||
console.log('Scan status elements not found, skipping update');
|
||||
return;
|
||||
}
|
||||
|
||||
statusText.textContent = message;
|
||||
|
||||
if (isActive) {
|
||||
statusBanner.className = 'scan-status-banner active';
|
||||
statusBanner.style.display = 'block';
|
||||
} else {
|
||||
statusBanner.className = 'scan-status-banner';
|
||||
// Don't hide completely, just mark as inactive
|
||||
setTimeout(() => {
|
||||
if (statusBanner.className === 'scan-status-banner') {
|
||||
statusBanner.style.display = 'none';
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkScanStatus() {
|
||||
try {
|
||||
const status = await apiCall('/api/scan/status');
|
||||
|
||||
if (status.scanning) {
|
||||
updateScanStatus(status.message || 'Scan in progress...', true);
|
||||
return true; // Continue polling
|
||||
} else {
|
||||
updateScanStatus(status.message || 'No scan in progress', false);
|
||||
return false; // Stop polling
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to check scan status:', error);
|
||||
updateScanStatus('Unable to check scan status', false);
|
||||
return false; // Stop polling on error
|
||||
}
|
||||
}
|
||||
|
||||
function startScanStatusPolling() {
|
||||
// Clear any existing interval
|
||||
if (scanStatusInterval) {
|
||||
clearInterval(scanStatusInterval);
|
||||
}
|
||||
|
||||
// Check status every 2 seconds
|
||||
scanStatusInterval = setInterval(async () => {
|
||||
const shouldContinue = await checkScanStatus();
|
||||
|
||||
if (!shouldContinue) {
|
||||
clearInterval(scanStatusInterval);
|
||||
scanStatusInterval = null;
|
||||
|
||||
// Refresh dashboard data after scan completes
|
||||
if (currentTab === 'dashboard') {
|
||||
setTimeout(loadDashboard, 1000);
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
// Initial status check
|
||||
checkScanStatus();
|
||||
}
|
||||
|
||||
function stopScanStatusPolling() {
|
||||
if (scanStatusInterval) {
|
||||
clearInterval(scanStatusInterval);
|
||||
scanStatusInterval = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user