Download src/api/main.py from fokan/pdf-4: direct link, hf CLI and curl.
- Browser
- Download file 8.86 kB
-
https://huggingface.co/spaces/fokan/pdf-4/resolve/main/src/api/main.py
- Command line
-
hf download hf://spaces/fokan/pdf-4/src/api/main.py
-
curl -L -o main.py https://huggingface.co/spaces/fokan/pdf-4/resolve/main/src/api/main.py
8.86 kB
| #!/usr/bin/env python3 | |
| """ | |
| Enhanced DOCX to PDF Converter | |
| Professional FastAPI Backend with Docker Support | |
| """ | |
| import os | |
| import logging | |
| import uuid | |
| from pathlib import Path | |
| from typing import Optional, List | |
| import base64 | |
| import json | |
| from fastapi import FastAPI, File, UploadFile, Form, HTTPException, BackgroundTasks | |
| from fastapi.responses import FileResponse, JSONResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel | |
| # Import utility modules | |
| from src.utils.config import Config | |
| from src.utils.file_handler import FileHandler | |
| from src.utils.converter import DocumentConverter | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Initialize utility classes | |
| file_handler = FileHandler(Config.TEMP_DIR) | |
| converter = DocumentConverter() | |
| app = FastAPI( | |
| title=Config.API_TITLE, | |
| description=Config.API_DESCRIPTION, | |
| version=Config.API_VERSION | |
| ) | |
| # Add CORS middleware for browser integration | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=Config.CORS_ORIGINS, | |
| allow_credentials=Config.CORS_CREDENTIALS, | |
| allow_methods=Config.CORS_METHODS, | |
| allow_headers=Config.CORS_HEADERS, | |
| ) | |
| # Mount static files if templates directory exists | |
| if os.path.exists("templates"): | |
| app.mount("/static", StaticFiles(directory="templates"), name="static") | |
| # Serve index.html at root if it exists | |
| if os.path.exists("templates/index.html"): | |
| from fastapi.responses import HTMLResponse | |
| async def read_index(): | |
| with open("templates/index.html", "r", encoding="utf-8") as f: | |
| return f.read() | |
| # Request/Response Models | |
| class ConversionRequest(BaseModel): | |
| """Request model for base64 conversion""" | |
| file_content: str # base64 encoded file | |
| filename: str | |
| class BatchConversionRequest(BaseModel): | |
| """Request model for batch conversion""" | |
| files: List[ConversionRequest] | |
| class ConversionResponse(BaseModel): | |
| """Response model for conversion results""" | |
| success: bool | |
| pdf_url: Optional[str] = None | |
| message: Optional[str] = None | |
| error: Optional[str] = None | |
| async def startup_event(): | |
| """Initialize application on startup""" | |
| logger.info("Starting Enhanced DOCX to PDF Converter...") | |
| # Validate LibreOffice installation | |
| if not converter.validate_libreoffice(): | |
| logger.warning("LibreOffice validation failed - conversions may not work") | |
| # Create temp directory if it doesn't exist | |
| os.makedirs(Config.TEMP_DIR, exist_ok=True) | |
| logger.info("Application started successfully") | |
| async def health_check(): | |
| """Health check endpoint""" | |
| return {"status": "healthy", "version": Config.API_VERSION} | |
| async def convert_docx( | |
| background_tasks: BackgroundTasks, | |
| file: Optional[UploadFile] = File(None), | |
| file_content: Optional[str] = Form(None), | |
| filename: Optional[str] = Form(None) | |
| ): | |
| """ | |
| Convert DOCX to PDF | |
| Supports two input methods: | |
| 1. Multipart file upload (file parameter) | |
| 2. Base64 encoded content (file_content and filename parameters) | |
| """ | |
| temp_dir = None | |
| input_path = None | |
| output_path = None | |
| try: | |
| # Create temporary directory for this conversion | |
| temp_dir = file_handler.create_temp_directory() | |
| # Handle file upload | |
| if file and file.filename: | |
| # Validate file size | |
| if file.size and file.size > Config.MAX_FILE_SIZE: | |
| raise HTTPException(status_code=413, detail="File too large") | |
| # Validate file extension | |
| if not file_handler.validate_file_extension(file.filename, Config.ALLOWED_EXTENSIONS): | |
| raise HTTPException(status_code=400, detail="Invalid file type") | |
| # Save uploaded file | |
| content = await file.read() | |
| input_path = file_handler.save_uploaded_file(temp_dir, file.filename, content) | |
| # Handle base64 content | |
| elif file_content and filename: | |
| # Validate filename | |
| if not file_handler.validate_file_extension(filename, Config.ALLOWED_EXTENSIONS): | |
| raise HTTPException(status_code=400, detail="Filename must have .docx extension") | |
| # Decode base64 content | |
| file_data = converter.decode_base64_content(file_content) | |
| if file_data is None: | |
| raise HTTPException(status_code=400, detail="Invalid base64 content") | |
| # Save decoded file | |
| input_path = file_handler.save_uploaded_file(temp_dir, filename, file_data) | |
| else: | |
| raise HTTPException(status_code=400, detail="Either file or file_content+filename must be provided") | |
| # Generate output path | |
| output_filename = os.path.splitext(os.path.basename(input_path))[0] + ".pdf" | |
| output_path = os.path.join(temp_dir, output_filename) | |
| # Perform conversion | |
| if not converter.convert_docx_to_pdf(input_path, output_path): | |
| raise HTTPException(status_code=500, detail="Conversion failed") | |
| # Return success response | |
| pdf_url = f"/download/{os.path.basename(temp_dir)}/{output_filename}" | |
| return ConversionResponse( | |
| success=True, | |
| pdf_url=pdf_url, | |
| message="Conversion successful" | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Conversion error: {e}") | |
| raise HTTPException(status_code=500, detail=f"Conversion failed: {str(e)}") | |
| finally: | |
| # Cleanup will be handled by download endpoint or background task | |
| pass | |
| async def download_pdf(temp_id: str, filename: str): | |
| """Download converted PDF file""" | |
| try: | |
| file_path = f"{Config.TEMP_DIR}/{temp_id}/{filename}" | |
| if not os.path.exists(file_path): | |
| raise HTTPException(status_code=404, detail="File not found") | |
| return FileResponse( | |
| path=file_path, | |
| filename=filename, | |
| media_type='application/pdf' | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Download error: {e}") | |
| raise HTTPException(status_code=500, detail="Download failed") | |
| async def batch_convert(request: BatchConversionRequest): | |
| """ | |
| Batch convert multiple DOCX files to PDF | |
| """ | |
| results = [] | |
| for file_req in request.files: | |
| try: | |
| # Create temporary directory for this conversion | |
| temp_dir = file_handler.create_temp_directory() | |
| # Decode base64 content | |
| file_data = converter.decode_base64_content(file_req.file_content) | |
| if file_data is None: | |
| results.append(ConversionResponse( | |
| success=False, | |
| error="Invalid base64 content" | |
| )) | |
| continue | |
| # Save decoded file | |
| input_path = file_handler.save_uploaded_file(temp_dir, file_req.filename, file_data) | |
| # Validate saved file | |
| if not file_handler.validate_file_extension(file_req.filename, Config.ALLOWED_EXTENSIONS): | |
| results.append(ConversionResponse( | |
| success=False, | |
| error="Invalid file content" | |
| )) | |
| continue | |
| # Generate output path | |
| output_filename = os.path.splitext(os.path.basename(input_path))[0] + ".pdf" | |
| output_path = os.path.join(temp_dir, output_filename) | |
| # Perform conversion | |
| if converter.convert_docx_to_pdf(input_path, output_path): | |
| pdf_url = f"/download/{os.path.basename(temp_dir)}/{output_filename}" | |
| results.append(ConversionResponse( | |
| success=True, | |
| pdf_url=pdf_url, | |
| message="Conversion successful" | |
| )) | |
| else: | |
| results.append(ConversionResponse( | |
| success=False, | |
| error="Conversion failed" | |
| )) | |
| except Exception as e: | |
| logger.error(f"Batch conversion error: {e}") | |
| results.append(ConversionResponse( | |
| success=False, | |
| error=str(e) | |
| )) | |
| return results |