| from fastapi import APIRouter, UploadFile, File, Form, HTTPException |
| from fastapi.responses import FileResponse, JSONResponse |
| import os |
| import uuid |
| import aiofiles |
| from app.core.config import settings |
| from custom_logger import logger_config as logger |
| from app.db import crud |
| from app.services.worker import start_worker, is_worker_running |
|
|
| router = APIRouter() |
|
|
| @router.get("/") |
| async def index(): |
| return FileResponse('index.html') |
|
|
| @router.post("/api/tasks/upload") |
| async def create_task( |
| text: str = Form(None), |
| file: UploadFile = File(None), |
| voice: str = Form("4"), |
| speed: float = Form(1.0), |
| hide_from_ui: str = Form("") |
| ): |
| task_text = "" |
| |
| if text: |
| task_text = text |
| elif file: |
| try: |
| content = await file.read() |
| task_text = content.decode('utf-8') |
| except Exception as e: |
| logger.error(f"Error reading uploaded file: {e}") |
| raise HTTPException(status_code=400, detail="Could not read file content") |
| |
| if not task_text.strip(): |
| raise HTTPException(status_code=400, detail="No text provided") |
| |
| task_id = str(uuid.uuid4()) |
| filename = file.filename if file else f"{task_text[:20]}..." |
| hide_from_ui_val = 1 if hide_from_ui.lower() in ['true', '1'] else 0 |
| |
| await crud.insert_task(task_id, filename, task_text, voice, speed, 'not_started', hide_from_ui_val) |
| await start_worker() |
| |
| return JSONResponse(status_code=201, content={ |
| 'id': task_id, |
| 'filename': filename, |
| 'status': 'not_started', |
| 'message': 'Task created successfully' |
| }) |
|
|
| @router.get("/api/tasks") |
| async def get_tasks(): |
| rows, queue_ids, processing_count, avg_time = await crud.get_all_tasks() |
| |
| tasks = [] |
| for row in rows: |
| queue_position = None |
| estimated_start_seconds = None |
| |
| if row['status'] == 'not_started' and row['id'] in queue_ids: |
| queue_position = queue_ids.index(row['id']) + 1 |
| tasks_ahead = queue_position - 1 + processing_count |
| estimated_start_seconds = round(tasks_ahead * avg_time) |
| |
| tasks.append({ |
| 'id': row['id'], |
| 'filename': row['filename'], |
| 'status': row['status'], |
| 'result': "HIDDEN_IN_LIST_VIEW", |
| 'created_at': row['created_at'], |
| 'processed_at': row['processed_at'], |
| 'progress': row['progress'] or 0, |
| 'progress_text': row['progress_text'], |
| 'queue_position': queue_position, |
| 'estimated_start_seconds': estimated_start_seconds |
| }) |
| |
| return tasks |
|
|
| @router.get("/api/tasks/{task_id}") |
| async def get_task(task_id: str): |
| result = await crud.get_task_by_id(task_id) |
| if not result: |
| raise HTTPException(status_code=404, detail="Task not found") |
| |
| row, queue_position, estimated_start_seconds = result |
| |
| return { |
| 'id': row['id'], |
| 'filename': row['filename'], |
| 'text': row['text'], |
| 'status': row['status'], |
| 'result': row['text'], |
| 'output_file': row['output_file'], |
| 'created_at': row['created_at'], |
| 'processed_at': row['processed_at'], |
| 'progress': row['progress'] or 0, |
| 'progress_text': row['progress_text'], |
| 'queue_position': queue_position, |
| 'estimated_start_seconds': estimated_start_seconds |
| } |
|
|
| @router.get("/api/download/{task_id}") |
| async def download_task(task_id: str): |
| result = await crud.get_task_by_id(task_id) |
| if not result or not result[0]['output_file']: |
| raise HTTPException(status_code=404, detail="Audio file not found") |
| |
| row = result[0] |
| filepath = os.path.join(settings.UPLOAD_FOLDER, row['output_file']) |
| if not os.path.exists(filepath): |
| raise HTTPException(status_code=404, detail="File missing on server") |
| |
| return FileResponse(filepath, media_type="audio/wav", filename=f"tts_{task_id}.wav") |
|
|
| @router.get("/health") |
| async def health(): |
| return { |
| 'status': 'healthy', |
| 'service': 'tts-runner', |
| 'worker_running': is_worker_running() |
| } |
|
|