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

This commit is contained in:
2025-10-28 15:07:03 -04:00
parent 1916dc2646
commit afdbe1f755
6 changed files with 610 additions and 1469 deletions
+28 -467
View File
@@ -49,32 +49,10 @@ body {
text-align: center;
}
/* Header Logo and Text Layout */
.header-logo {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.header-logo .logo {
height: 60px;
width: auto;
/* Clean display for new logo */
object-fit: contain;
}
.header-text {
display: flex;
flex-direction: column;
align-items: flex-start;
text-align: left;
}
.header-content h1 {
font-size: 2rem;
font-weight: 300;
margin-bottom: 0.25rem;
margin-bottom: 0.5rem;
}
.header-content h1 i {
@@ -84,28 +62,6 @@ body {
.header-content p {
opacity: 0.9;
font-size: 1rem;
margin: 0;
}
/* Responsive logo layout */
@media (max-width: 768px) {
.header-logo {
flex-direction: column;
gap: 0.5rem;
}
.header-text {
align-items: center;
text-align: center;
}
.header-logo .logo {
height: 45px;
}
.header-content h1 {
font-size: 1.5rem;
}
}
/* Authentication Status */
@@ -888,454 +844,59 @@ body {
.justify-content-between { justify-content: space-between; }
.align-items-center { align-items: center; }
/* Database Admin Tools Styles */
.query-results {
/* Manual Scan Styles */
.scan-status {
margin-top: 1rem;
padding: 1rem;
background: #f8f9fa;
background-color: var(--light-color);
border: 1px solid var(--border-color);
border-radius: 0.375rem;
}
.query-info {
padding: 1rem;
border-radius: 0.375rem;
.scan-progress {
margin-bottom: 1rem;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.875rem;
}
.query-info.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.query-info.error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
.results-table {
.progress-bar {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
background: white;
height: 1.5rem;
background-color: #e9ecef;
border-radius: 0.375rem;
overflow: hidden;
box-shadow: var(--shadow);
}
.results-table th {
background-color: var(--dark-color);
color: white;
padding: 0.75rem;
text-align: left;
font-weight: 600;
}
.results-table td {
padding: 0.75rem;
border-bottom: 1px solid var(--border-color);
vertical-align: top;
}
.results-table tr:hover {
background-color: #f8f9fa;
}
.results-table td em {
color: var(--text-muted);
font-style: italic;
}
.query-results .table-container,
.admin-results .table-container {
max-height: 400px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
}
/* NFO Repair Table Styling */
.nfo-repair-table-container {
background: white;
border-radius: 0.5rem;
box-shadow: var(--shadow);
overflow-x: auto;
overflow-y: auto;
max-height: 600px;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
}
.nfo-repair-table {
width: 100%;
min-width: 800px;
border-collapse: collapse;
font-size: 0.875rem;
background: white;
}
.nfo-repair-table th {
background-color: var(--dark-color);
color: white;
padding: 0.75rem 0.5rem;
text-align: left;
font-weight: 600;
position: sticky;
top: 0;
z-index: 10;
}
.nfo-repair-table td {
padding: 0.75rem 0.5rem;
border-bottom: 1px solid var(--border-color);
vertical-align: top;
}
.nfo-repair-table tr:hover {
background-color: #f8f9fa;
}
.media-type-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.media-type-badge.episode {
background-color: #e3f2fd;
color: #1976d2;
}
.media-type-badge.movie {
background-color: #f3e5f5;
color: #7b1fa2;
}
.status-ready {
color: #2e7d32;
font-weight: 600;
font-size: 0.75rem;
}
.status-missing {
color: #d32f2f;
font-weight: 600;
font-size: 0.75rem;
}
.text-center {
text-align: center;
}
.text-muted {
color: #6c757d;
}
.nfo-fix-actions {
margin-top: 1rem;
padding: 1rem;
background-color: #f8f9fa;
border-radius: 0.5rem;
text-align: center;
}
.nfo-fix-actions .btn {
margin-bottom: 0.5rem;
}
.admin-tools {
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary-color), var(--success-color));
transition: width 0.3s ease;
width: 0%;
}
.scan-info {
display: flex;
flex-direction: column;
gap: 0.5rem;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
}
.admin-tools .btn {
text-align: left;
justify-content: flex-start;
}
.admin-results {
margin-top: 1rem;
padding: 1rem;
background: #f8f9fa;
border: 1px solid var(--border-color);
border-radius: 0.375rem;
}
.scan-summary,
.fix-summary {
padding: 1rem;
border-radius: 0.375rem;
margin-bottom: 1rem;
}
.scan-summary {
background-color: #e7f3ff;
border: 1px solid #b3d9ff;
}
.fix-summary.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.fix-errors {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
padding: 1rem;
border-radius: 0.375rem;
margin-top: 1rem;
}
.fix-errors ul {
margin: 0.5rem 0 0 1rem;
}
.missing-episodes {
margin-top: 1rem;
}
.loading {
text-align: center;
padding: 2rem;
.scan-info span:first-child {
color: var(--text-muted);
font-style: italic;
}
.loading::before {
content: "⏳ ";
}
.success {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
padding: 1rem;
border-radius: 0.375rem;
margin: 1rem 0;
}
.error {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
padding: 1rem;
border-radius: 0.375rem;
margin: 1rem 0;
}
/* Make text areas more distinctive */
textarea {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.875rem;
resize: vertical;
}
/* Button variations for admin tools */
.btn.btn-info {
background-color: #17a2b8;
border-color: #17a2b8;
color: white;
}
.btn.btn-info:hover {
background-color: #138496;
border-color: #117a8b;
}
/* Missing IMDb Items Styles */
.missing-imdb-table-container {
overflow-x: auto;
max-height: 600px;
border: 1px solid var(--border-color);
border-radius: 8px;
margin: 1rem 0;
}
.missing-imdb-table {
width: 100%;
border-collapse: collapse;
min-width: 800px;
}
.missing-imdb-table thead {
background-color: var(--bg-secondary);
position: sticky;
top: 0;
z-index: 1;
}
.missing-imdb-table th,
.missing-imdb-table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
vertical-align: top;
}
.missing-imdb-table th {
.scan-info span:last-child {
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
}
.missing-imdb-table td {
color: var(--text-secondary);
}
.missing-imdb-table .file-path {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.85rem;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.media-type-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.media-type-badge.tv-series {
background-color: #e3f2fd;
color: #1976d2;
}
.media-type-badge.movie {
background-color: #fff3e0;
color: #f57c00;
}
.missing-imdb-actions {
background-color: var(--bg-secondary);
padding: 1rem;
border-radius: 8px;
margin-top: 1rem;
}
.missing-imdb-actions ul {
margin: 0.5rem 0 0 1rem;
padding: 0;
}
.missing-imdb-actions li {
margin-bottom: 0.5rem;
}
.missing-imdb-actions code {
background-color: var(--bg-primary);
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.85rem;
border: 1px solid var(--border-color);
}
.btn-small {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
min-width: auto;
}
/* NFO Statistics Styling */
.nfo-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 1rem 0;
}
.stat-box {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
text-align: center;
}
.stat-number {
font-size: 2rem;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 0.5rem;
}
.stat-label {
.form-group small {
display: block;
margin-top: 0.25rem;
color: var(--text-muted);
font-size: 0.875rem;
color: var(--text-secondary);
font-weight: 500;
}
.recommendation-box {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
.recommendation-box ul {
margin: 0.5rem 0 0 1rem;
padding: 0;
}
.recommendation-box li {
margin-bottom: 0.5rem;
}
.success-box {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
text-align: center;
font-weight: 600;
}
/* Responsive adjustments for admin tools */
@media (max-width: 768px) {
.table-container {
max-height: 300px;
}
.results-table {
font-size: 0.75rem;
}
.results-table th,
.results-table td {
padding: 0.5rem;
}
.admin-tools {
gap: 0.75rem;
}
.missing-imdb-table {
min-width: 600px;
}
.missing-imdb-table th,
.missing-imdb-table td {
padding: 0.5rem;
font-size: 0.85rem;
}
.missing-imdb-table .file-path {
max-width: 200px;
}
.btn-sm {
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
}
+59 -146
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NFOGuard - Database Management</title>
<link rel="stylesheet" href="/static/css/styles.css?v=2.8.2-20241026-nfo-table-redesign">
<link rel="stylesheet" href="/static/css/styles.css?v=manual-scan-ui">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body>
@@ -12,12 +12,8 @@
<!-- Header -->
<header class="app-header">
<div class="header-content">
<div class="header-logo">
<div class="header-text">
<h1>NFOGuard</h1>
<p>Database Management & Reporting</p>
</div>
</div>
<h1><i class="fas fa-shield-alt"></i> NFOGuard</h1>
<p>Database Management & Reporting</p>
</div>
<div class="auth-status" id="auth-status" style="display: none;">
<span class="auth-user">
@@ -50,24 +46,6 @@
<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">
@@ -315,31 +293,50 @@
<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-exchange-alt"></i> Bulk Source Update</h3>
<p>Change source for multiple items at once</p>
<form id="bulk-update-form">
<div class="form-group">
<label>Media Type:</label>
<select id="bulk-media-type" required>
<option value="">Select type...</option>
<option value="movies">Movies</option>
<option value="episodes">Episodes</option>
</select>
</div>
<div class="form-group">
<label>From Source:</label>
<input type="text" id="bulk-old-source" placeholder="e.g., no_valid_date_source" required>
</div>
<div class="form-group">
<label>To Source:</label>
<select id="bulk-new-source" required>
<option value="">Select new source...</option>
<option value="airdate">Air Date</option>
<option value="digital_release">Digital Release</option>
<option value="manual">Manual</option>
<option value="radarr:db.history.import">Radarr Import</option>
<option value="sonarr:history.import">Sonarr Import</option>
</select>
</div>
<button type="submit" class="btn btn-warning">
<i class="fas fa-exchange-alt"></i> Update Sources
</button>
</form>
</div>
<div class="tool-card">
<h3><i class="fas fa-search"></i> Manual Scan</h3>
<p>Run manual scans on your media library</p>
<p>Scan specific folders or perform full library scans</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="both">TV Shows & Movies</option>
<option value="tv">TV Shows Only</option>
<option value="movies">Movies Only</option>
<option value="tv">TV Series Only</option>
</select>
</div>
<div class="form-group">
@@ -347,39 +344,34 @@
<select id="scan-mode" required>
<option value="smart">Smart (Recommended)</option>
<option value="full">Full Scan</option>
<option value="incomplete">Incomplete Items Only</option>
<option value="incomplete">Incomplete Only</option>
</select>
</div>
<button type="submit" class="btn-primary">
<div class="form-group">
<label>Specific Path (Optional):</label>
<input type="text" id="scan-path" placeholder="e.g., /mnt/unionfs/Media/TV/Series Name" title="Leave empty for full library scan">
<small>Leave empty to scan entire library</small>
</div>
<button type="submit" class="btn 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 id="scan-status" class="scan-status" style="display: none;">
<div class="scan-progress">
<div class="progress-bar">
<div class="progress-fill" id="scan-progress-bar"></div>
</div>
<div class="scan-info">
<span id="scan-current-operation">Initializing...</span>
<span id="scan-progress-text">0%</span>
</div>
</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 class="btn btn-secondary btn-sm" onclick="stopScanPolling()">
<i class="fas fa-times"></i> Hide Status
</button>
</form>
</div>
</div>
<div class="tool-card">
<h3><i class="fas fa-database"></i> Database Statistics</h3>
<p>View detailed database information</p>
@@ -390,85 +382,6 @@
<i class="fas fa-sync"></i> Refresh Stats
</button>
</div>
<!-- Database Query Tool -->
<div class="tool-card">
<h3><i class="fas fa-terminal"></i> Database Query Tool</h3>
<p>Execute custom SQL queries on the NFOGuard database</p>
<form id="database-query-form">
<div class="form-group">
<label>Quick Queries:</label>
<select id="quick-query-select" onchange="loadQuickQuery()">
<option value="">Select a predefined query...</option>
</select>
</div>
<div class="form-group">
<label>SQL Query:</label>
<textarea id="sql-query" rows="4" placeholder="SELECT * FROM episodes WHERE dateadded IS NULL LIMIT 10"></textarea>
<small>Query will be automatically limited to 100 results for safety</small>
</div>
<div class="form-group">
<label>Result Limit:</label>
<select id="query-limit">
<option value="10">10 rows</option>
<option value="50">50 rows</option>
<option value="100" selected>100 rows</option>
</select>
</div>
<button type="submit" class="btn btn-info">
<i class="fas fa-play"></i> Execute Query
</button>
</form>
<div id="query-results" class="query-results" style="display: none;">
<h4>Query Results:</h4>
<div id="query-results-content"></div>
</div>
</div>
<!-- NFO File Repair Tool -->
<div class="tool-card">
<h3><i class="fas fa-file-medical"></i> NFO File Repair</h3>
<p>Fix missing &lt;dateadded&gt; elements in NFO files</p>
<div class="form-group">
<button class="btn btn-warning" onclick="checkMissingNFODates()">
<i class="fas fa-search"></i> Scan for Missing dateadded in NFO Files
</button>
</div>
<div id="nfo-scan-results" style="display: none;">
<div id="nfo-scan-content"></div>
<div class="form-group">
<button class="btn btn-success" onclick="fixAllMissingNFODates()">
<i class="fas fa-wrench"></i> Fix All Missing dateadded Elements
</button>
<button class="btn btn-secondary" onclick="fixSpecificSeries()">
<i class="fas fa-cog"></i> Fix Specific Series
</button>
</div>
</div>
</div>
<!-- Database Admin Tools -->
<div class="tool-card">
<h3><i class="fas fa-user-shield"></i> Database Admin</h3>
<p>Advanced database management tools</p>
<div class="admin-tools">
<button class="btn btn-info" onclick="showEpisodesMissingNFODateadded()">
<i class="fas fa-exclamation-triangle"></i> Episodes Missing NFO dateadded
</button>
<button class="btn btn-warning" onclick="showMissingImdbItems()">
<i class="fas fa-search"></i> Items Missing IMDb IDs
</button>
<button class="btn btn-secondary" onclick="exportDatabaseReport()">
<i class="fas fa-download"></i> Export Database Report
</button>
<button class="btn btn-secondary" onclick="showRecentWebhookActivity()">
<i class="fas fa-clock"></i> Recent Webhook Activity
</button>
</div>
<div id="admin-results" class="admin-results" style="display: none;">
<div id="admin-results-content"></div>
</div>
</div>
</div>
</div>
</main>
@@ -536,6 +449,6 @@
<!-- Toast Notifications -->
<div class="toast-container" id="toast-container"></div>
<script src="/static/js/app.js?v=3.0.1-nfo-table-redesign"></script>
<script src="/static/js/app.js?v=manual-scan-ui"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff