Files
fb2converter_server/app/main.py
2022-08-15 15:43:54 +03:00

129 lines
3.1 KiB
Python

import asyncio
import os
import os.path
import shutil
import time
from typing import AsyncIterator, Optional
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
if env_config.SENTRY_DSN:
sentry_sdk.init(
env_config.SENTRY_DSN,
)
router = APIRouter(tags=["converter"])
@router.post("/")
async def convert(
file: Optional[UploadFile] = File(None),
format: Optional[str] = Form(None),
):
if file is None or format is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="File and format required!"
)
format_lower = format.lower()
if format_lower not in ["epub", "mobi"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Bad 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=5 * 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)
try:
creation_time = os.path.getctime(target_path)
except FileNotFoundError:
continue
if (current_time - creation_time) // 3600 >= 3:
if is_file:
os.remove(target_path)
else:
shutil.rmtree(target_path)