Compare commits
2 Commits
7f5991f60d
...
7b24245ddb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b24245ddb | ||
|
|
58694ff3f4 |
43
app/app.py
43
app/app.py
@@ -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)
|
||||||
|
|
||||||
|
|||||||
BIN
app/static/uploads/call-of-duty-black-3840x2160-23674.jpg
Normal file
BIN
app/static/uploads/call-of-duty-black-3840x2160-23674.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -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>
|
||||||
|
|||||||
@@ -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...';
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user