kacapower's picture
Create main.py
2b0b869 verified
import os
import subprocess
from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.responses import FileResponse
from tempfile import NamedTemporaryFile
import uvicorn
app = FastAPI(title="Universal File Converter API")
# ---------------------------------------------------------
# The 300+ Format Routing Dictionary
# Expand this dictionary to include all 300 extensions you need.
# ---------------------------------------------------------
CONVERSION_ENGINES = {
# Documents -> LibreOffice
'pdf': 'libreoffice', 'docx': 'libreoffice', 'odt': 'libreoffice', 'xlsx': 'libreoffice', 'pptx': 'libreoffice',
# Images -> ImageMagick
'jpg': 'imagemagick', 'png': 'imagemagick', 'webp': 'imagemagick', 'gif': 'imagemagick', 'bmp': 'imagemagick',
# Media -> FFmpeg
'mp4': 'ffmpeg', 'mp3': 'ffmpeg', 'wav': 'ffmpeg', 'avi': 'ffmpeg', 'mkv': 'ffmpeg', 'flac': 'ffmpeg',
# Text & Markup -> Pandoc
'html': 'pandoc', 'md': 'pandoc', 'rst': 'pandoc', 'epub': 'pandoc', 'tex': 'pandoc'
}
def execute_conversion(input_path: str, output_path: str, target_ext: str):
"""Routes the file to the correct system engine based on the target extension."""
engine = CONVERSION_ENGINES.get(target_ext)
if not engine:
raise ValueError(f"Target format '{target_ext}' is not mapped to an engine.")
# Construct the specific CLI command
if engine == 'libreoffice':
out_dir = os.path.dirname(output_path)
cmd = ['soffice', '--headless', '--convert-to', target_ext, '--outdir', out_dir, input_path]
elif engine == 'imagemagick':
cmd = ['magick', input_path, output_path]
elif engine == 'ffmpeg':
# -y overwrites output files without asking
cmd = ['ffmpeg', '-y', '-i', input_path, output_path]
elif engine == 'pandoc':
cmd = ['pandoc', input_path, '-o', output_path]
try:
# Execute the command and capture any errors
subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Engine Error: {e.stderr.decode('utf-8')}")
@app.post("/convert")
async def convert_file(
file: UploadFile = File(...),
target_format: str = Form(...)
):
target_format = target_format.lower().strip('.')
# Extract original extension safely
filename_parts = file.filename.rsplit('.', 1)
input_ext = filename_parts[-1] if len(filename_parts) > 1 else "tmp"
base_name = filename_parts[0] if len(filename_parts) > 1 else "file"
# Write the uploaded file to a temporary location
with NamedTemporaryFile(delete=False, suffix=f".{input_ext}") as tmp_in:
tmp_in.write(await file.read())
input_path = tmp_in.name
# Define the output path in the same temporary directory
output_path = input_path.rsplit('.', 1)[0] + f".{target_format}"
try:
execute_conversion(input_path, output_path, target_format)
# Return the file to the frontend space
return FileResponse(
path=output_path,
filename=f"{base_name}_converted.{target_format}",
media_type='application/octet-stream'
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
# Hugging Face binds the Space to 0.0.0.0:7860
uvicorn.run(app, host="0.0.0.0", port=7860)