updated digiserver 2
This commit is contained in:
315
app/templates/content/upload_content.html
Normal file
315
app/templates/content/upload_content.html
Normal file
@@ -0,0 +1,315 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Upload Content - DigiServer v2{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container" style="max-width: 1200px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h1>Upload Content</h1>
|
||||
</div>
|
||||
|
||||
<form id="upload-form" method="POST" enctype="multipart/form-data" onsubmit="handleFormSubmit(event)">
|
||||
<input type="hidden" name="return_url" value="{{ return_url or url_for('content.content_list') }}">
|
||||
|
||||
<div class="card" style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 15px;">Target Selection</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold;">Target Type:</label>
|
||||
<select name="target_type" id="target_type" class="form-control" required onchange="updateTargetIdOptions()">
|
||||
<option value="" disabled selected>Select Target Type</option>
|
||||
<option value="player" {% if target_type == 'player' %}selected{% endif %}>Player</option>
|
||||
<option value="group" {% if target_type == 'group' %}selected{% endif %}>Group</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold;">Target ID:</label>
|
||||
<select name="target_id" id="target_id" class="form-control" required>
|
||||
{% if target_type == 'player' %}
|
||||
{% for player in players %}
|
||||
<option value="{{ player.id }}" {% if target_id == player.id %}selected{% endif %}>{{ player.name }}</option>
|
||||
{% endfor %}
|
||||
{% elif target_type == 'group' %}
|
||||
{% for group in groups %}
|
||||
<option value="{{ group.id }}" {% if target_id == group.id %}selected{% endif %}>{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<option value="" disabled selected>Select a Target ID</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom: 20px;">
|
||||
<h3 style="margin-bottom: 15px;">Media Details</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold;">Media Type:</label>
|
||||
<select name="media_type" id="media_type" class="form-control" required onchange="handleMediaTypeChange()">
|
||||
<option value="image">Image (JPG, PNG, GIF)</option>
|
||||
<option value="video">Video (MP4, AVI, MOV)</option>
|
||||
<option value="pdf">PDF Document</option>
|
||||
<option value="ppt">PowerPoint (PPT/PPTX)</option>
|
||||
</select>
|
||||
<small style="color: #6c757d; display: block; margin-top: 5px;" id="media-type-hint">
|
||||
Images will be displayed as-is
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold;">Duration (seconds):</label>
|
||||
<input type="number" name="duration" id="duration" class="form-control" required min="1" value="10">
|
||||
<small style="color: #6c757d; display: block; margin-top: 5px;">
|
||||
How long to display each image/slide (videos use actual length)
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 5px; font-weight: bold;">Files:</label>
|
||||
<input type="file" name="files" id="files" class="form-control" multiple required
|
||||
accept="image/*,video/*,.pdf,.ppt,.pptx" onchange="handleFileChange()">
|
||||
<small style="color: #6c757d; display: block; margin-top: 5px;">
|
||||
Select multiple files. Supported: JPG, PNG, GIF, MP4, PDF, PPT, PPTX
|
||||
</small>
|
||||
<div id="file-list" style="margin-top: 10px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<button type="submit" id="submit-button" class="btn btn-success" style="padding: 10px 30px; font-size: 16px;">
|
||||
📤 Upload Files
|
||||
</button>
|
||||
<a href="{{ return_url or url_for('content.content_list') }}" class="btn" style="padding: 10px 30px; font-size: 16px;">
|
||||
← Back
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal for Status Updates -->
|
||||
<div id="statusModal" class="modal" style="display: none;">
|
||||
<div class="modal-content" style="max-width: 800px; margin: 50px auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
|
||||
<h2 style="margin-bottom: 20px; color: #2c3e50;">Processing Files</h2>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<p id="status-message" style="font-size: 16px; color: #555;">Uploading and processing your files. Please wait...</p>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div style="margin-bottom: 30px;">
|
||||
<label style="display: block; margin-bottom: 10px; font-weight: bold;">File Processing Progress</label>
|
||||
<div style="width: 100%; height: 30px; background: #e9ecef; border-radius: 5px; overflow: hidden;">
|
||||
<div id="progress-bar" style="width: 0%; height: 100%; background: linear-gradient(90deg, #007bff, #0056b3); transition: width 0.3s ease; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 14px;">
|
||||
0%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 20px;">
|
||||
<button type="button" class="btn" onclick="closeModal()" disabled id="close-modal-btn">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9999;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let progressInterval = null;
|
||||
let sessionId = null;
|
||||
let returnUrl = '{{ return_url or url_for("content.content_list") }}';
|
||||
|
||||
function generateSessionId() {
|
||||
return 'upload_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
|
||||
function handleFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
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;
|
||||
|
||||
showStatusModal();
|
||||
|
||||
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');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Form submission error:', error);
|
||||
document.getElementById('status-message').textContent = 'Upload failed: ' + error.message;
|
||||
document.getElementById('progress-bar').style.background = '#dc3545';
|
||||
document.getElementById('close-modal-btn').disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function showStatusModal() {
|
||||
const modal = document.getElementById('statusModal');
|
||||
modal.style.display = 'block';
|
||||
|
||||
const mediaType = document.getElementById('media_type').value;
|
||||
const statusMessage = document.getElementById('status-message');
|
||||
|
||||
switch(mediaType) {
|
||||
case 'image':
|
||||
statusMessage.textContent = 'Uploading images...';
|
||||
break;
|
||||
case 'video':
|
||||
statusMessage.textContent = 'Uploading and converting video. This may take several minutes...';
|
||||
break;
|
||||
case 'pdf':
|
||||
statusMessage.textContent = 'Uploading and converting PDF to images...';
|
||||
break;
|
||||
case 'ppt':
|
||||
statusMessage.textContent = 'Uploading and converting PowerPoint to images...';
|
||||
break;
|
||||
default:
|
||||
statusMessage.textContent = 'Uploading and processing your files. Please wait...';
|
||||
}
|
||||
|
||||
pollUploadProgress();
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
const modal = document.getElementById('statusModal');
|
||||
modal.style.display = 'none';
|
||||
|
||||
if (progressInterval) {
|
||||
clearInterval(progressInterval);
|
||||
}
|
||||
|
||||
window.location.href = returnUrl;
|
||||
}
|
||||
|
||||
function pollUploadProgress() {
|
||||
progressInterval = setInterval(() => {
|
||||
fetch(`/api/upload-progress/${sessionId}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
progressBar.style.width = `${data.progress}%`;
|
||||
progressBar.textContent = `${data.progress}%`;
|
||||
|
||||
document.getElementById('status-message').textContent = data.message;
|
||||
|
||||
if (data.status === 'complete' || data.status === 'error') {
|
||||
clearInterval(progressInterval);
|
||||
progressInterval = null;
|
||||
|
||||
const closeBtn = document.getElementById('close-modal-btn');
|
||||
closeBtn.disabled = false;
|
||||
|
||||
if (data.status === 'complete') {
|
||||
progressBar.style.background = '#28a745';
|
||||
setTimeout(() => closeModal(), 2000);
|
||||
} else if (data.status === 'error') {
|
||||
progressBar.style.background = '#dc3545';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error fetching progress:', error));
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function updateTargetIdOptions() {
|
||||
const targetType = document.getElementById('target_type').value;
|
||||
const targetIdSelect = document.getElementById('target_id');
|
||||
targetIdSelect.innerHTML = '';
|
||||
|
||||
if (targetType === 'player') {
|
||||
const players = {{ players|tojson }};
|
||||
players.forEach(player => {
|
||||
const option = document.createElement('option');
|
||||
option.value = player.id;
|
||||
option.textContent = player.name;
|
||||
targetIdSelect.appendChild(option);
|
||||
});
|
||||
} else if (targetType === 'group') {
|
||||
const groups = {{ groups|tojson }};
|
||||
groups.forEach(group => {
|
||||
const option = document.createElement('option');
|
||||
option.value = group.id;
|
||||
option.textContent = group.name;
|
||||
targetIdSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleMediaTypeChange() {
|
||||
const mediaType = document.getElementById('media_type').value;
|
||||
const hint = document.getElementById('media-type-hint');
|
||||
|
||||
switch(mediaType) {
|
||||
case 'image':
|
||||
hint.textContent = 'Images will be displayed as-is';
|
||||
break;
|
||||
case 'video':
|
||||
hint.textContent = 'Videos will be converted to optimized format';
|
||||
break;
|
||||
case 'pdf':
|
||||
hint.textContent = 'PDF will be converted to images (one per page)';
|
||||
break;
|
||||
case 'ppt':
|
||||
hint.textContent = 'PowerPoint will be converted to images (one per slide)';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileChange() {
|
||||
const filesInput = document.getElementById('files');
|
||||
const fileList = document.getElementById('file-list');
|
||||
const mediaType = document.getElementById('media_type').value;
|
||||
const durationInput = document.getElementById('duration');
|
||||
|
||||
fileList.innerHTML = '';
|
||||
if (filesInput.files.length > 0) {
|
||||
fileList.innerHTML = '<strong>Selected files:</strong><ul style="margin: 5px 0; padding-left: 20px;">';
|
||||
for (let i = 0; i < filesInput.files.length; i++) {
|
||||
const file = filesInput.files[i];
|
||||
const sizeMB = (file.size / (1024 * 1024)).toFixed(2);
|
||||
fileList.innerHTML += `<li>${file.name} (${sizeMB} MB)</li>`;
|
||||
}
|
||||
fileList.innerHTML += '</ul>';
|
||||
}
|
||||
|
||||
if (mediaType === 'video' && filesInput.files.length > 0) {
|
||||
const file = filesInput.files[0];
|
||||
const video = document.createElement('video');
|
||||
video.preload = 'metadata';
|
||||
video.onloadedmetadata = function() {
|
||||
window.URL.revokeObjectURL(video.src);
|
||||
const duration = Math.round(video.duration);
|
||||
durationInput.value = duration;
|
||||
};
|
||||
video.src = URL.createObjectURL(file);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user