updated upload functions
This commit is contained in:
@@ -58,7 +58,6 @@ COPY --from=build /root/.cargo /root/.cargo
|
|||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
RUN pip install gunicorn
|
RUN pip install gunicorn
|
||||||
RUN apt-get update && apt-get install -y libreoffice poppler-utils
|
|
||||||
|
|
||||||
# Make port 5000 available to the world outside this container
|
# Make port 5000 available to the world outside this container
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|||||||
Binary file not shown.
90
app.py
90
app.py
@@ -8,6 +8,10 @@ from functools import wraps
|
|||||||
from extensions import db, bcrypt, login_manager
|
from extensions import db, bcrypt, login_manager
|
||||||
from models import User, Player, Content, Group # Add Group to the imports
|
from models import User, Player, Content, Group # Add Group to the imports
|
||||||
from flask_login import login_user, logout_user, login_required, current_user
|
from flask_login import login_user, logout_user, login_required, current_user
|
||||||
|
from pptx import Presentation
|
||||||
|
from pptx.util import Inches
|
||||||
|
from PIL import Image
|
||||||
|
import io
|
||||||
|
|
||||||
app = Flask(__name__, instance_relative_config=True)
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
|
||||||
@@ -55,6 +59,10 @@ def convert_ppt_to_pdf(input_file, output_file):
|
|||||||
command = ['libreoffice', '--headless', '--convert-to', 'pdf', '--outdir', os.path.dirname(output_file), input_file]
|
command = ['libreoffice', '--headless', '--convert-to', 'pdf', '--outdir', os.path.dirname(output_file), input_file]
|
||||||
subprocess.run(command, check=True)
|
subprocess.run(command, check=True)
|
||||||
|
|
||||||
|
# Convert EMU to pixels
|
||||||
|
def emu_to_pixels(emu):
|
||||||
|
return int(emu / 914400 * 96)
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
@@ -107,50 +115,61 @@ def upload_content():
|
|||||||
duration = int(request.form['duration'])
|
duration = int(request.form['duration'])
|
||||||
return_url = request.form['return_url']
|
return_url = request.form['return_url']
|
||||||
media_type = request.form['media_type']
|
media_type = request.form['media_type']
|
||||||
|
|
||||||
|
print(f"Redirecting to: {return_url}") # Debugging: Log the return_url
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||||
file.save(file_path)
|
file.save(file_path)
|
||||||
|
|
||||||
# Handle PDF to JPG conversion
|
# Handle PPT/PPTX to JPG conversion
|
||||||
if media_type == 'pdf':
|
if media_type == 'ppt':
|
||||||
images = convert_from_path(file_path, dpi=300)
|
try:
|
||||||
for i, image in enumerate(images):
|
presentation = Presentation(file_path)
|
||||||
image_filename = f"{os.path.splitext(filename)[0]}_{i + 1}.jpg"
|
for i, slide in enumerate(presentation.slides):
|
||||||
image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
|
slide_width = emu_to_pixels(presentation.slide_width)
|
||||||
image.save(image_path, 'JPEG')
|
slide_height = emu_to_pixels(presentation.slide_height)
|
||||||
|
img = Image.new('RGB', (slide_width, slide_height), 'white')
|
||||||
# Add each converted image to the playlist
|
|
||||||
if target_type == 'group':
|
# Save the slide as an image
|
||||||
group = Group.query.get_or_404(target_id)
|
image_filename = f"{os.path.splitext(filename)[0]}_slide_{i + 1}.jpg"
|
||||||
for player in group.players:
|
image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
|
||||||
new_content = Content(file_name=image_filename, duration=duration, player_id=player.id)
|
img.save(image_path, 'JPEG')
|
||||||
|
|
||||||
|
# Add each converted image to the playlist
|
||||||
|
if target_type == 'group':
|
||||||
|
group = Group.query.get_or_404(target_id)
|
||||||
|
for player in group.players:
|
||||||
|
new_content = Content(file_name=image_filename, duration=duration, player_id=player.id)
|
||||||
|
db.session.add(new_content)
|
||||||
|
elif target_type == 'player':
|
||||||
|
new_content = Content(file_name=image_filename, duration=duration, player_id=target_id)
|
||||||
db.session.add(new_content)
|
db.session.add(new_content)
|
||||||
elif target_type == 'player':
|
finally:
|
||||||
new_content = Content(file_name=image_filename, duration=duration, player_id=target_id)
|
# Ensure the original PPT file is deleted after processing
|
||||||
db.session.add(new_content)
|
if os.path.exists(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
# Optionally, delete the original PDF file after conversion
|
|
||||||
os.remove(file_path)
|
|
||||||
|
|
||||||
# Handle other media types
|
# Handle other media types
|
||||||
elif media_type in ['image', 'video', 'ppt']:
|
elif media_type in ['image', 'video', 'pdf']:
|
||||||
if media_type == 'ppt':
|
if media_type == 'pdf':
|
||||||
ppt_output_file = os.path.splitext(file_path)[0] + '.pdf'
|
images = convert_from_path(file_path, dpi=300)
|
||||||
convert_ppt_to_pdf(file_path, ppt_output_file)
|
for i, image in enumerate(images):
|
||||||
os.remove(file_path) # Remove the original PPT file
|
image_filename = f"{os.path.splitext(filename)[0]}_{i + 1}.jpg"
|
||||||
file_path = ppt_output_file
|
image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
|
||||||
|
image.save(image_path, 'JPEG')
|
||||||
|
if target_type == 'group':
|
||||||
|
group = Group.query.get_or_404(target_id)
|
||||||
|
for player in group.players:
|
||||||
|
new_content = Content(file_name=image_filename, duration=duration, player_id=player.id)
|
||||||
|
db.session.add(new_content)
|
||||||
|
elif target_type == 'player':
|
||||||
|
new_content = Content(file_name=image_filename, duration=duration, player_id=target_id)
|
||||||
|
db.session.add(new_content)
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
|
|
||||||
if target_type == 'group':
|
|
||||||
group = Group.query.get_or_404(target_id)
|
|
||||||
for player in group.players:
|
|
||||||
new_content = Content(file_name=filename, duration=duration, player_id=player.id)
|
|
||||||
db.session.add(new_content)
|
|
||||||
elif target_type == 'player':
|
|
||||||
new_content = Content(file_name=filename, duration=duration, player_id=target_id)
|
|
||||||
db.session.add(new_content)
|
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(return_url)
|
return redirect(return_url)
|
||||||
|
|
||||||
@@ -158,7 +177,6 @@ def upload_content():
|
|||||||
target_id = request.args.get('target_id')
|
target_id = request.args.get('target_id')
|
||||||
return_url = request.args.get('return_url', url_for('dashboard'))
|
return_url = request.args.get('return_url', url_for('dashboard'))
|
||||||
|
|
||||||
# Serialize players and groups into JSON-serializable dictionaries
|
|
||||||
players = [{'id': player.id, 'username': player.username} for player in Player.query.filter(~Player.groups.any()).all()]
|
players = [{'id': player.id, 'username': player.username} for player in Player.query.filter(~Player.groups.any()).all()]
|
||||||
groups = [{'id': group.id, 'name': group.name} for group in Group.query.all()]
|
groups = [{'id': group.id, 'name': group.name} for group in Group.query.all()]
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,4 @@ services:
|
|||||||
# when setting allready exist and data are setted and is performed an update use second line of command
|
# when setting allready exist and data are setted and is performed an update use second line of command
|
||||||
command: sh -c "python clear_db.py && python init_db.py && gunicorn -w 4 -b 0.0.0.0:5000 app:app"
|
command: sh -c "python clear_db.py && python init_db.py && gunicorn -w 4 -b 0.0.0.0:5000 app:app"
|
||||||
#command: sh -c "python init_db.py && gunicorn -w 4 -b 0.0.0.0:5000 app:app"
|
#command: sh -c "python init_db.py && gunicorn -w 4 -b 0.0.0.0:5000 app:app"
|
||||||
|
restart: unless-stopped
|
||||||
Binary file not shown.
@@ -18,4 +18,5 @@ Werkzeug==3.1.3
|
|||||||
gunicorn==20.1.0
|
gunicorn==20.1.0
|
||||||
pdf2image==1.17.0
|
pdf2image==1.17.0
|
||||||
pillow==11.1.0
|
pillow==11.1.0
|
||||||
|
python-pptx==0.6.21
|
||||||
setuptools==75.8.0
|
setuptools==75.8.0
|
||||||
BIN
static/uploads/merged_1.jpg
Normal file
BIN
static/uploads/merged_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 713 KiB |
BIN
static/uploads/merged_2.jpg
Normal file
BIN
static/uploads/merged_2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 612 KiB |
|
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 414 KiB |
BIN
static/uploads/welcome_Prodrive_slide_1.jpg
Normal file
BIN
static/uploads/welcome_Prodrive_slide_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -15,22 +15,24 @@
|
|||||||
.dark-mode label, .dark-mode th, .dark-mode td {
|
.dark-mode label, .dark-mode th, .dark-mode td {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
.popup-message {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
color: white;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: none;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
.logo {
|
.logo {
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
/* Modal styling for dark mode */
|
||||||
|
.modal-content.dark-mode {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
.modal-header.dark-mode {
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
}
|
||||||
|
.modal-footer.dark-mode {
|
||||||
|
border-top: 1px solid #444;
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
background-color: #007bff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="{{ 'dark-mode' if theme == 'dark' else '' }}">
|
<body class="{{ 'dark-mode' if theme == 'dark' else '' }}">
|
||||||
@@ -41,7 +43,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<h1 class="mb-0">Upload Content</h1>
|
<h1 class="mb-0">Upload Content</h1>
|
||||||
</div>
|
</div>
|
||||||
<form id="upload-form" action="{{ url_for('upload_content') }}" method="post" enctype="multipart/form-data" onsubmit="showPopupMessage('Content uploaded successfully!')">
|
<form id="upload-form" action="{{ url_for('upload_content') }}" method="post" enctype="multipart/form-data" onsubmit="showStatusModal()">
|
||||||
<input type="hidden" name="return_url" value="{{ return_url }}">
|
<input type="hidden" name="return_url" value="{{ return_url }}">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="target_type" class="form-label">Target Type:</label>
|
<label for="target_type" class="form-label">Target Type:</label>
|
||||||
@@ -91,13 +93,31 @@
|
|||||||
<label for="duration" class="form-label">Duration (seconds):</label>
|
<label for="duration" class="form-label">Duration (seconds):</label>
|
||||||
<input type="number" name="duration" id="duration" class="form-control" required>
|
<input type="number" name="duration" id="duration" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Upload</button>
|
<button type="submit" id="submit-button" class="btn btn-primary">Upload</button>
|
||||||
</form>
|
</form>
|
||||||
<a href="{{ return_url }}" class="btn btn-secondary mt-3">Back</a>
|
<a href="{{ return_url }}" class="btn btn-secondary mt-3">Back</a>
|
||||||
<a href="{{ url_for('dashboard') }}" class="btn btn-secondary mt-3">Back to Dashboard</a>
|
<a href="{{ url_for('dashboard') }}" class="btn btn-secondary mt-3">Back to Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="popup-message" class="popup-message"></div>
|
<!-- Modal for Status Updates -->
|
||||||
|
<div class="modal fade" id="statusModal" tabindex="-1" aria-labelledby="statusModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content {{ 'dark-mode' if theme == 'dark' else '' }}">
|
||||||
|
<div class="modal-header {{ 'dark-mode' if theme == 'dark' else '' }}">
|
||||||
|
<h5 class="modal-title" id="statusModalLabel">Processing Files</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p id="status-message">Uploading and processing your files. Please wait...</p>
|
||||||
|
<div class="progress">
|
||||||
|
<div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer {{ 'dark-mode' if theme == 'dark' else '' }}">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" disabled>Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -147,14 +167,22 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function showPopupMessage(message) {
|
function showStatusModal() {
|
||||||
const popup = document.getElementById('popup-message');
|
const statusModal = new bootstrap.Modal(document.getElementById('statusModal'));
|
||||||
popup.textContent = message;
|
statusModal.show();
|
||||||
popup.style.display = 'block';
|
|
||||||
setTimeout(() => {
|
// Simulate progress updates
|
||||||
popup.style.display = 'none';
|
const progressBar = document.getElementById('progress-bar');
|
||||||
document.getElementById('upload-form').submit();
|
let progress = 0;
|
||||||
}, 5000); // Display time set to 5 seconds
|
const interval = setInterval(() => {
|
||||||
|
progress += 10;
|
||||||
|
progressBar.style.width = `${progress}%`;
|
||||||
|
progressBar.setAttribute('aria-valuenow', progress);
|
||||||
|
if (progress >= 100) {
|
||||||
|
clearInterval(interval);
|
||||||
|
document.getElementById('status-message').textContent = 'Files uploaded and processed successfully!';
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user