355 lines
14 KiB
Python
355 lines
14 KiB
Python
import os
|
|
import subprocess
|
|
from flask import Flask
|
|
from werkzeug.utils import secure_filename
|
|
from pdf2image import convert_from_path
|
|
from extensions import db
|
|
from models import Content, Player, Group
|
|
from utils.logger import log_upload, log_process
|
|
|
|
# Function to add image to playlist
|
|
def add_image_to_playlist(app, file, filename, duration, target_type, target_id):
|
|
"""
|
|
Save the image file and add it to the playlist database.
|
|
"""
|
|
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
|
# Only save if file does not already exist
|
|
if not os.path.exists(file_path):
|
|
file.save(file_path)
|
|
|
|
print(f"Adding image to playlist: {filename}, Target Type: {target_type}, Target ID: {target_id}")
|
|
|
|
if target_type == 'group':
|
|
group = Group.query.get_or_404(target_id)
|
|
for player in group.players:
|
|
new_content = Content(file_name=filename, duration=duration, player_id=player.id)
|
|
db.session.add(new_content)
|
|
player.playlist_version += 1
|
|
group.playlist_version += 1
|
|
# Log the action
|
|
log_upload('image', filename, 'group', group.name)
|
|
elif target_type == 'player':
|
|
player = Player.query.get_or_404(target_id)
|
|
new_content = Content(file_name=filename, duration=duration, player_id=target_id)
|
|
db.session.add(new_content)
|
|
player.playlist_version += 1
|
|
# Log the action
|
|
log_upload('image', filename, 'player', player.username)
|
|
|
|
db.session.commit()
|
|
|
|
# Video conversion functions
|
|
def convert_video(input_file, output_folder):
|
|
""" Converts a video file to MP4 format with H.264 codec, 720p resolution, and 30 FPS.
|
|
Args:
|
|
input_file (str): Path to the input video file.
|
|
output_folder (str): Path to the folder where the converted video will be saved.
|
|
Returns:
|
|
str: Path to the converted video file, or None if conversion fails.
|
|
"""
|
|
if not os.path.exists(output_folder):
|
|
os.makedirs(output_folder)
|
|
|
|
# Generate the output file path
|
|
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
|
output_file = os.path.join(output_folder, f"{base_name}.mp4")
|
|
|
|
# FFmpeg command to convert the video
|
|
command = [
|
|
"ffmpeg",
|
|
"-i", input_file, # Input file
|
|
"-c:v", "libx264", # Video codec: H.264
|
|
"-preset", "fast", # Encoding speed/quality tradeoff
|
|
"-crf", "23", # Constant Rate Factor (quality, lower is better)
|
|
"-vf", "scale=-1:1080", # Scale video to 1080p (preserve aspect ratio)
|
|
"-r", "30", # Frame rate: 30 FPS
|
|
"-c:a", "aac", # Audio codec: AAC
|
|
"-b:a", "128k", # Audio bitrate
|
|
output_file # Output file
|
|
]
|
|
|
|
try:
|
|
# Run the FFmpeg command
|
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
print(f"Video converted successfully: {output_file}")
|
|
return output_file
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error converting video: {e.stderr.decode()}")
|
|
return None
|
|
|
|
def convert_video_and_update_playlist(app, file_path, original_filename, target_type, target_id, duration):
|
|
print(f"Starting video conversion for: {file_path}")
|
|
converted_file = convert_video(file_path, app.config['UPLOAD_FOLDER'])
|
|
if converted_file:
|
|
converted_filename = os.path.basename(converted_file)
|
|
print(f"Video converted successfully: {converted_filename}")
|
|
|
|
# Use the application context to interact with the database
|
|
with app.app_context():
|
|
# Update the database with the converted filename
|
|
if target_type == 'group':
|
|
group = Group.query.get_or_404(target_id)
|
|
for player in group.players:
|
|
content = Content.query.filter_by(player_id=player.id, file_name=original_filename).first()
|
|
if content:
|
|
content.file_name = converted_filename
|
|
elif target_type == 'player':
|
|
content = Content.query.filter_by(player_id=target_id, file_name=original_filename).first()
|
|
if content:
|
|
content.file_name = converted_filename
|
|
|
|
db.session.commit()
|
|
print(f"Database updated with converted video: {converted_filename}")
|
|
|
|
# Delete the original file only if it exists
|
|
if os.path.exists(file_path):
|
|
os.remove(file_path)
|
|
print(f"Original file deleted: {file_path}")
|
|
else:
|
|
print(f"Video conversion failed for: {file_path}")
|
|
|
|
# PDF conversion functions
|
|
def convert_pdf_to_images(pdf_file, output_folder, delete_pdf=True):
|
|
"""
|
|
Convert a PDF file to images.
|
|
|
|
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, or empty list if conversion failed
|
|
"""
|
|
print(f"Converting PDF to images: {pdf_file}")
|
|
try:
|
|
images = convert_from_path(pdf_file, dpi=300)
|
|
print(f"Number of pages in PDF: {len(images)}")
|
|
base_name = os.path.splitext(os.path.basename(pdf_file))[0]
|
|
image_filenames = []
|
|
|
|
for i, image in enumerate(images):
|
|
image_filename = f"{base_name}_page_{i + 1}.jpg"
|
|
image_path = os.path.join(output_folder, image_filename)
|
|
image.save(image_path, 'JPEG')
|
|
image_filenames.append(image_filename)
|
|
print(f"Saved page {i + 1} as image: {image_path}")
|
|
|
|
# Delete the PDF file if requested
|
|
if delete_pdf and os.path.exists(pdf_file):
|
|
os.remove(pdf_file)
|
|
print(f"PDF file deleted: {pdf_file}")
|
|
|
|
return image_filenames
|
|
except Exception as e:
|
|
print(f"Error converting PDF to images: {e}")
|
|
return []
|
|
|
|
def update_playlist_with_files(image_filenames, duration, target_type, target_id):
|
|
"""
|
|
Add files to a player or group playlist and update version numbers.
|
|
|
|
Args:
|
|
image_filenames (list): List of filenames to add to playlist
|
|
duration (int): Duration in seconds for each file
|
|
target_type (str): 'player' or 'group'
|
|
target_id (int): ID of the player or group
|
|
|
|
Returns:
|
|
bool: True if successful, False otherwise
|
|
"""
|
|
try:
|
|
if target_type == 'group':
|
|
group = Group.query.get_or_404(target_id)
|
|
for player in group.players:
|
|
for image_filename in image_filenames:
|
|
new_content = Content(file_name=image_filename, duration=duration, player_id=player.id)
|
|
db.session.add(new_content)
|
|
player.playlist_version += 1
|
|
group.playlist_version += 1
|
|
elif target_type == 'player':
|
|
player = Player.query.get_or_404(target_id)
|
|
for image_filename in image_filenames:
|
|
new_content = Content(file_name=image_filename, duration=duration, player_id=target_id)
|
|
db.session.add(new_content)
|
|
player.playlist_version += 1
|
|
else:
|
|
print(f"Invalid target type: {target_type}")
|
|
return False
|
|
|
|
db.session.commit()
|
|
print(f"Added {len(image_filenames)} files to playlist")
|
|
return True
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
print(f"Error updating playlist: {e}")
|
|
return False
|
|
|
|
def process_pdf(input_file, output_folder, duration, target_type, target_id):
|
|
"""
|
|
Process a PDF file: convert to images and update playlist.
|
|
|
|
Args:
|
|
input_file (str): Path to the PDF file
|
|
output_folder (str): Path to save the images
|
|
duration (int): Duration in seconds for each image
|
|
target_type (str): 'player' or 'group'
|
|
target_id (int): ID of the player or group
|
|
|
|
Returns:
|
|
bool: True if successful, False otherwise
|
|
"""
|
|
# Ensure output folder exists
|
|
if not os.path.exists(output_folder):
|
|
os.makedirs(output_folder)
|
|
|
|
# Convert PDF to images
|
|
image_filenames = convert_pdf_to_images(input_file, output_folder)
|
|
|
|
# Update playlist with generated images
|
|
if image_filenames:
|
|
return update_playlist_with_files(image_filenames, duration, target_type, target_id)
|
|
return False
|
|
|
|
def process_pptx(input_file, output_folder, duration, target_type, target_id):
|
|
"""
|
|
Process a PPTX file: convert to PDF, then to images, and update playlist.
|
|
|
|
Args:
|
|
input_file (str): Path to the PPTX file
|
|
output_folder (str): Path to save the images
|
|
duration (int): Duration in seconds for each image
|
|
target_type (str): 'player' or 'group'
|
|
target_id (int): ID of the player or group
|
|
|
|
Returns:
|
|
bool: True if successful, False otherwise
|
|
"""
|
|
# Ensure output folder exists
|
|
if not os.path.exists(output_folder):
|
|
os.makedirs(output_folder)
|
|
|
|
# Step 1: Convert PPTX to PDF using LibreOffice
|
|
pdf_file = os.path.join(output_folder, os.path.splitext(os.path.basename(input_file))[0] + ".pdf")
|
|
command = [
|
|
'libreoffice',
|
|
'--headless',
|
|
'--convert-to', 'pdf',
|
|
'--outdir', output_folder,
|
|
input_file
|
|
]
|
|
|
|
print(f"Running LibreOffice command: {' '.join(command)}")
|
|
try:
|
|
result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
print(f"PPTX file converted to PDF: {pdf_file}")
|
|
|
|
# Step 2: Convert PDF to images and update playlist
|
|
image_filenames = convert_pdf_to_images(pdf_file, output_folder, True)
|
|
|
|
# Step 3: Delete the original PPTX file
|
|
if image_filenames and os.path.exists(input_file):
|
|
os.remove(input_file)
|
|
print(f"Original PPTX file deleted: {input_file}")
|
|
|
|
# Step 4: Update playlist with generated images
|
|
if image_filenames:
|
|
return update_playlist_with_files(image_filenames, duration, target_type, target_id)
|
|
return False
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error converting PPTX to PDF: {e.stderr.decode() if hasattr(e, 'stderr') else str(e)}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"Error processing PPTX file: {e}")
|
|
return False
|
|
|
|
def process_uploaded_files(app, files, media_type, duration, target_type, target_id):
|
|
"""
|
|
Process uploaded files based on media type and add them to playlists.
|
|
|
|
Returns:
|
|
list: List of result dictionaries with success status and messages
|
|
"""
|
|
results = []
|
|
|
|
# Get target name for logging
|
|
target_name = ""
|
|
if target_type == 'group':
|
|
group = Group.query.get_or_404(target_id)
|
|
target_name = group.name
|
|
elif target_type == 'player':
|
|
player = Player.query.get_or_404(target_id)
|
|
target_name = player.username
|
|
|
|
for file in files:
|
|
try:
|
|
# Generate a secure filename and save the file
|
|
filename = secure_filename(file.filename)
|
|
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
|
file.save(file_path)
|
|
|
|
print(f"Processing file: {filename}, Media Type: {media_type}")
|
|
result = {'filename': filename, 'success': True, 'message': ''}
|
|
|
|
if media_type == 'image':
|
|
add_image_to_playlist(app, file, filename, duration, target_type, target_id)
|
|
result['message'] = f"Image {filename} added to playlist"
|
|
log_upload('image', filename, target_type, target_name)
|
|
|
|
elif media_type == 'video':
|
|
# For videos, add to playlist then start conversion in background
|
|
if target_type == 'group':
|
|
group = Group.query.get_or_404(target_id)
|
|
for player in group.players:
|
|
new_content = Content(file_name=filename, duration=duration, player_id=player.id)
|
|
db.session.add(new_content)
|
|
player.playlist_version += 1
|
|
group.playlist_version += 1
|
|
elif target_type == 'player':
|
|
player = Player.query.get_or_404(target_id)
|
|
new_content = Content(file_name=filename, duration=duration, player_id=target_id)
|
|
db.session.add(new_content)
|
|
player.playlist_version += 1
|
|
|
|
db.session.commit()
|
|
# Start background conversion
|
|
import threading
|
|
threading.Thread(target=convert_video_and_update_playlist,
|
|
args=(app, file_path, filename, target_type, target_id, duration)).start()
|
|
result['message'] = f"Video {filename} added to playlist and being processed"
|
|
log_upload('video', filename, target_type, target_name)
|
|
|
|
elif media_type == 'pdf':
|
|
# For PDFs, convert to images and update playlist
|
|
success = process_pdf(file_path, app.config['UPLOAD_FOLDER'],
|
|
duration, target_type, target_id)
|
|
if success:
|
|
result['message'] = f"PDF {filename} processed successfully"
|
|
log_process('pdf', filename, target_type, target_name)
|
|
else:
|
|
result['success'] = False
|
|
result['message'] = f"Error processing PDF file: {filename}"
|
|
|
|
elif media_type == 'ppt':
|
|
# For PPT/PPTX, convert to PDF, then to images, and update playlist
|
|
success = process_pptx(file_path, app.config['UPLOAD_FOLDER'],
|
|
duration, target_type, target_id)
|
|
if success:
|
|
result['message'] = f"PowerPoint {filename} processed successfully"
|
|
log_process('ppt', filename, target_type, target_name)
|
|
else:
|
|
result['success'] = False
|
|
result['message'] = f"Error processing PowerPoint file: {filename}"
|
|
|
|
results.append(result)
|
|
|
|
except Exception as e:
|
|
print(f"Error processing file {file.filename}: {e}")
|
|
results.append({
|
|
'filename': file.filename,
|
|
'success': False,
|
|
'message': f"Error processing file {file.filename}: {str(e)}"
|
|
})
|
|
|
|
return results |