This commit is contained in:
@@ -443,6 +443,39 @@
|
|||||||
<i class="fas fa-sync"></i> Refresh Stats
|
<i class="fas fa-sync"></i> Refresh Stats
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ function initializeEventListeners() {
|
|||||||
document.getElementById('edit-form').addEventListener('submit', handleEditSubmit);
|
document.getElementById('edit-form').addEventListener('submit', handleEditSubmit);
|
||||||
document.getElementById('bulk-update-form').addEventListener('submit', handleBulkUpdate);
|
document.getElementById('bulk-update-form').addEventListener('submit', handleBulkUpdate);
|
||||||
document.getElementById('manual-scan-form').addEventListener('submit', handleManualScan);
|
document.getElementById('manual-scan-form').addEventListener('submit', handleManualScan);
|
||||||
|
document.getElementById('populate-form').addEventListener('submit', handlePopulateDatabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API calls
|
// API calls
|
||||||
@@ -1742,3 +1743,212 @@ async function bulkDeleteSelected() {
|
|||||||
showToast(`❌ Failed to delete ${failCount} episode(s)`, 'error');
|
showToast(`❌ Failed to delete ${failCount} episode(s)`, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Database Population Functions ====================
|
||||||
|
|
||||||
|
let populatePollingInterval = null;
|
||||||
|
|
||||||
|
async function handlePopulateDatabase(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const mediaType = document.getElementById('populate-media-type').value;
|
||||||
|
const submitButton = event.target.querySelector('button[type="submit"]');
|
||||||
|
const originalText = submitButton.innerHTML;
|
||||||
|
|
||||||
|
// Confirm action
|
||||||
|
const confirmMsg = mediaType === 'both'
|
||||||
|
? 'This will populate the database with ALL movies and TV episodes from Radarr/Sonarr. Continue?'
|
||||||
|
: mediaType === 'movies'
|
||||||
|
? 'This will populate the database with ALL movies from Radarr. Continue?'
|
||||||
|
: 'This will populate the database with ALL TV episodes from Sonarr. Continue?';
|
||||||
|
|
||||||
|
if (!confirm(confirmMsg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable button and show loading
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Starting...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/admin/populate-database', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ media_type: mediaType })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.status === 'started') {
|
||||||
|
showPopulateStatus();
|
||||||
|
startPopulatePolling();
|
||||||
|
} else {
|
||||||
|
throw new Error('Failed to start population');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error starting population:', error);
|
||||||
|
showToast('❌ Failed to start database population', 'error');
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.innerHTML = originalText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPopulateStatus() {
|
||||||
|
const statusDiv = document.getElementById('populate-status');
|
||||||
|
const resultsDiv = document.getElementById('populate-results');
|
||||||
|
|
||||||
|
if (statusDiv) {
|
||||||
|
statusDiv.style.display = 'block';
|
||||||
|
statusDiv.querySelector('.progress-fill').style.width = '0%';
|
||||||
|
statusDiv.querySelector('.progress-text').textContent = 'Starting...';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultsDiv) {
|
||||||
|
resultsDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hidePopulateStatus() {
|
||||||
|
const statusDiv = document.getElementById('populate-status');
|
||||||
|
if (statusDiv) {
|
||||||
|
statusDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPopulatePolling() {
|
||||||
|
// Clear any existing interval
|
||||||
|
if (populatePollingInterval) {
|
||||||
|
clearInterval(populatePollingInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll every 2 seconds
|
||||||
|
populatePollingInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/populate/status');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = await response.json();
|
||||||
|
updatePopulateProgress(status);
|
||||||
|
|
||||||
|
// Stop polling if complete or error
|
||||||
|
if (status.status === 'completed' || status.status === 'error') {
|
||||||
|
stopPopulatePolling();
|
||||||
|
|
||||||
|
// Re-enable submit button
|
||||||
|
const submitButton = document.querySelector('#populate-form button[type="submit"]');
|
||||||
|
if (submitButton) {
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.innerHTML = '<i class="fas fa-play"></i> Start Population';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error polling populate status:', error);
|
||||||
|
stopPopulatePolling();
|
||||||
|
|
||||||
|
// Re-enable submit button
|
||||||
|
const submitButton = document.querySelector('#populate-form button[type="submit"]');
|
||||||
|
if (submitButton) {
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.innerHTML = '<i class="fas fa-play"></i> Start Population';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPopulatePolling() {
|
||||||
|
if (populatePollingInterval) {
|
||||||
|
clearInterval(populatePollingInterval);
|
||||||
|
populatePollingInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePopulateProgress(status) {
|
||||||
|
const progressFill = document.querySelector('#populate-status .progress-fill');
|
||||||
|
const progressText = document.querySelector('#populate-status .progress-text');
|
||||||
|
const resultsDiv = document.getElementById('populate-results');
|
||||||
|
|
||||||
|
if (status.status === 'in_progress') {
|
||||||
|
if (progressFill && progressText) {
|
||||||
|
// Show indeterminate progress
|
||||||
|
progressFill.style.width = '50%';
|
||||||
|
progressText.textContent = 'Populating database...';
|
||||||
|
}
|
||||||
|
} else if (status.status === 'completed') {
|
||||||
|
if (progressFill && progressText) {
|
||||||
|
progressFill.style.width = '100%';
|
||||||
|
progressText.textContent = 'Complete!';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show results
|
||||||
|
if (resultsDiv && status.result) {
|
||||||
|
const result = status.result;
|
||||||
|
let html = '<h4>Population Results</h4>';
|
||||||
|
|
||||||
|
// Movies
|
||||||
|
if (result.movies) {
|
||||||
|
const m = result.movies;
|
||||||
|
html += '<div class="populate-section">';
|
||||||
|
html += '<h5><i class="fas fa-film"></i> Movies</h5>';
|
||||||
|
html += '<ul>';
|
||||||
|
html += `<li>Total found: ${m.total || 0}</li>`;
|
||||||
|
html += `<li>Added: ${m.added || 0}</li>`;
|
||||||
|
html += `<li>Skipped: ${m.skipped || 0}</li>`;
|
||||||
|
html += `<li>Errors: ${m.errors || 0}</li>`;
|
||||||
|
html += `<li>Duration: ${(m.duration || 0).toFixed(2)}s</li>`;
|
||||||
|
html += '</ul>';
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TV Episodes
|
||||||
|
if (result.tv) {
|
||||||
|
const tv = result.tv;
|
||||||
|
html += '<div class="populate-section">';
|
||||||
|
html += '<h5><i class="fas fa-tv"></i> TV Shows</h5>';
|
||||||
|
html += '<ul>';
|
||||||
|
html += `<li>Series: ${tv.total_series || 0}</li>`;
|
||||||
|
html += `<li>Episodes: ${tv.total_episodes || 0}</li>`;
|
||||||
|
html += `<li>Added: ${tv.added || 0}</li>`;
|
||||||
|
html += `<li>Skipped: ${tv.skipped || 0}</li>`;
|
||||||
|
html += `<li>Errors: ${tv.errors || 0}</li>`;
|
||||||
|
html += `<li>Duration: ${(tv.duration || 0).toFixed(2)}s</li>`;
|
||||||
|
html += '</ul>';
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total duration
|
||||||
|
if (result.total_duration) {
|
||||||
|
html += `<p><strong>Total Duration:</strong> ${result.total_duration.toFixed(2)}s</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultsDiv.innerHTML = html;
|
||||||
|
resultsDiv.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast('✅ Database population completed successfully', 'success');
|
||||||
|
|
||||||
|
} else if (status.status === 'error') {
|
||||||
|
if (progressFill && progressText) {
|
||||||
|
progressFill.style.width = '100%';
|
||||||
|
progressFill.style.backgroundColor = '#dc3545';
|
||||||
|
progressText.textContent = 'Error!';
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMsg = status.error || 'Unknown error occurred';
|
||||||
|
showToast(`❌ Database population failed: ${errorMsg}`, 'error');
|
||||||
|
|
||||||
|
if (resultsDiv) {
|
||||||
|
resultsDiv.innerHTML = `<p class="error">Error: ${errorMsg}</p>`;
|
||||||
|
resultsDiv.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user