Add leftover media management, PDF to PNG conversion, video delete, and playlist duration editing

Features added:
- Leftover media management page in admin panel with delete functionality for images and videos
- Individual file delete buttons for leftover media
- PDF to PNG conversion (each page becomes a separate image at Full HD resolution)
- Delete functionality for leftover video files
- Enhanced playlist management with duration editing for all content types
- Improved dark mode support for playlist management page
- Content type badges with color coding
- Better styling for duration input fields with save functionality
- Fixed URL generation for duration update endpoint
This commit is contained in:
DigiServer Developer
2025-11-15 02:20:15 +02:00
parent 930a5bf636
commit 2e3e181bb2
4 changed files with 249 additions and 35 deletions

View File

@@ -295,17 +295,58 @@ def process_video_file_extended(filepath: str, filename: str) -> tuple[bool, str
def process_pdf_file(filepath: str, filename: str) -> tuple[bool, str]:
"""Process PDF files."""
"""Process PDF files by converting each page to PNG images."""
try:
from pdf2image import convert_from_path
from pathlib import Path
# Basic PDF validation - check if it's a valid PDF
with open(filepath, 'rb') as f:
header = f.read(5)
if header != b'%PDF-':
return False, "Invalid PDF file"
log_action('info', f'PDF validated: {filename}')
return True, "PDF processed successfully"
log_action('info', f'Converting PDF to images: {filename}')
# Convert PDF pages to images at Full HD resolution
images = convert_from_path(
filepath,
dpi=150, # Good quality for Full HD display
fmt='png',
size=(1920, 1080) # Direct Full HD output
)
if not images:
return False, "No pages found in PDF"
# Generate base filename without extension
base_filename = Path(filename).stem
upload_folder = os.path.dirname(filepath)
# Save each page directly as PNG
converted_files = []
for idx, image in enumerate(images, start=1):
# Create filename for this page
page_filename = f"{base_filename}_page{idx:03d}.png"
page_filepath = os.path.join(upload_folder, page_filename)
# Save the image directly without additional optimization
image.save(page_filepath, 'PNG', optimize=True)
converted_files.append((page_filepath, page_filename))
log_action('info', f'Converted PDF page {idx}/{len(images)}: {page_filename}')
log_action('info', f'PDF converted successfully: {len(images)} pages from {filename}')
# Return success with file info for later processing
return True, f"PDF converted to {len(images)} images"
except ImportError:
return False, "pdf2image library not installed. Install with: pip install pdf2image"
except Exception as e:
import traceback
error_details = traceback.format_exc()
log_action('error', f'PDF processing error: {str(e)}\n{error_details}')
return False, f"PDF processing error: {str(e)}"
@@ -421,32 +462,38 @@ def process_presentation_file(filepath: str, filename: str) -> tuple[bool, str]:
return False, f"Presentation processing error: {str(e)}"
def create_fullhd_image(img):
"""Create a Full HD (1920x1080) image from PIL Image object, centered on white background."""
from PIL import Image as PILImage
target_size = (1920, 1080)
# Resize maintaining aspect ratio
img_copy = img.copy()
img_copy.thumbnail(target_size, PILImage.Resampling.LANCZOS)
# Create canvas with white background
fullhd_img = PILImage.new('RGB', target_size, (255, 255, 255))
# Center the image
x = (target_size[0] - img_copy.width) // 2
y = (target_size[1] - img_copy.height) // 2
if img_copy.mode == 'RGBA':
fullhd_img.paste(img_copy, (x, y), img_copy)
else:
fullhd_img.paste(img_copy, (x, y))
return fullhd_img
def optimize_image_to_fullhd(filepath: str) -> bool:
"""Optimize and resize image to Full HD (1920x1080) maintaining aspect ratio."""
"""Optimize and resize image file to Full HD (1920x1080) maintaining aspect ratio."""
try:
from PIL import Image
img = Image.open(filepath)
# Target Full HD resolution
target_size = (1920, 1080)
# Calculate resize maintaining aspect ratio
img.thumbnail(target_size, Image.Resampling.LANCZOS)
# Create Full HD canvas with white background
fullhd_img = Image.new('RGB', target_size, (255, 255, 255))
# Center the image on the canvas
x = (target_size[0] - img.width) // 2
y = (target_size[1] - img.height) // 2
if img.mode == 'RGBA':
fullhd_img.paste(img, (x, y), img)
else:
fullhd_img.paste(img, (x, y))
# Save optimized image
fullhd_img = create_fullhd_image(img)
fullhd_img.save(filepath, 'PNG', optimize=True)
return True
@@ -510,6 +557,61 @@ def upload_media():
detected_type = 'pdf'
processing_success, processing_message = process_pdf_file(filepath, filename)
# For PDFs, pages are converted to individual images
# We need to add each page image as a separate content item
if processing_success and "converted to" in processing_message.lower():
# Find all page images that were created
base_name = os.path.splitext(filename)[0]
page_pattern = f"{base_name}_page*.png"
import glob
page_files = sorted(glob.glob(os.path.join(upload_folder, page_pattern)))
if page_files:
max_position = 0
if playlist_id:
playlist = Playlist.query.get(playlist_id)
max_position = db.session.query(db.func.max(playlist_content.c.position))\
.filter(playlist_content.c.playlist_id == playlist_id)\
.scalar() or 0
# Add each page as separate content
for page_file in page_files:
page_filename = os.path.basename(page_file)
# Create content record for page
page_content = Content(
filename=page_filename,
content_type='image',
duration=duration,
file_size=os.path.getsize(page_file)
)
db.session.add(page_content)
db.session.flush()
# Add to playlist if specified
if playlist_id:
max_position += 1
stmt = playlist_content.insert().values(
playlist_id=playlist_id,
content_id=page_content.id,
position=max_position,
duration=duration
)
db.session.execute(stmt)
uploaded_count += 1
# Increment playlist version if pages were added
if playlist_id and page_files:
playlist.version += 1
# Delete original PDF file
if os.path.exists(filepath):
os.remove(filepath)
log_action('info', f'Removed original PDF after conversion: {filename}')
continue # Skip normal content creation below
elif file_ext in ['ppt', 'pptx']:
detected_type = 'pptx'
processing_success, processing_message = process_presentation_file(filepath, filename)
@@ -562,6 +664,11 @@ def upload_media():
if playlist_id and slide_files:
playlist.version += 1
# Delete original PPTX file
if os.path.exists(filepath):
os.remove(filepath)
log_action('info', f'Removed original PPTX after conversion: {filename}')
continue # Skip normal content creation below
else: