Add real-time upload progress tracking, mobile-optimized manage_group page with player status cards
Features: - Real-time upload progress tracking with AJAX polling and session-based monitoring - API endpoint /api/upload_progress/<session_id> for progress updates - Video conversion progress tracking with background threads - Mobile-responsive design for manage_group page - Player status cards with feedback, playlist sync, and last activity - Bootstrap Icons integration throughout UI - Responsive layout (1/4 group info, 3/4 players on desktop) - Video thumbnails with play icon, image thumbnails in media lists - Bulk selection and delete for group media - Enhanced logging for video conversion debugging
This commit is contained in:
@@ -57,7 +57,7 @@
|
||||
{% endif %}
|
||||
<h1 class="mb-0">Upload Content</h1>
|
||||
</div>
|
||||
<form id="upload-form" action="{{ url_for('upload_content') }}" method="post" enctype="multipart/form-data" onsubmit="showStatusModal()">
|
||||
<form id="upload-form" action="{{ url_for('upload_content') }}" method="post" enctype="multipart/form-data" onsubmit="handleFormSubmit(event)">
|
||||
<input type="hidden" name="return_url" value="{{ return_url }}">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
@@ -223,82 +223,127 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
let progressInterval = null;
|
||||
let sessionId = null;
|
||||
let statusModal = null;
|
||||
let returnUrl = '{{ return_url }}';
|
||||
|
||||
// Generate unique session ID for this upload
|
||||
function generateSessionId() {
|
||||
return 'upload_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
|
||||
function handleFormSubmit(event) {
|
||||
event.preventDefault(); // Prevent default form submission
|
||||
|
||||
// Generate session ID and add it to the form
|
||||
sessionId = generateSessionId();
|
||||
const form = document.getElementById('upload-form');
|
||||
let sessionInput = document.getElementById('session_id_input');
|
||||
if (!sessionInput) {
|
||||
sessionInput = document.createElement('input');
|
||||
sessionInput.type = 'hidden';
|
||||
sessionInput.name = 'session_id';
|
||||
sessionInput.id = 'session_id_input';
|
||||
form.appendChild(sessionInput);
|
||||
}
|
||||
sessionInput.value = sessionId;
|
||||
|
||||
// Show modal
|
||||
showStatusModal();
|
||||
|
||||
// Submit form via AJAX
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch(form.action, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Upload failed');
|
||||
}
|
||||
console.log('Form submitted successfully');
|
||||
// Don't redirect yet - keep polling until status is complete
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Form submission error:', error);
|
||||
if (upload_progress && sessionId) {
|
||||
upload_progress[sessionId] = {
|
||||
'status': 'error',
|
||||
'progress': 0,
|
||||
'message': 'Upload failed: ' + error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showStatusModal() {
|
||||
console.log("Processing popup triggered");
|
||||
const statusModal = new bootstrap.Modal(document.getElementById('statusModal'));
|
||||
|
||||
statusModal = new bootstrap.Modal(document.getElementById('statusModal'));
|
||||
statusModal.show();
|
||||
|
||||
// Update status message based on media type
|
||||
const mediaType = document.getElementById('media_type').value;
|
||||
{% if system_info %}
|
||||
// Start system monitoring updates
|
||||
startModalSystemMonitoring();
|
||||
{% endif %}
|
||||
|
||||
// Start polling progress
|
||||
pollUploadProgress();
|
||||
}
|
||||
|
||||
function pollUploadProgress() {
|
||||
const statusMessage = document.getElementById('status-message');
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
let progress = 0;
|
||||
|
||||
if (mediaType === 'video') {
|
||||
statusMessage.textContent = 'Uploading video...';
|
||||
// Stage 1: Uploading (0-40%)
|
||||
let uploadInterval = setInterval(() => {
|
||||
progress += 8;
|
||||
if (progress >= 40) {
|
||||
clearInterval(uploadInterval);
|
||||
progress = 40;
|
||||
progressBar.style.width = `${progress}%`;
|
||||
progressBar.setAttribute('aria-valuenow', progress);
|
||||
// Stage 2: Converting
|
||||
statusMessage.textContent = 'Converting video to standard format (29.97 fps, 1080p)...';
|
||||
let convertProgress = 40;
|
||||
let convertInterval = setInterval(() => {
|
||||
convertProgress += 3;
|
||||
progressBar.style.width = `${convertProgress}%`;
|
||||
progressBar.setAttribute('aria-valuenow', convertProgress);
|
||||
if (convertProgress >= 100) {
|
||||
clearInterval(convertInterval);
|
||||
statusMessage.textContent = 'Video uploaded and converted successfully!';
|
||||
// Stop system monitoring updates
|
||||
{% if system_info %}
|
||||
stopModalSystemMonitoring();
|
||||
{% endif %}
|
||||
document.querySelector('[data-bs-dismiss="modal"]').disabled = false;
|
||||
|
||||
// Poll every 500ms for real-time updates
|
||||
progressInterval = setInterval(() => {
|
||||
fetch(`/api/upload_progress/${sessionId}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Progress update:', data);
|
||||
|
||||
// Update progress bar
|
||||
progressBar.style.width = `${data.progress}%`;
|
||||
progressBar.setAttribute('aria-valuenow', data.progress);
|
||||
|
||||
// Update status message
|
||||
statusMessage.textContent = data.message;
|
||||
|
||||
// If complete or error, stop polling and enable close button
|
||||
if (data.status === 'complete' || data.status === 'error') {
|
||||
clearInterval(progressInterval);
|
||||
progressInterval = null;
|
||||
|
||||
{% if system_info %}
|
||||
stopModalSystemMonitoring();
|
||||
{% endif %}
|
||||
|
||||
const closeBtn = document.querySelector('[data-bs-dismiss="modal"]');
|
||||
closeBtn.disabled = false;
|
||||
|
||||
// Change progress bar color based on status
|
||||
if (data.status === 'complete') {
|
||||
progressBar.classList.remove('progress-bar-animated');
|
||||
progressBar.classList.add('bg-success');
|
||||
|
||||
// Auto-close after 2 seconds and redirect
|
||||
setTimeout(() => {
|
||||
statusModal.hide();
|
||||
window.location.href = returnUrl;
|
||||
}, 2000);
|
||||
} else if (data.status === 'error') {
|
||||
progressBar.classList.remove('progress-bar-animated', 'progress-bar-striped');
|
||||
progressBar.classList.add('bg-danger');
|
||||
}
|
||||
}, 600);
|
||||
} else {
|
||||
progressBar.style.width = `${progress}%`;
|
||||
progressBar.setAttribute('aria-valuenow', progress);
|
||||
}
|
||||
}, 400);
|
||||
} else {
|
||||
// Default for other media types
|
||||
switch(mediaType) {
|
||||
case 'image':
|
||||
statusMessage.textContent = 'Uploading images...';
|
||||
break;
|
||||
case 'pdf':
|
||||
statusMessage.textContent = 'Converting PDF to 4K images. This may take a while...';
|
||||
break;
|
||||
case 'ppt':
|
||||
statusMessage.textContent = 'Converting PowerPoint to images (PPTX → PDF → Images). This may take 2-5 minutes...';
|
||||
break;
|
||||
default:
|
||||
statusMessage.textContent = 'Uploading and processing your files. Please wait...';
|
||||
}
|
||||
// Simulate progress updates
|
||||
let interval = setInterval(() => {
|
||||
const increment = (mediaType === 'image') ? 20 : 5;
|
||||
progress += increment;
|
||||
if (progress >= 100) {
|
||||
clearInterval(interval);
|
||||
statusMessage.textContent = 'Files uploaded and processed successfully!';
|
||||
{% if system_info %}
|
||||
stopModalSystemMonitoring();
|
||||
{% endif %}
|
||||
document.querySelector('[data-bs-dismiss="modal"]').disabled = false;
|
||||
} else {
|
||||
progressBar.style.width = `${progress}%`;
|
||||
progressBar.setAttribute('aria-valuenow', progress);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching progress:', error);
|
||||
statusMessage.textContent = 'Error tracking upload progress';
|
||||
});
|
||||
}, 500); // Poll every 500ms
|
||||
}
|
||||
|
||||
{% if system_info %}
|
||||
|
||||
Reference in New Issue
Block a user