updated features to upload pptx files
This commit is contained in:
@@ -219,6 +219,92 @@
|
||||
.duration-input {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.duration-input:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.duration-input:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.save-duration-btn {
|
||||
transition: all 0.2s ease;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
body.dark-mode .playlist-section {
|
||||
background: #2d3748;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
body.dark-mode .section-header h2 {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
body.dark-mode .section-header {
|
||||
border-bottom-color: #4a5568;
|
||||
}
|
||||
|
||||
body.dark-mode .playlist-table thead {
|
||||
background: #1a202c;
|
||||
}
|
||||
|
||||
body.dark-mode .playlist-table th {
|
||||
color: #cbd5e0;
|
||||
border-bottom-color: #4a5568;
|
||||
}
|
||||
|
||||
body.dark-mode .playlist-table td {
|
||||
border-bottom-color: #4a5568;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
body.dark-mode .playlist-table tr:hover {
|
||||
background: #1a202c;
|
||||
}
|
||||
|
||||
body.dark-mode .form-control {
|
||||
background: #1a202c;
|
||||
border-color: #4a5568;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
body.dark-mode .form-control:focus {
|
||||
border-color: #667eea;
|
||||
background: #2d3748;
|
||||
}
|
||||
|
||||
body.dark-mode .add-content-form {
|
||||
background: #1a202c;
|
||||
}
|
||||
|
||||
body.dark-mode .form-group label {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
body.dark-mode .empty-state {
|
||||
color: #718096;
|
||||
}
|
||||
|
||||
body.dark-mode .drag-handle {
|
||||
color: #718096;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -290,9 +376,9 @@
|
||||
</thead>
|
||||
<tbody id="playlist-tbody">
|
||||
{% for content in playlist_content %}
|
||||
<tr class="draggable-row" draggable="true" data-content-id="{{ content.id }}">
|
||||
<tr class="draggable-row" data-content-id="{{ content.id }}">
|
||||
<td>
|
||||
<span class="drag-handle">⋮⋮</span>
|
||||
<span class="drag-handle" draggable="true">⋮⋮</span>
|
||||
</td>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ content.filename }}</td>
|
||||
@@ -304,11 +390,28 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<input type="number"
|
||||
class="form-control duration-input"
|
||||
value="{{ content.duration }}"
|
||||
min="1"
|
||||
onchange="updateDuration({{ content.id }}, this.value)">
|
||||
<div style="display: flex; align-items: center; gap: 5px;">
|
||||
<input type="number"
|
||||
class="form-control duration-input"
|
||||
id="duration-{{ content.id }}"
|
||||
value="{{ content._playlist_duration }}"
|
||||
min="1"
|
||||
draggable="false"
|
||||
onclick="event.stopPropagation()"
|
||||
onmousedown="event.stopPropagation()"
|
||||
oninput="markDurationChanged({{ content.id }})"
|
||||
onkeypress="if(event.key==='Enter') saveDuration({{ content.id }})"
|
||||
style="width: 60px; padding: 5px 8px;">
|
||||
<button type="button"
|
||||
class="btn btn-success btn-sm save-duration-btn"
|
||||
id="save-btn-{{ content.id }}"
|
||||
onclick="event.stopPropagation(); saveDuration({{ content.id }})"
|
||||
onmousedown="event.stopPropagation()"
|
||||
style="display: none; padding: 5px 10px; font-size: 12px; cursor: pointer;"
|
||||
title="Save duration (or press Enter)">
|
||||
💾
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ "%.2f"|format(content.file_size_mb) }} MB</td>
|
||||
<td>
|
||||
@@ -335,7 +438,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Add Content Section -->
|
||||
{% if available_files %}
|
||||
{% if available_content %}
|
||||
<div class="playlist-section">
|
||||
<div class="section-header">
|
||||
<h2>➕ Add Existing Content</h2>
|
||||
@@ -344,11 +447,11 @@
|
||||
<form method="POST" action="{{ url_for('playlist.add_to_playlist', player_id=player.id) }}"
|
||||
class="add-content-form">
|
||||
<div class="form-group">
|
||||
<label for="filename">Select File:</label>
|
||||
<select name="filename" id="filename" class="form-control" required>
|
||||
<option value="" disabled selected>Choose a file...</option>
|
||||
{% for filename in available_files %}
|
||||
<option value="{{ filename }}">{{ filename }}</option>
|
||||
<label for="content_id">Select Content:</label>
|
||||
<select name="content_id" id="content_id" class="form-control" required>
|
||||
<option value="" disabled selected>Choose content...</option>
|
||||
{% for content in available_content %}
|
||||
<option value="{{ content.id }}">{{ content.filename }} ({{ content.content_type }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -380,20 +483,41 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const tbody = document.getElementById('playlist-tbody');
|
||||
if (!tbody) return;
|
||||
|
||||
const rows = tbody.querySelectorAll('.draggable-row');
|
||||
// Set up drag handles
|
||||
const dragHandles = tbody.querySelectorAll('.drag-handle');
|
||||
dragHandles.forEach(handle => {
|
||||
handle.addEventListener('dragstart', handleDragStart);
|
||||
});
|
||||
|
||||
// Set up drop zones on rows
|
||||
const rows = tbody.querySelectorAll('.draggable-row');
|
||||
rows.forEach(row => {
|
||||
row.addEventListener('dragstart', handleDragStart);
|
||||
row.addEventListener('dragover', handleDragOver);
|
||||
row.addEventListener('drop', handleDrop);
|
||||
row.addEventListener('dragend', handleDragEnd);
|
||||
});
|
||||
|
||||
// Prevent dragging from inputs and buttons
|
||||
const inputs = document.querySelectorAll('.duration-input, button');
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('mousedown', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
input.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function handleDragStart(e) {
|
||||
draggedElement = this;
|
||||
this.classList.add('dragging');
|
||||
// Get the parent row
|
||||
const row = e.target.closest('.draggable-row');
|
||||
if (!row) return;
|
||||
|
||||
draggedElement = row;
|
||||
row.classList.add('dragging');
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/html', row.innerHTML);
|
||||
}
|
||||
|
||||
function handleDragOver(e) {
|
||||
@@ -464,7 +588,41 @@ function saveOrder() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateDuration(contentId, duration) {
|
||||
function markDurationChanged(contentId) {
|
||||
const saveBtn = document.getElementById(`save-btn-${contentId}`);
|
||||
const input = document.getElementById(`duration-${contentId}`);
|
||||
|
||||
// Show save button if value changed
|
||||
if (input.value !== input.defaultValue) {
|
||||
saveBtn.style.display = 'inline-block';
|
||||
input.style.borderColor = '#ffc107';
|
||||
} else {
|
||||
saveBtn.style.display = 'none';
|
||||
input.style.borderColor = '';
|
||||
}
|
||||
}
|
||||
|
||||
function saveDuration(contentId) {
|
||||
const inputElement = document.getElementById(`duration-${contentId}`);
|
||||
const saveBtn = document.getElementById(`save-btn-${contentId}`);
|
||||
const duration = parseInt(inputElement.value);
|
||||
|
||||
// Validate duration
|
||||
if (duration < 1) {
|
||||
alert('Duration must be at least 1 second');
|
||||
inputElement.value = inputElement.defaultValue;
|
||||
inputElement.style.borderColor = '';
|
||||
saveBtn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const originalValue = inputElement.defaultValue;
|
||||
|
||||
// Visual feedback
|
||||
inputElement.disabled = true;
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.textContent = '⏳';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('duration', duration);
|
||||
|
||||
@@ -476,15 +634,65 @@ function updateDuration(contentId, duration) {
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
console.log('Duration updated successfully');
|
||||
// Update total duration
|
||||
location.reload();
|
||||
inputElement.style.borderColor = '#28a745';
|
||||
inputElement.defaultValue = duration;
|
||||
saveBtn.textContent = '✓';
|
||||
|
||||
// Update total duration display
|
||||
updateTotalDuration();
|
||||
|
||||
setTimeout(() => {
|
||||
inputElement.style.borderColor = '';
|
||||
inputElement.disabled = false;
|
||||
saveBtn.style.display = 'none';
|
||||
saveBtn.textContent = '💾';
|
||||
saveBtn.disabled = false;
|
||||
}, 1500);
|
||||
} else {
|
||||
inputElement.style.borderColor = '#dc3545';
|
||||
inputElement.value = originalValue;
|
||||
saveBtn.textContent = '✖';
|
||||
alert('Error updating duration: ' + data.message);
|
||||
|
||||
setTimeout(() => {
|
||||
inputElement.disabled = false;
|
||||
inputElement.style.borderColor = '';
|
||||
saveBtn.style.display = 'none';
|
||||
saveBtn.textContent = '💾';
|
||||
saveBtn.disabled = false;
|
||||
}, 1500);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
inputElement.style.borderColor = '#dc3545';
|
||||
inputElement.value = originalValue;
|
||||
saveBtn.textContent = '✖';
|
||||
alert('Error updating duration');
|
||||
|
||||
setTimeout(() => {
|
||||
inputElement.disabled = false;
|
||||
inputElement.style.borderColor = '';
|
||||
saveBtn.style.display = 'none';
|
||||
saveBtn.textContent = '💾';
|
||||
saveBtn.disabled = false;
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
function updateTotalDuration() {
|
||||
const durationInputs = document.querySelectorAll('.duration-input');
|
||||
let total = 0;
|
||||
durationInputs.forEach(input => {
|
||||
total += parseInt(input.value) || 0;
|
||||
});
|
||||
|
||||
const statValues = document.querySelectorAll('.stat-value');
|
||||
statValues.forEach((element, index) => {
|
||||
const label = element.parentElement.querySelector('.stat-label');
|
||||
if (label && label.textContent.includes('Total Duration')) {
|
||||
element.textContent = total + 's';
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user