conv / app.py
ketannnn's picture
Fix Hugging Face port binding issues (default to 7860)
b27b096
Raw
History Blame Contribute Delete
6.2 kB
import io
import os
import subprocess
import tempfile
import zipfile
from flask import Flask, render_template, request, send_file, jsonify
app = Flask(__name__)
# Max upload size: 100 MB
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024
ALLOWED_EXTENSIONS = {'.doc', '.docx', '.ppt', '.pptx', '.xls', '.xlsx'}
def is_allowed_file(filename):
_, ext = os.path.splitext(filename.lower())
return ext in ALLOWED_EXTENSIONS
@app.route('/')
def index():
return render_template('index.html')
@app.route('/convert', methods=['POST'])
def convert():
if 'files' not in request.files:
return jsonify({'error': 'No files found in the request.'}), 400
files = request.files.getlist('files')
if not files or all(file.filename == '' for file in files):
return jsonify({'error': 'No files selected for upload.'}), 400
# Build the ZIP archive in memory
zip_buffer = io.BytesIO()
converted_files_count = 0
failed_conversions = []
# Use TemporaryDirectory context manager to ensure automatic clean-up of files
with tempfile.TemporaryDirectory() as temp_dir:
for file in files:
if not file.filename:
continue
_, ext = os.path.splitext(file.filename.lower())
if ext not in ALLOWED_EXTENSIONS:
failed_conversions.append((file.filename, f"Unsupported extension '{ext}'"))
continue
# Save the uploaded file in the temporary directory
input_path = os.path.join(temp_dir, file.filename)
try:
file.save(input_path)
except Exception as e:
failed_conversions.append((file.filename, f"Failed to save file: {e}"))
continue
# Build the LibreOffice execution command
cmd = [
"soffice",
"--headless",
"--convert-to",
"pdf",
"--outdir",
temp_dir,
input_path
]
print(f"Web conversion: executing {' '.join(cmd)}")
try:
# Convert the file safely using subprocess list invocation with a 180s timeout per file
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=180
)
if result.returncode != 0:
failed_conversions.append((file.filename, f"LibreOffice failed (code {result.returncode})"))
continue
except subprocess.TimeoutExpired:
failed_conversions.append((file.filename, "Conversion timed out (180s)"))
continue
except Exception as e:
failed_conversions.append((file.filename, f"Conversion error: {e}"))
continue
# Verify that the PDF was successfully created
base_name, _ = os.path.splitext(file.filename)
pdf_filename = f"{base_name}.pdf"
pdf_path = os.path.join(temp_dir, pdf_filename)
if os.path.exists(pdf_path):
converted_files_count += 1
else:
failed_conversions.append((file.filename, "Expected PDF output was not created"))
# If zero files were successfully converted, return a detailed error response
if converted_files_count == 0:
error_details = ", ".join([f"'{name}': {reason}" for name, reason in failed_conversions])
return jsonify({'error': f'Failed to convert any files. Details: {error_details}'}), 400
# If only a single file was successfully converted, return the PDF directly
if converted_files_count == 1:
pdf_filename = None
pdf_path = None
for file in files:
base_name, _ = os.path.splitext(file.filename)
pdf_filename = f"{base_name}.pdf"
pdf_path = os.path.join(temp_dir, pdf_filename)
if os.path.exists(pdf_path):
break
if pdf_path and pdf_filename:
# Read PDF file into memory buffer so we can safely delete temporary directory
pdf_buffer = io.BytesIO()
with open(pdf_path, 'rb') as f:
pdf_buffer.write(f.read())
pdf_buffer.seek(0)
return send_file(
pdf_buffer,
mimetype='application/pdf',
as_attachment=True,
download_name=pdf_filename
)
# Create the ZIP archive containing all successfully converted PDFs (for 2 or more files)
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for file in files:
base_name, _ = os.path.splitext(file.filename)
pdf_filename = f"{base_name}.pdf"
pdf_path = os.path.join(temp_dir, pdf_filename)
if os.path.exists(pdf_path):
# Write to the zip archive with only the filename (avoiding temp directory paths)
zip_file.write(pdf_path, pdf_filename)
# Append a conversion log if any files failed
if failed_conversions:
log_lines = ["Conversion Report (Some files failed to convert):\n", "="*50, "\n"]
for name, reason in failed_conversions:
log_lines.append(f"FAILED: {name} | Reason: {reason}\n")
zip_file.writestr("conversion_errors.log", "".join(log_lines))
# Move pointer to the beginning of the memory buffer
zip_buffer.seek(0)
# Return the zip file for download
return send_file(
zip_buffer,
mimetype='application/zip',
as_attachment=True,
download_name='converted_pdfs.zip'
)
if __name__ == '__main__':
# Get port from environment (Hugging Face defaults to 7860) or default to 7860
port = int(os.environ.get('PORT', 7860))
app.run(host='0.0.0.0', port=port)