import asyncio import os import os.path import shutil import time from typing import AsyncIterator import uuid from fastapi import FastAPI, APIRouter, File, UploadFile, Form, HTTPException, status from fastapi.responses import StreamingResponse import aiofiles import aiofiles.os import aiofiles.ospath from fastapi_utils.tasks import repeat_every import sentry_sdk from config import env_config sentry_sdk.init( env_config.SENTRY_DSN, ) router = APIRouter(tags=["converter"]) @router.post("/") async def convert( file: UploadFile = File({}), format: str = Form({}), ): format_lower = format.lower() if format_lower not in ["epub", "mobi"]: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Wrong format!" ) temp_uuid = uuid.uuid1() temp_filename = str(temp_uuid) + ".fb2" converted_temp_filename = str(temp_uuid) + "." + format_lower try: async with aiofiles.open(temp_filename, "wb") as f: while content := await file.read(1024): if isinstance(content, str): content = content.encode() await f.write(content) proc = await asyncio.create_subprocess_exec( "./bin/fb2c", "convert", "--to", format, temp_filename, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) _, stderr = await proc.communicate() finally: await aiofiles.os.remove(temp_filename) if proc.returncode != 0 or len(stderr) != 0: try: await aiofiles.os.remove(converted_temp_filename) except FileNotFoundError: pass raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Can't convert!" ) async def result_iterator() -> AsyncIterator[bytes]: try: async with aiofiles.open(converted_temp_filename, "rb") as f: while data := await f.read(2048): yield data finally: await aiofiles.os.remove(converted_temp_filename) return StreamingResponse(result_iterator()) @router.get("/healthcheck") async def healthcheck(): return "Ok!" app = FastAPI() app.include_router(router) @app.on_event("startup") @repeat_every(seconds=60, raise_exceptions=True) def remove_temp_files(): current_time = time.time() try: os.remove("./conversion.log") except IOError: pass for f in os.listdir("/tmp/"): target_path = f"/tmp/{f}" is_file = os.path.isfile(target_path) creation_time = os.path.getctime(target_path) if (current_time - creation_time) // 3600 >= 3: if is_file: os.remove(target_path) else: shutil.rmtree(target_path)