web: update to interface
Local Docker Build (Dev) / build-dev (push) Successful in 5s

This commit is contained in:
2025-10-22 16:19:35 -04:00
parent 696ac7455d
commit ce17ae9ece
2 changed files with 271 additions and 0 deletions
+81
View File
@@ -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>
+190
View File
@@ -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;
}
}