updated to 4k images from pptx

This commit is contained in:
2025-08-01 10:23:38 +03:00
parent c8bbbebb48
commit 1326543418
9 changed files with 82 additions and 30 deletions

19
app.py
View File

@@ -327,7 +327,8 @@ def edit_player(player_id):
hostname = request.form['hostname'] hostname = request.form['hostname']
password = request.form['password'] if request.form['password'] else None password = request.form['password'] if request.form['password'] else None
quickconnect_password = request.form['quickconnect_password'] if request.form['quickconnect_password'] else None quickconnect_password = request.form['quickconnect_password'] if request.form['quickconnect_password'] else None
edit_player_util(player_id, username, hostname, password, quickconnect_password) orientation = request.form.get('orientation', player.orientation) # <-- Get orientation
edit_player_util(player_id, username, hostname, password, quickconnect_password, orientation) # <-- Pass orientation
flash(f'Player "{username}" updated successfully.', 'success') flash(f'Player "{username}" updated successfully.', 'success')
return redirect(url_for('player_page', player_id=player.id)) return redirect(url_for('player_page', player_id=player.id))
@@ -648,14 +649,14 @@ def create_admin(username, password):
from models.create_default_user import create_default_user from models.create_default_user import create_default_user
with app.app_context(): if not app.debug or os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
try: with app.app_context():
db.session.execute(db.select(User).limit(1)) try:
except Exception as e: db.session.execute(db.select(User).limit(1))
print("Database not initialized or missing tables. Re-initializing...") except Exception as e:
db.create_all() print("Database not initialized or missing tables. Re-initializing...")
# Always ensure default user exists db.create_all()
create_default_user(db, User, bcrypt) create_default_user(db, User, bcrypt)
# Add this at the end of app.py # Add this at the end of app.py
if __name__ == '__main__': if __name__ == '__main__':

Binary file not shown.

View File

@@ -49,11 +49,23 @@
</select> </select>
</div> </div>
</div> </div>
<div class="col-md-6 col-12">
<div class="mb-3">
<label for="orientation" class="form-label">Group Orientation</label>
<select class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" id="orientation" name="orientation" required>
<option value="Landscape" selected>Landscape</option>
<option value="Portret">Portret</option>
</select>
</div>
</div>
</div> </div>
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
<strong>Warning:</strong> Adding players to a group will delete their individual playlists. <strong>Warning:</strong> Adding players to a group will delete their individual playlists.
All players in a group will share the same content. All players in a group will share the same content.
</div> </div>
<div id="orientation-warning" class="alert alert-danger d-none" role="alert">
No players with the selected orientation are available.
</div>
<div class="text-center"> <div class="text-center">
<button type="submit" class="btn btn-primary">Create Group</button> <button type="submit" class="btn btn-primary">Create Group</button>
<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>
@@ -61,5 +73,38 @@
</form> </form>
</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>
// Get all players and their orientations from the backend
const players = [
{% for player in players %}
{id: {{ player.id }}, username: "{{ player.username }}", orientation: "{{ player.orientation }}"},
{% endfor %}
];
const orientationSelect = document.getElementById('orientation');
const playersSelect = document.getElementById('players');
const orientationWarning = document.getElementById('orientation-warning');
function filterPlayers() {
const selectedOrientation = orientationSelect.value;
playersSelect.innerHTML = '';
let compatibleCount = 0;
players.forEach(player => {
if (player.orientation === selectedOrientation) {
const option = document.createElement('option');
option.value = player.id;
option.textContent = player.username;
playersSelect.appendChild(option);
compatibleCount++;
}
});
document.getElementById('orientation-warning').classList.toggle('d-none', compatibleCount > 0);
}
orientationSelect.addEventListener('change', filterPlayers);
// Initial filter on page load
filterPlayers();
</script>
</body> </body>
</html> </html>

View File

@@ -60,6 +60,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mb-3">
<label for="orientation" class="form-label">Orientation</label>
<select class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" id="orientation" name="orientation" required>
<option value="Landscape" {% if player.orientation == 'Landscape' %}selected{% endif %}>Landscape</option>
<option value="Portret" {% if player.orientation == 'Portret' %}selected{% endif %}>Portret</option>
</select>
</div>
<div class="text-center"> <div class="text-center">
<button type="submit" class="btn btn-primary">Update Player</button> <button type="submit" class="btn btn-primary">Update Player</button>
<a href="{{ return_url }}" class="btn btn-secondary mt-3">Back to Player Page</a> <a href="{{ return_url }}" class="btn btn-secondary mt-3">Back to Player Page</a>

View File

@@ -106,8 +106,12 @@
&#9776; &#9776;
</div> </div>
<!-- Media Name --> <!-- Media Thumbnail and Name -->
<div class="flex-grow-1 mb-2 mb-md-0"> <div class="flex-grow-1 mb-2 mb-md-0 d-flex align-items-center">
<img src="{{ url_for('static', filename='uploads/' ~ media.file_name) }}"
alt="thumbnail"
style="width: 48px; height: 48px; object-fit: cover; margin-right: 10px; border-radius: 4px;"
onerror="this.style.display='none';">
<p class="mb-0"><strong>Media Name:</strong> {{ media.file_name }}</p> <p class="mb-0"><strong>Media Name:</strong> {{ media.file_name }}</p>
</div> </div>

View File

