Compare commits

...

2 Commits

Author SHA1 Message Date
DigiServer Developer
7b24245ddb updated the upload functionality to handle large files and added a new image file 2025-08-21 16:27:16 +03:00
DigiServer Developer
58694ff3f4 Update all changes before rebase and push 2025-08-21 16:26:53 +03:00
6 changed files with 99 additions and 15 deletions

View File

@@ -69,8 +69,12 @@ db_path = os.path.join(instance_dir, 'dashboard.db')
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}' app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Set maximum content length to 1GB # Set maximum content length to 2GB
app.config['MAX_CONTENT_LENGTH'] = 2048 * 2048 * 2048 # 2GB, adjust as needed app.config['MAX_CONTENT_LENGTH'] = 2048 * 1024 * 1024 # 2GB, adjust as needed
# Set longer timeouts for file processing
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 300 # 5 minutes for static files
app.config['PERMANENT_SESSION_LIFETIME'] = 1800 # 30 minutes for sessions
# Ensure the instance folder exists # Ensure the instance folder exists
os.makedirs(app.instance_path, exist_ok=True) os.makedirs(app.instance_path, exist_ok=True)
@@ -95,6 +99,22 @@ login_manager.login_view = 'login'
migrate = Migrate(app, db) migrate = Migrate(app, db)
# Add error handlers for better user experience
@app.errorhandler(413)
def request_entity_too_large(error):
flash('File too large. Please upload files smaller than 2GB.', 'danger')
return redirect(url_for('dashboard'))
@app.errorhandler(408)
def request_timeout(error):
flash('Request timed out. Please try uploading smaller files or try again later.', 'danger')
return redirect(url_for('dashboard'))
@app.errorhandler(500)
def internal_server_error(error):
flash('An internal server error occurred. Please try again or contact support.', 'danger')
return redirect(url_for('dashboard'))
@login_manager.user_loader @login_manager.user_loader
def load_user(user_id): def load_user(user_id):
return db.session.get(User, int(user_id)) return db.session.get(User, int(user_id))
@@ -213,8 +233,23 @@ def upload_content():
flash('Please select a target type and target ID.', 'danger') flash('Please select a target type and target ID.', 'danger')
return redirect(url_for('upload_content')) return redirect(url_for('upload_content'))
# Process uploaded files and get results try:
results = process_uploaded_files(app, files, media_type, duration, target_type, target_id) # Process uploaded files and get results
results = process_uploaded_files(app, files, media_type, duration, target_type, target_id)
# Check for any failed uploads
failed_files = [r for r in results if not r.get('success', True)]
if failed_files:
for failed in failed_files:
flash(f"Error uploading {failed.get('filename', 'unknown file')}: {failed.get('message', 'Unknown error')}", 'warning')
else:
flash('All files uploaded and processed successfully!', 'success')
except Exception as e:
print(f"Error in upload_content: {e}")
import traceback
traceback.print_exc()
flash(f'Upload failed: {str(e)}', 'danger')
return redirect(return_url) return redirect(return_url)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -128,9 +128,18 @@
☰ ☰
</div> </div>
<div class="flex-grow-1"> <!-- Media Thumbnail and Name -->
<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>
<<<<<<< HEAD
=======
>>>>>>> 2255cc2 (Show media thumbnails in manage group page, matching player page style)
<form action="{{ url_for('edit_group_media_route', group_id=group.id, content_id=media.id) }}" method="post" class="d-flex align-items-center"> <form action="{{ url_for('edit_group_media_route', group_id=group.id, content_id=media.id) }}" method="post" class="d-flex align-items-center">
<div class="input-group me-2"> <div class="input-group me-2">
<span class="input-group-text">seconds</span> <span class="input-group-text">seconds</span>

View File

@@ -243,7 +243,7 @@
statusMessage.textContent = 'Converting PDF to 4K images. This may take a while...'; statusMessage.textContent = 'Converting PDF to 4K images. This may take a while...';
break; break;
case 'ppt': case 'ppt':
statusMessage.textContent = 'Converting PowerPoint to 4K images. This may take a while...'; statusMessage.textContent = 'Converting PowerPoint to images (PPTX → PDF → Images). This may take 2-5 minutes...';
break; break;
default: default:
statusMessage.textContent = 'Uploading and processing your files. Please wait...'; statusMessage.textContent = 'Uploading and processing your files. Please wait...';

View File

