Models (6 + 1 association table): - User: Authentication with bcrypt, admin role check, last_login tracking - Player: Digital signage devices with auth codes, status tracking, online detection - Group: Player/content organization with statistics properties - Content: Media files with type detection, file size helpers, position ordering - ServerLog: Audit trail with class methods for logging levels - PlayerFeedback: Player status updates with error tracking - group_content: Many-to-many association table for groups and content Model improvements: - Added type hints to all methods and properties - Added database indexes on frequently queried columns (username, auth_code, group_id, player_id, position, level, timestamp, status) - Added comprehensive docstrings - Added helper properties (is_online, is_admin, file_size_mb, etc.) - Added relationship back_populates for bidirectional navigation - Added timestamps (created_at, updated_at, last_seen, uploaded_at) Utils (4 modules): - logger.py: Logging utility with level-based functions (info, warning, error) - uploads.py: File upload handling with progress tracking, video optimization - group_player_management.py: Player/group status tracking and bulk operations - pptx_converter.py: PowerPoint to PDF conversion using LibreOffice Utils improvements: - Full type hints on all functions - Comprehensive error handling - Progress tracking for long-running operations - Video optimization (H.264, 30fps, max 1080p, 8Mbps) - Helper functions for time formatting and statistics - Proper logging of all operations Performance optimizations: - Database indexes on all foreign keys and frequently filtered columns - Lazy loading for relationships where appropriate - Efficient queries with proper ordering - Helper properties to avoid repeated calculations Ready for template migration and testing
107 lines
3.3 KiB
Python
107 lines
3.3 KiB
Python
"""PowerPoint to PDF converter using LibreOffice."""
|
|
import os
|
|
import subprocess
|
|
import time
|
|
import logging
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def cleanup_libreoffice_processes() -> None:
|
|
"""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: str, output_dir: str) -> Optional[str]:
|
|
"""Convert PPTX to PDF using LibreOffice for highest quality.
|
|
|
|
This function is the core component of the PPTX processing workflow:
|
|
PPTX → PDF (this function) → JPG images (handled in uploads.py)
|
|
|
|
Args:
|
|
pptx_path: Path to the PPTX file
|
|
output_dir: Directory to save the PDF
|
|
|
|
Returns:
|
|
Path to the generated PDF file, or None if conversion failed
|
|
"""
|
|
try:
|
|
# Clean up any existing LibreOffice processes
|
|
cleanup_libreoffice_processes()
|
|
|
|
# Ensure output directory exists
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
# Use LibreOffice to convert PPTX to PDF
|
|
cmd = [
|
|
'libreoffice',
|
|
'--headless',
|
|
'--convert-to', 'pdf',
|
|
'--outdir', output_dir,
|
|
'--invisible',
|
|
'--nodefault',
|
|
pptx_path
|
|
]
|
|
|
|
logger.info(f"Converting PPTX to PDF using LibreOffice: {pptx_path}")
|
|
|
|
# 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:
|
|
logger.error(f"LibreOffice conversion failed: {result.stderr}")
|
|
logger.error(f"LibreOffice stdout: {result.stdout}")
|
|
cleanup_libreoffice_processes()
|
|
return None
|
|
|
|
# Find the generated PDF file
|
|
base_name = os.path.splitext(os.path.basename(pptx_path))[0]
|
|
pdf_path = os.path.join(output_dir, f"{base_name}.pdf")
|
|
|
|
if os.path.exists(pdf_path):
|
|
logger.info(f"PDF conversion successful: {pdf_path}")
|
|
cleanup_libreoffice_processes()
|
|
return pdf_path
|
|
else:
|
|
logger.error(f"PDF file not found after conversion: {pdf_path}")
|
|
cleanup_libreoffice_processes()
|
|
return None
|
|
|
|
except subprocess.TimeoutExpired:
|
|
logger.error("LibreOffice conversion timed out (300s)")
|
|
cleanup_libreoffice_processes()
|
|
return None
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in PPTX to PDF conversion: {e}")
|
|
cleanup_libreoffice_processes()
|
|
return None
|
|
|
|
|
|
def validate_pptx_file(filepath: str) -> bool:
|
|
"""Validate if file is a valid PowerPoint file.
|
|
|
|
Args:
|
|
filepath: Path to file to validate
|
|
|
|
Returns:
|
|
True if valid PPTX file, False otherwise
|
|
"""
|
|
if not os.path.exists(filepath):
|
|
return False
|
|
|
|
# Check file extension
|
|
if not filepath.lower().endswith(('.ppt', '.pptx')):
|
|
return False
|
|
|
|
# Check file size (must be > 0)
|
|
if os.path.getsize(filepath) == 0:
|
|
return False
|
|
|
|
return True
|