refactor: update and remove anything to do with NFO files
Local Docker Build (Dev) / build-dev (push) Successful in 35s

moving to an all DB approach since radarr overwrites .nfo files
This commit is contained in:
2025-11-02 13:43:28 -05:00
parent 041caf0d0d
commit 97a9f3431a
13 changed files with 837 additions and 2252 deletions
+33
View File
@@ -382,6 +382,39 @@
<i class="fas fa-sync"></i> Refresh Stats
</button>
</div>
<div class="tool-card">
<h3><i class="fas fa-upload"></i> Populate Database</h3>
<p>Bulk import data from Radarr/Sonarr into NFOGuard database</p>
<form id="populate-form">
<div class="form-group">
<label>Media Type:</label>
<select id="populate-media-type" required>
<option value="both">Movies & TV Shows</option>
<option value="movies">Movies Only</option>
<option value="tv">TV Shows Only</option>
</select>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-play"></i> Start Population
</button>
</form>
<div id="populate-status" class="scan-status" style="display: none;">
<div class="scan-progress">
<div class="progress-bar">
<div class="progress-fill" id="populate-progress-bar"></div>
</div>
<div class="scan-info">
<span id="populate-current-operation">Running...</span>
<span id="populate-progress-text">In Progress</span>
</div>
</div>
<div id="populate-results" class="stats-display" style="margin-top: 10px;"></div>
<button class="btn btn-secondary btn-sm" onclick="stopPopulatePolling()">
<i class="fas fa-times"></i> Hide Status
</button>
</div>
</div>
</div>
</div>
</main>
+184
View File
@@ -77,6 +77,7 @@ function initializeEventListeners() {
document.getElementById('edit-form').addEventListener('submit', handleEditSubmit);
document.getElementById('bulk-update-form').addEventListener('submit', handleBulkUpdate);
document.getElementById('manual-scan-form').addEventListener('submit', handleManualScan);
document.getElementById('populate-form').addEventListener('submit', handlePopulateDatabase);
}
// API calls
@@ -1742,3 +1743,186 @@ async function bulkDeleteSelected() {
showToast(`❌ Failed to delete ${failCount} episode(s)`, 'error');
}
}
// ---------------------------
// Database Population Functions
// ---------------------------
async function handlePopulateDatabase(event) {
event.preventDefault();
const mediaType = document.getElementById('populate-media-type').value;
// Validate input
if (!mediaType) {
showToast('❌ Please select media type', 'error');
return;
}
// Confirm with user
const confirmMsg = `Are you sure you want to populate the database with ${mediaType}? This will query Radarr/Sonarr and may take several minutes.`;
if (!confirm(confirmMsg)) {
return;
}
try {
// Show populate status
showPopulateStatus();
// Start the population
showToast('🚀 Starting database population...', 'info');
const response = await fetch(`/admin/populate-database?media_type=${mediaType}`, {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.status === 'started') {
showToast('✅ Population started successfully', 'success');
// Start polling for status
startPopulatePolling();
} else {
showToast(`${result.message || 'Population completed'}`, 'info');
hidePopulateStatus();
}
} catch (error) {
console.error('Database population failed:', error);
showToast(`❌ Population failed: ${error.message}`, 'error');
hidePopulateStatus();
}
}
function showPopulateStatus() {
const populateStatus = document.getElementById('populate-status');
const progressBar = document.getElementById('populate-progress-bar');
const operationText = document.getElementById('populate-current-operation');
const progressText = document.getElementById('populate-progress-text');
const resultsDiv = document.getElementById('populate-results');
populateStatus.style.display = 'block';
progressBar.style.width = '0%';
operationText.textContent = 'Initializing...';
progressText.textContent = '0%';
resultsDiv.innerHTML = '';
}
function hidePopulateStatus() {
const populateStatus = document.getElementById('populate-status');
if (populateStatus) {
populateStatus.style.display = 'none';
}
}
function startPopulatePolling() {
// Poll every 2 seconds for populate status
window.populatePollingInterval = setInterval(async () => {
try {
const response = await fetch('/api/populate/status');
if (!response.ok) {
throw new Error('Failed to get populate status');
}
const status = await response.json();
updatePopulateProgress(status);
// Stop polling if population is complete
if (!status.running && status.completed) {
stopPopulatePolling();
showToast('✅ Database population completed!', 'success');
}
} catch (error) {
console.error('Failed to poll populate status:', error);
// Don't show error toast repeatedly, just stop polling
stopPopulatePolling();
}
}, 2000);
}
function stopPopulatePolling() {
if (window.populatePollingInterval) {
clearInterval(window.populatePollingInterval);
window.populatePollingInterval = null;
}
}
function updatePopulateProgress(status) {
const progressBar = document.getElementById('populate-progress-bar');
const operationText = document.getElementById('populate-current-operation');
const progressText = document.getElementById('populate-progress-text');
const resultsDiv = document.getElementById('populate-results');
if (status.error) {
progressBar.style.width = '100%';
progressBar.style.backgroundColor = '#e74c3c';
operationText.textContent = 'Error occurred';
progressText.textContent = 'Failed';
resultsDiv.innerHTML = `<p style="color: #e74c3c;">Error: ${status.error}</p>`;
return;
}
if (!status.running && status.completed) {
progressBar.style.width = '100%';
operationText.textContent = 'Population completed';
progressText.textContent = '100%';
// Display results
let resultsHTML = '<h4>Population Results:</h4>';
if (status.movies && status.movies.stats) {
const m = status.movies.stats;
resultsHTML += `
<div style="margin: 10px 0;">
<strong>Movies:</strong><br>
Total: ${m.total || 0} | Added: ${m.added || 0} | Skipped: ${m.skipped || 0} | Errors: ${m.errors || 0}<br>
Duration: ${m.duration ? m.duration.toFixed(2) : 0}s
</div>
`;
}
if (status.tv && status.tv.stats) {
const t = status.tv.stats;
resultsHTML += `
<div style="margin: 10px 0;">
<strong>TV Shows:</strong><br>
Series: ${t.total_series || 0} | Episodes: ${t.total_episodes || 0}<br>
Added: ${t.added || 0} | Skipped: ${t.skipped || 0} | Errors: ${t.errors || 0}<br>
Duration: ${t.duration ? t.duration.toFixed(2) : 0}s
</div>
`;
}
resultsDiv.innerHTML = resultsHTML;
return;
}
// Update progress based on status
let totalProgress = 0;
let progressDetails = '';
if (status.movies && status.movies.status === 'running') {
totalProgress = 50;
progressDetails = 'Processing movies...';
} else if (status.movies && status.movies.status === 'completed') {
totalProgress = 50;
progressDetails = 'Movies completed, processing TV...';
}
if (status.tv && status.tv.status === 'running') {
totalProgress = 75;
progressDetails = 'Processing TV shows...';
} else if (status.tv && status.tv.status === 'completed') {
totalProgress = 100;
progressDetails = 'Completed';
}
progressBar.style.width = `${totalProgress}%`;
operationText.textContent = progressDetails;
progressText.textContent = `${totalProgress}%`;
}