@@ -130,7 +130,7 @@ def add_player(username, hostname, password, quickconnect_password, orientation=
log_player_created(username, hostname) log_player_created(username, hostname)
return new_player return new_player
def edit_player(player_id, username, hostname, password=None, quickconnect_password=None): def edit_player(player_id, username, hostname, password=None, quickconnect_password=None, orientation=None):
""" """
Edit an existing player's details. Edit an existing player's details.
""" """
@@ -147,6 +147,9 @@ def edit_player(player_id, username, hostname, password=None, quickconnect_passw
if quickconnect_password: if quickconnect_password:
player.quickconnect_password = bcrypt.generate_password_hash(quickconnect_password).decode('utf-8') player.quickconnect_password = bcrypt.generate_password_hash(quickconnect_password).decode('utf-8')
if orientation:
player.orientation = orientation
db.session.commit() db.session.commit()
log_player_edited(username) log_player_edited(username)
return player return player

View File

@@ -105,23 +105,14 @@ def convert_video_and_update_playlist(app, file_path, original_filename, target_
print(f"Video conversion failed for: {file_path}") print(f"Video conversion failed for: {file_path}")
# PDF conversion functions # PDF conversion functions
def convert_pdf_to_images(pdf_file, output_folder, delete_pdf=True): def convert_pdf_to_images(pdf_file, output_folder, delete_pdf=True, dpi=600):
""" """
Convert a PDF file to images in sequential order. Convert a PDF file to images in sequential order at high resolution (4K).
Args:
pdf_file (str): Path to the PDF file
output_folder (str): Path to save the images
delete_pdf (bool): Whether to delete the PDF file after processing
Returns:
list: List of generated image filenames in page order, or empty list if conversion failed
""" """
print(f"Converting PDF to images: {pdf_file}") print(f"Converting PDF to images: {pdf_file} at {dpi} DPI")
try: try:
# Convert PDF to images # Convert PDF to images
images = convert_from_path(pdf_file, dpi=300) images = convert_from_path(pdf_file, dpi=dpi)
print(f"Number of pages in PDF: {len(images)}")
base_name = os.path.splitext(os.path.basename(pdf_file))[0] base_name = os.path.splitext(os.path.basename(pdf_file))[0]
image_filenames = [] image_filenames = []
@@ -240,6 +231,7 @@ def process_pptx(input_file, output_folder, duration, target_type, target_id):
'--headless', '--headless',
'--convert-to', 'pdf', '--convert-to', 'pdf',
'--outdir', output_folder, '--outdir', output_folder,
'--printer-resolution', '600',
input_file input_file
] ]
@@ -251,7 +243,7 @@ def process_pptx(input_file, output_folder, duration, target_type, target_id):
print(f"LibreOffice errors (if any): {result.stderr.decode()}") print(f"LibreOffice errors (if any): {result.stderr.decode()}")
# Step 2: Convert PDF to images and update playlist # Step 2: Convert PDF to images and update playlist
image_filenames = convert_pdf_to_images(pdf_file, output_folder, True) image_filenames = convert_pdf_to_images(pdf_file, output_folder, True, dpi=600)
# Verify we got images # Verify we got images
if not image_filenames: if not image_filenames:
@@ -306,7 +298,7 @@ def process_uploaded_files(app, files, media_type, duration, target_type, target
if media_type == 'image': if media_type == 'image':
add_image_to_playlist(app, file, filename, duration, target_type, target_id) add_image_to_playlist(app, file, filename, duration, target_type, target_id)
result['message'] = f"Image {filename} added to playlist" result['message'] = f"Image {filename} added to playlist"
log_upload('image', filename, target_type, target_name) log_upload('image', filename, target_type, target_id)
elif media_type == 'video': elif media_type == 'video':
# For videos, add to playlist then start conversion in background # For videos, add to playlist then start conversion in background
@@ -329,7 +321,7 @@ def process_uploaded_files(app, files, media_type, duration, target_type, target
threading.Thread(target=convert_video_and_update_playlist, threading.Thread(target=convert_video_and_update_playlist,
args=(app, file_path, filename, target_type, target_id, duration)).start() args=(app, file_path, filename, target_type, target_id, duration)).start()
result['message'] = f"Video {filename} added to playlist and being processed" result['message'] = f"Video {filename} added to playlist and being processed"
log_upload('video', filename, target_type, target_name) log_upload('video', filename, target_type, target_id)
elif media_type == 'pdf': elif media_type == 'pdf':
# For PDFs, convert to images and update playlist # For PDFs, convert to images and update playlist
@@ -337,7 +329,7 @@ def process_uploaded_files(app, files, media_type, duration, target_type, target
duration, target_type, target_id) duration, target_type, target_id)
if success: if success:
result['message'] = f"PDF {filename} processed successfully" result['message'] = f"PDF {filename} processed successfully"
log_process('pdf', filename, target_type, target_name) log_process('pdf', filename, target_type, target_id)
else: else:
result['success'] = False result['success'] = False
result['message'] = f"Error processing PDF file: {filename}" result['message'] = f"Error processing PDF file: {filename}"
@@ -348,7 +340,7 @@ def process_uploaded_files(app, files, media_type, duration, target_type, target
duration, target_type, target_id) duration, target_type, target_id)
if success: if success:
result['message'] = f"PowerPoint {filename} processed successfully" result['message'] = f"PowerPoint {filename} processed successfully"
log_process('ppt', filename, target_type, target_name) log_process('ppt', filename, target_type, target_id)
else: else:
result['success'] = False result['success'] = False
result['message'] = f"Error processing PowerPoint file: {filename}" result['message'] = f"Error processing PowerPoint file: {filename}"