@@ -9,12 +9,23 @@ The converted PDF is then processed by the main upload workflow for 4K image gen
import os import os
import subprocess import subprocess
import logging import logging
import signal
import time
# Set up logging # Set up logging
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def cleanup_libreoffice_processes():
"""Clean up any hanging LibreOffice processes"""
try:
subprocess.run(['pkill', '-f', 'soffice'], capture_output=True, timeout=10)
time.sleep(1) # Give processes time to terminate
except Exception as e:
logger.warning(f"Failed to cleanup LibreOffice processes: {e}")
def pptx_to_pdf_libreoffice(pptx_path, output_dir): def pptx_to_pdf_libreoffice(pptx_path, output_dir):
""" """
Convert PPTX to PDF using LibreOffice for highest quality. Convert PPTX to PDF using LibreOffice for highest quality.
@@ -30,6 +41,9 @@ def pptx_to_pdf_libreoffice(pptx_path, output_dir):
str: Path to the generated PDF file, or None if conversion failed str: Path to the generated PDF file, or None if conversion failed
""" """
try: try:
# Clean up any existing LibreOffice processes
cleanup_libreoffice_processes()
# Ensure output directory exists # Ensure output directory exists
os.makedirs(output_dir, exist_ok=True) os.makedirs(output_dir, exist_ok=True)
@@ -39,14 +53,19 @@ def pptx_to_pdf_libreoffice(pptx_path, output_dir):
'--headless', '--headless',
'--convert-to', 'pdf', '--convert-to', 'pdf',
'--outdir', output_dir, '--outdir', output_dir,
'--invisible', # Run without any UI
'--nodefault', # Don't start with default template
pptx_path pptx_path
] ]
logger.info(f"Converting PPTX to PDF using LibreOffice: {pptx_path}") logger.info(f"Converting PPTX to PDF using LibreOffice: {pptx_path}")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) # Increase timeout to 300 seconds (5 minutes) for large presentations
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode != 0: if result.returncode != 0:
logger.error(f"LibreOffice conversion failed: {result.stderr}") logger.error(f"LibreOffice conversion failed: {result.stderr}")
logger.error(f"LibreOffice stdout: {result.stdout}")
cleanup_libreoffice_processes() # Clean up on failure
return None return None
# Find the generated PDF file # Find the generated PDF file
@@ -55,16 +74,22 @@ def pptx_to_pdf_libreoffice(pptx_path, output_dir):
if os.path.exists(pdf_path): if os.path.exists(pdf_path):
logger.info(f"PDF conversion successful: {pdf_path}") logger.info(f"PDF conversion successful: {pdf_path}")
cleanup_libreoffice_processes() # Clean up after success
return pdf_path return pdf_path
else: else:
logger.error(f"PDF file not found after conversion: {pdf_path}") logger.error(f"PDF file not found after conversion: {pdf_path}")
cleanup_libreoffice_processes() # Clean up on failure
return None return None
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
logger.error("LibreOffice conversion timed out (120s)") logger.error("LibreOffice conversion timed out (300s)")
cleanup_libreoffice_processes() # Clean up on timeout
return None return None
except Exception as e: except Exception as e:
logger.error(f"Error in PPTX to PDF conversion: {e}") logger.error(f"Error in PPTX to PDF conversion: {e}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
cleanup_libreoffice_processes() # Clean up on error
return None return None

View File

@@ -290,21 +290,33 @@ def process_pptx(input_file, output_folder, duration, target_type, target_id):
try: try:
# Step 1: Convert PPTX to PDF using LibreOffice for vector quality # Step 1: Convert PPTX to PDF using LibreOffice for vector quality
print("Step 1: Converting PPTX to PDF...")
from utils.pptx_converter import pptx_to_pdf_libreoffice from utils.pptx_converter import pptx_to_pdf_libreoffice
pdf_file = pptx_to_pdf_libreoffice(input_file, output_folder) pdf_file = pptx_to_pdf_libreoffice(input_file, output_folder)
if not pdf_file: if not pdf_file:
print("Error: Failed to convert PPTX to PDF") print("Error: Failed to convert PPTX to PDF")
print("This could be due to:")
print("- LibreOffice not properly installed")
print("- Corrupted PPTX file")
print("- Insufficient memory")
print("- File permission issues")
return False return False
print(f"PPTX successfully converted to PDF: {pdf_file}") print(f"PPTX successfully converted to PDF: {pdf_file}")
# Step 2: Use the same PDF to images workflow as direct PDF uploads # Step 2: Use the same PDF to images workflow as direct PDF uploads
print("Step 2: Converting PDF to JPG images...")
# Convert PDF to JPG images (300 DPI, same as PDF workflow) # Convert PDF to JPG images (300 DPI, same as PDF workflow)
image_filenames = convert_pdf_to_images(pdf_file, output_folder, delete_pdf=True, dpi=300) image_filenames = convert_pdf_to_images(pdf_file, output_folder, delete_pdf=True, dpi=300)
if not image_filenames: if not image_filenames:
print("Error: Failed to convert PDF to images") print("Error: Failed to convert PDF to images")
print("This could be due to:")
print("- poppler-utils not properly installed")
print("- PDF corruption during conversion")
print("- Insufficient disk space")
print("- Memory issues during image processing")
return False return False
print(f"Generated {len(image_filenames)} JPG images from PPTX → PDF") print(f"Generated {len(image_filenames)} JPG images from PPTX → PDF")
@@ -315,9 +327,12 @@ def process_pptx(input_file, output_folder, duration, target_type, target_id):
print(f"Original PPTX file deleted: {input_file}") print(f"Original PPTX file deleted: {input_file}")
# Step 4: Update playlist with generated images in sequential order # Step 4: Update playlist with generated images in sequential order
print("Step 3: Adding images to playlist...")
success = update_playlist_with_files(image_filenames, duration, target_type, target_id) success = update_playlist_with_files(image_filenames, duration, target_type, target_id)
if success: if success:
print(f"Successfully processed PPTX: {len(image_filenames)} images added to playlist") print(f"Successfully processed PPTX: {len(image_filenames)} images added to playlist")
else:
print("Error: Failed to add images to playlist database")
return success return success
except Exception as e: except Exception as e: