Compare commits
5 Commits
v1.1.0
...
7b24245ddb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b24245ddb | ||
|
|
58694ff3f4 | ||
| 7f5991f60d | |||
|
|
5e4950563c | ||
| 091e985ff2 |
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)
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
def create_default_user(db, User, bcrypt):
|
def create_default_user(db, User, bcrypt):
|
||||||
username = os.getenv('DEFAULT_USER', 'admin')
|
# Use ADMIN_USER and ADMIN_PASSWORD to match docker-compose environment variables
|
||||||
password = os.getenv('DEFAULT_PASSWORD', '1234')
|
username = os.getenv('ADMIN_USER', os.getenv('DEFAULT_USER', 'admin'))
|
||||||
|
password = os.getenv('ADMIN_PASSWORD', os.getenv('DEFAULT_PASSWORD', '1234'))
|
||||||
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
|
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||||
existing_user = User.query.filter_by(username=username).first()
|
existing_user = User.query.filter_by(username=username).first()
|
||||||
if not existing_user:
|
if not existing_user:
|
||||||
|
|||||||
BIN
app/static/uploads/Castle_192452700-CSL._V392937836_RI_.jpg
Normal file
BIN
app/static/uploads/Castle_192452700-CSL._V392937836_RI_.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 331 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 432 KiB |
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 |
Binary file not shown.
@@ -110,35 +110,44 @@
|
|||||||
|
|
||||||
<ul class="list-group sortable-list" id="groupMediaList">
|
<ul class="list-group sortable-list" id="groupMediaList">
|
||||||
{% for media in content %}
|
{% for media in content %}
|
||||||
<li class="list-group-item d-flex align-items-center {{ 'dark-mode' if theme == 'dark' else '' }}"
|
<li class="list-group-item d-flex align-items-center {{ 'dark-mode' if theme == 'dark' else '' }}"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
data-id="{{ media.id }}"
|
data-id="{{ media.id }}"
|
||||||
data-position="{{ loop.index0 }}">
|
data-position="{{ loop.index0 }}">
|
||||||
<!-- Checkbox for bulk selection -->
|
<!-- Checkbox for bulk selection -->
|
||||||
<div class="me-2">
|
<div class="me-2">
|
||||||
<input class="form-check-input media-checkbox"
|
<input class="form-check-input media-checkbox"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="selected_content"
|
name="selected_content"
|
||||||
value="{{ media.id }}">
|
value="{{ media.id }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Drag handle -->
|
<!-- Drag handle -->
|
||||||
<div class="drag-handle me-2" title="Drag to reorder">
|
<div class="drag-handle me-2" title="Drag to reorder">
|
||||||
<i class="bi bi-grip-vertical"></i>
|
<i class="bi bi-grip-vertical"></i>
|
||||||
☰
|
☰
|
||||||
</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>
|
||||||
<form action="{{ url_for('edit_group_media', group_id=group.id, content_id=media.id) }}" method="post" class="d-flex align-items-center">
|
<<<<<<< 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">
|
||||||
<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>
|
||||||
<input type="number" class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" name="duration" value="{{ media.duration }}" required>
|
<input type="number" class="form-control {{ 'dark-mode' if theme == 'dark' else '' }}" name="duration" value="{{ media.duration }}" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-warning me-2">Edit</button>
|
<button type="submit" class="btn btn-warning me-2">Edit</button>
|
||||||
</form>
|
</form>
|
||||||
<form action="{{ url_for('delete_group_media', group_id=group.id, content_id=media.id) }}" method="post" style="display:inline;">
|
<form action="{{ url_for('delete_group_media_route', group_id=group.id, content_id=media.id) }}" method="post" style="display:inline;">
|
||||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this media?');">Delete</button>
|
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this media?');">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ def add_image_to_playlist(app, file, filename, duration, target_type, target_id)
|
|||||||
"""
|
"""
|
||||||
Save the image file and add it to the playlist database.
|
Save the image file and add it to the playlist database.
|
||||||
"""
|
"""
|
||||||
# Ensure we use absolute path for upload folder
|
# Use simple path resolution for containerized environment
|
||||||
upload_folder = app.config['UPLOAD_FOLDER']
|
upload_folder = app.config['UPLOAD_FOLDER']
|
||||||
if not os.path.isabs(upload_folder):
|
# In container, working directory is /app, so static/uploads resolves correctly
|
||||||
upload_folder = os.path.abspath(upload_folder)
|
print(f"Upload folder config: {upload_folder}")
|
||||||
|
|
||||||
# Ensure upload folder exists
|
# Ensure upload folder exists
|
||||||
if not os.path.exists(upload_folder):
|
if not os.path.exists(upload_folder):
|
||||||
@@ -55,10 +55,12 @@ def convert_video(input_file, output_folder):
|
|||||||
"""
|
"""
|
||||||
Converts a video file to MP4 format with H.264 codec.
|
Converts a video file to MP4 format with H.264 codec.
|
||||||
"""
|
"""
|
||||||
# Ensure we use absolute path for output folder
|
# Use simple path resolution for containerized environment
|
||||||
if not os.path.isabs(output_folder):
|
if not os.path.isabs(output_folder):
|
||||||
output_folder = os.path.abspath(output_folder)
|
# In container, relative paths work from /app directory
|
||||||
print(f"Converted output folder to absolute path: {output_folder}")
|
print(f"Using relative path: {output_folder}")
|
||||||
|
else:
|
||||||
|
print(f"Using absolute path: {output_folder}")
|
||||||
|
|
||||||
if not os.path.exists(output_folder):
|
if not os.path.exists(output_folder):
|
||||||
os.makedirs(output_folder, exist_ok=True)
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
@@ -98,11 +100,9 @@ def convert_video_and_update_playlist(app, file_path, original_filename, target_
|
|||||||
"""
|
"""
|
||||||
print(f"Starting video conversion for: {file_path}")
|
print(f"Starting video conversion for: {file_path}")
|
||||||
|
|
||||||
# Ensure we use absolute path for upload folder
|
# Use simple path resolution for containerized environment
|
||||||
upload_folder = app.config['UPLOAD_FOLDER']
|
upload_folder = app.config['UPLOAD_FOLDER']
|
||||||
if not os.path.isabs(upload_folder):
|
print(f"Upload folder: {upload_folder}")
|
||||||
upload_folder = os.path.abspath(upload_folder)
|
|
||||||
print(f"Converted upload folder to absolute path: {upload_folder}")
|
|
||||||
|
|
||||||
converted_file = convert_video(file_path, upload_folder)
|
converted_file = convert_video(file_path, upload_folder)
|
||||||
if converted_file:
|
if converted_file:
|
||||||
@@ -140,25 +140,7 @@ def convert_pdf_to_images(pdf_file, output_folder, delete_pdf=True, dpi=300):
|
|||||||
Uses standard 300 DPI for reliable conversion.
|
Uses standard 300 DPI for reliable conversion.
|
||||||
"""
|
"""
|
||||||
print(f"Converting PDF to JPG images: {pdf_file} at {dpi} DPI")
|
print(f"Converting PDF to JPG images: {pdf_file} at {dpi} DPI")
|
||||||
print(f"Original output folder: {output_folder}")
|
print(f"Output folder: {output_folder}")
|
||||||
|
|
||||||
# Force absolute path resolution to ensure we use the app directory
|
|
||||||
if not os.path.isabs(output_folder):
|
|
||||||
# If relative path, resolve from the current working directory
|
|
||||||
output_folder = os.path.abspath(output_folder)
|
|
||||||
print(f"Converted relative path to absolute: {output_folder}")
|
|
||||||
else:
|
|
||||||
print(f"Using provided absolute path: {output_folder}")
|
|
||||||
|
|
||||||
# Ensure we're using the app static folder, not workspace root
|
|
||||||
if output_folder.endswith('static/uploads'):
|
|
||||||
# Check if we're accidentally using workspace root instead of app folder
|
|
||||||
expected_app_path = '/opt/digiserver/app/static/uploads'
|
|
||||||
if output_folder != expected_app_path:
|
|
||||||
print(f"WARNING: Correcting path from {output_folder} to {expected_app_path}")
|
|
||||||
output_folder = expected_app_path
|
|
||||||
|
|
||||||
print(f"Final output folder: {output_folder}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ensure output folder exists
|
# Ensure output folder exists
|
||||||
@@ -266,11 +248,6 @@ def process_pdf(input_file, output_folder, duration, target_type, target_id):
|
|||||||
print(f"Processing PDF file: {input_file}")
|
print(f"Processing PDF file: {input_file}")
|
||||||
print(f"Output folder: {output_folder}")
|
print(f"Output folder: {output_folder}")
|
||||||
|
|
||||||
# Ensure we have absolute path for output folder
|
|
||||||
if not os.path.isabs(output_folder):
|
|
||||||
output_folder = os.path.abspath(output_folder)
|
|
||||||
print(f"Converted output folder to absolute path: {output_folder}")
|
|
||||||
|
|
||||||
# Ensure output folder exists
|
# Ensure output folder exists
|
||||||
if not os.path.exists(output_folder):
|
if not os.path.exists(output_folder):
|
||||||
os.makedirs(output_folder, exist_ok=True)
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
@@ -306,11 +283,6 @@ def process_pptx(input_file, output_folder, duration, target_type, target_id):
|
|||||||
print(f"Processing PPTX file using PDF workflow: {input_file}")
|
print(f"Processing PPTX file using PDF workflow: {input_file}")
|
||||||
print(f"Output folder: {output_folder}")
|
print(f"Output folder: {output_folder}")
|
||||||
|
|
||||||
# Ensure we have absolute path for output folder
|
|
||||||
if not os.path.isabs(output_folder):
|
|
||||||
output_folder = os.path.abspath(output_folder)
|
|
||||||
print(f"Converted output folder to absolute path: {output_folder}")
|
|
||||||
|
|
||||||
# Ensure output folder exists
|
# Ensure output folder exists
|
||||||
if not os.path.exists(output_folder):
|
if not os.path.exists(output_folder):
|
||||||
os.makedirs(output_folder, exist_ok=True)
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
@@ -318,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")
|
||||||
@@ -341,11 +325,14 @@ def process_pptx(input_file, output_folder, duration, target_type, target_id):
|
|||||||
if os.path.exists(input_file):
|
if os.path.exists(input_file):
|
||||||
os.remove(input_file)
|
os.remove(input_file)
|
||||||
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:
|
||||||
@@ -377,10 +364,9 @@ def process_uploaded_files(app, files, media_type, duration, target_type, target
|
|||||||
# Generate a secure filename and save the file
|
# Generate a secure filename and save the file
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
|
|
||||||
# Ensure we use absolute path for upload folder
|
# Use simple path resolution for containerized environment
|
||||||
upload_folder = app.config['UPLOAD_FOLDER']
|
upload_folder = app.config['UPLOAD_FOLDER']
|
||||||
if not os.path.isabs(upload_folder):
|
print(f"Upload folder: {upload_folder}")
|
||||||
upload_folder = os.path.abspath(upload_folder)
|
|
||||||
|
|
||||||
# Ensure upload folder exists
|
# Ensure upload folder exists
|
||||||
if not os.path.exists(upload_folder):
|
if not os.path.exists(upload_folder):
|
||||||
|
|||||||
@@ -8,18 +8,16 @@ services:
|
|||||||
image: digiserver:latest
|
image: digiserver:latest
|
||||||
container_name: digiserver
|
container_name: digiserver
|
||||||
ports:
|
ports:
|
||||||
- "8880:5000"
|
- "80:5000"
|
||||||
environment:
|
environment:
|
||||||
- FLASK_APP=app.py
|
- FLASK_APP=app.py
|
||||||
- FLASK_RUN_HOST=0.0.0.0
|
- FLASK_RUN_HOST=0.0.0.0
|
||||||
- DEFAULT_USER=admin
|
- ADMIN_USER=admin
|
||||||
- DEFAULT_PASSWORD=Initial01!
|
- ADMIN_PASSWORD=Initial01!
|
||||||
- SECRET_KEY=Ma_Duc_Dupa_Merele_Lui_Ana
|
- SECRET_KEY=Ma_Duc_Dupa_Merele_Lui_Ana
|
||||||
volumes:
|
volumes:
|
||||||
# Persistent data volumes
|
# Bind mount the app folder for easier development and debugging
|
||||||
- ./data/instance:/app/instance
|
- ./app:/app
|
||||||
- ./data/uploads:/app/static/uploads
|
|
||||||
- ./data/resurse:/app/static/resurse
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:5000/"]
|
test: ["CMD", "curl", "-f", "http://localhost:5000/"]
|
||||||
|
|||||||
Reference in New Issue
Block a user