146 lines
5.3 KiB
Python
146 lines
5.3 KiB
Python
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import logging
|
||
import os
|
||
import uuid
|
||
from typing import Optional, List, Dict, Any
|
||
|
||
from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Query
|
||
from fastapi.responses import JSONResponse, FileResponse
|
||
|
||
from app.core.converter.conversion_manager import ConversionManager
|
||
from app.core.models.converter.conversion_models import (
|
||
ContentMetadata,
|
||
ConversionPriority,
|
||
ConversionStatus,
|
||
)
|
||
|
||
router = APIRouter(prefix="/api/converter", tags=["converter"])
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Глобальный singleton менеджера (можно заменить DI контейнером)
|
||
_conversion_manager: Optional[ConversionManager] = None
|
||
|
||
|
||
def get_manager() -> ConversionManager:
|
||
global _conversion_manager
|
||
if _conversion_manager is None:
|
||
_conversion_manager = ConversionManager()
|
||
return _conversion_manager
|
||
|
||
|
||
@router.post("/submit")
|
||
async def submit_conversion(
|
||
file: UploadFile = File(...),
|
||
title: str = Form(...),
|
||
description: Optional[str] = Form(None),
|
||
author: Optional[str] = Form(None),
|
||
collection: Optional[str] = Form(None),
|
||
tags: Optional[str] = Form(None), # CSV
|
||
language: Optional[str] = Form(None),
|
||
explicit: Optional[bool] = Form(None),
|
||
quality: str = Form("high"), # "high" | "low"
|
||
input_ext: Optional[str] = Form(None), # если неизвестно — попытаемся из файла
|
||
priority: int = Form(50),
|
||
trim: Optional[str] = Form(None),
|
||
custom: Optional[str] = Form(None), # произвольные ffmpeg-параметры через пробел
|
||
):
|
||
"""
|
||
Принимает файл и ставит задачу конвертации в очередь.
|
||
Возвращает task_id.
|
||
"""
|
||
try:
|
||
# Сохраняем входной файл во временное хранилище uploader-bot
|
||
uploads_dir = "uploader-bot/uploader-bot/data/uploads"
|
||
os.makedirs(uploads_dir, exist_ok=True)
|
||
input_name = file.filename or f"upload-{uuid.uuid4().hex}"
|
||
local_path = os.path.join(uploads_dir, input_name)
|
||
|
||
with open(local_path, "wb") as f:
|
||
f.write(await file.read())
|
||
|
||
# Определяем расширение, если не передано
|
||
in_ext = input_ext or os.path.splitext(input_name)[1].lstrip(".").lower() or "bin"
|
||
|
||
# Метаданные
|
||
md = ContentMetadata(
|
||
title=title,
|
||
description=description,
|
||
author=author,
|
||
collection=collection,
|
||
tags=[t.strip() for t in (tags.split(","))] if tags else [],
|
||
language=language,
|
||
explicit=explicit,
|
||
attributes={},
|
||
)
|
||
|
||
prio = ConversionPriority.NORMAL
|
||
try:
|
||
# нормализуем диапазон int -> enum
|
||
p_int = int(priority)
|
||
if p_int >= ConversionPriority.CRITICAL:
|
||
prio = ConversionPriority.CRITICAL
|
||
elif p_int >= ConversionPriority.HIGH:
|
||
prio = ConversionPriority.HIGH
|
||
elif p_int >= ConversionPriority.NORMAL:
|
||
prio = ConversionPriority.NORMAL
|
||
else:
|
||
prio = ConversionPriority.LOW
|
||
except Exception:
|
||
pass
|
||
|
||
custom_list: List[str] = []
|
||
if custom:
|
||
# Разбиваем по пробелам, без сложного парсинга
|
||
custom_list = [c for c in custom.split(" ") if c]
|
||
|
||
manager = get_manager()
|
||
task_id = await manager.process_upload(
|
||
local_input_path=local_path,
|
||
input_ext=in_ext,
|
||
quality="high" if quality == "high" else "low",
|
||
metadata=md,
|
||
priority=prio,
|
||
custom=custom_list,
|
||
trim=trim,
|
||
)
|
||
|
||
return JSONResponse({"task_id": task_id})
|
||
except Exception as e:
|
||
logger.exception("submit_conversion failed: %s", e)
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.get("/status/{task_id}")
|
||
async def get_status(task_id: str):
|
||
"""
|
||
Возвращает статус задачи.
|
||
"""
|
||
try:
|
||
manager = get_manager()
|
||
status = await manager.get_conversion_status(task_id)
|
||
return JSONResponse({"task_id": task_id, "status": status.value})
|
||
except Exception as e:
|
||
logger.exception("get_status failed: %s", e)
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
@router.get("/result/{task_id}")
|
||
async def get_result(task_id: str):
|
||
"""
|
||
Возвращает результат задачи с content_id, чанками и nft метаданными.
|
||
"""
|
||
try:
|
||
manager = get_manager()
|
||
res = await manager.handle_conversion_result(task_id)
|
||
if not res:
|
||
# если задача всё ещё идёт/в очереди
|
||
status = await manager.get_conversion_status(task_id)
|
||
if status in (ConversionStatus.QUEUED, ConversionStatus.RUNNING):
|
||
return JSONResponse({"task_id": task_id, "status": status.value})
|
||
raise HTTPException(status_code=404, detail="result not ready")
|
||
return JSONResponse(res.to_dict())
|
||
except Exception as e:
|
||
logger.exception("get_result failed: %s", e)
|
||
raise HTTPException(status_code=500, detail=str(e)) |