# Transcriptarr REST API Documentación completa de las APIs REST del backend de Transcriptarr. ## 🚀 Inicio Rápido ### Ejecutar el servidor ```bash # Usando el CLI python backend/cli.py server --host 0.0.0.0 --port 8000 # Con auto-reload (desarrollo) python backend/cli.py server --reload # Con múltiples workers (producción) python backend/cli.py server --workers 4 ``` ### Documentación interactiva Una vez iniciado el servidor, accede a: - **Swagger UI**: http://localhost:8000/docs - **ReDoc**: http://localhost:8000/redoc ## 📋 Endpoints ### System Status #### `GET /` Información básica de la API. **Response:** ```json { "name": "Transcriptarr API", "version": "1.0.0", "status": "running" } ``` #### `GET /health` Health check para monitoring. **Response:** ```json { "status": "healthy", "database": "connected", "workers": 2, "queue_size": 5 } ``` #### `GET /api/status` Estado completo del sistema. **Response:** ```json { "system": { "status": "running", "uptime_seconds": 3600.5 }, "workers": { "total_workers": 2, "cpu_workers": 1, "gpu_workers": 1, "idle_workers": 1, "busy_workers": 1, "total_jobs_completed": 42, "total_jobs_failed": 2 }, "queue": { "total": 100, "queued": 5, "processing": 2, "completed": 90, "failed": 3 }, "scanner": { "scheduler_running": true, "next_scan_time": "2026-01-13T02:00:00", "watcher_running": true } } ``` --- ## 👷 Workers API (`/api/workers`) ### `GET /api/workers` Lista todos los workers activos. **Response:** ```json [ { "worker_id": "worker-cpu-0", "worker_type": "cpu", "device_id": null, "status": "busy", "current_job_id": "abc123", "jobs_completed": 10, "jobs_failed": 0, "uptime_seconds": 3600.5, "current_job_progress": 45.2, "current_job_eta": 120 } ] ``` ### `GET /api/workers/stats` Estadísticas del pool de workers. **Response:** ```json { "total_workers": 2, "cpu_workers": 1, "gpu_workers": 1, "idle_workers": 1, "busy_workers": 1, "stopped_workers": 0, "error_workers": 0, "total_jobs_completed": 42, "total_jobs_failed": 2, "uptime_seconds": 3600.5, "is_running": true } ``` ### `GET /api/workers/{worker_id}` Obtener estado de un worker específico. **Response:** Same as individual worker in list ### `POST /api/workers` Añadir un nuevo worker al pool. **Request:** ```json { "worker_type": "gpu", "device_id": 0 } ``` **Response:** Worker status object ### `DELETE /api/workers/{worker_id}` Remover un worker del pool. **Query Params:** - `timeout` (float, default=30.0): Timeout en segundos **Response:** ```json { "message": "Worker worker-cpu-0 removed successfully" } ``` ### `POST /api/workers/pool/start` Iniciar el pool de workers. **Query Params:** - `cpu_workers` (int, default=0) - `gpu_workers` (int, default=0) **Response:** ```json { "message": "Worker pool started: 1 CPU workers, 1 GPU workers" } ``` ### `POST /api/workers/pool/stop` Detener el pool de workers. **Query Params:** - `timeout` (float, default=30.0) **Response:** ```json { "message": "Worker pool stopped successfully" } ``` --- ## 📋 Jobs API (`/api/jobs`) ### `GET /api/jobs` Lista de trabajos con paginación. **Query Params:** - `status_filter` (optional): queued, processing, completed, failed, cancelled - `page` (int, default=1): Número de página - `page_size` (int, default=50): Items por página **Response:** ```json { "jobs": [ { "id": "abc123", "file_path": "/media/anime/episode.mkv", "file_name": "episode.mkv", "status": "completed", "priority": 10, "source_lang": "ja", "target_lang": "es", "quality_preset": "fast", "transcribe_or_translate": "transcribe", "progress": 100.0, "current_stage": "finalizing", "eta_seconds": null, "created_at": "2026-01-12T10:00:00", "started_at": "2026-01-12T10:00:05", "completed_at": "2026-01-12T10:05:30", "output_path": "/media/anime/episode.es.srt", "segments_count": 245, "error": null, "retry_count": 0, "worker_id": "worker-gpu-0", "vram_used_mb": 4096, "processing_time_seconds": 325.5, "model_used": "large-v3", "device_used": "cuda:0" } ], "total": 100, "page": 1, "page_size": 50 } ``` ### `GET /api/jobs/stats` Estadísticas de la cola. **Response:** ```json { "total_jobs": 100, "queued": 5, "processing": 2, "completed": 90, "failed": 3, "cancelled": 0 } ``` ### `GET /api/jobs/{job_id}` Obtener un trabajo específico. **Response:** Job object (same as in list) ### `POST /api/jobs` Crear un nuevo trabajo de transcripción. **Request:** ```json { "file_path": "/media/anime/Attack on Titan S04E01.mkv", "file_name": "Attack on Titan S04E01.mkv", "source_lang": "ja", "target_lang": "es", "quality_preset": "fast", "transcribe_or_translate": "transcribe", "priority": 10, "is_manual_request": true } ``` **Response:** Created job object ### `POST /api/jobs/{job_id}/retry` Reintentar un trabajo fallido. **Response:** Updated job object ### `DELETE /api/jobs/{job_id}` Cancelar un trabajo. **Response:** ```json { "message": "Job abc123 cancelled successfully" } ``` ### `POST /api/jobs/queue/clear` Limpiar trabajos completados. **Response:** ```json { "message": "Cleared 42 completed jobs" } ``` --- ## 📏 Scan Rules API (`/api/scan-rules`) ### `GET /api/scan-rules` Lista todas las reglas de escaneo. **Query Params:** - `enabled_only` (bool, default=false): Solo reglas habilitadas **Response:** ```json [ { "id": 1, "name": "Japanese anime without Spanish subs", "enabled": true, "priority": 10, "conditions": { "audio_language_is": "ja", "audio_language_not": null, "audio_track_count_min": null, "has_embedded_subtitle_lang": null, "missing_embedded_subtitle_lang": "es", "missing_external_subtitle_lang": "es", "file_extension": ".mkv,.mp4" }, "action": { "action_type": "transcribe", "target_language": "es", "quality_preset": "fast", "job_priority": 5 }, "created_at": "2026-01-12T10:00:00", "updated_at": null } ] ``` ### `GET /api/scan-rules/{rule_id}` Obtener una regla específica. **Response:** Rule object (same as in list) ### `POST /api/scan-rules` Crear una nueva regla de escaneo. **Request:** ```json { "name": "Japanese anime without Spanish subs", "enabled": true, "priority": 10, "conditions": { "audio_language_is": "ja", "missing_embedded_subtitle_lang": "es", "missing_external_subtitle_lang": "es", "file_extension": ".mkv,.mp4" }, "action": { "action_type": "transcribe", "target_language": "es", "quality_preset": "fast", "job_priority": 5 } } ``` **Response:** Created rule object ### `PUT /api/scan-rules/{rule_id}` Actualizar una regla. **Request:** Same as POST (all fields optional) **Response:** Updated rule object ### `DELETE /api/scan-rules/{rule_id}` Eliminar una regla. **Response:** ```json { "message": "Scan rule 1 deleted successfully" } ``` ### `POST /api/scan-rules/{rule_id}/toggle` Activar/desactivar una regla. **Response:** Updated rule object --- ## 🔍 Scanner API (`/api/scanner`) ### `GET /api/scanner/status` Estado del scanner. **Response:** ```json { "scheduler_enabled": true, "scheduler_running": true, "next_scan_time": "2026-01-13T02:00:00", "watcher_enabled": true, "watcher_running": true, "watched_paths": ["/media/anime", "/media/movies"], "last_scan_time": "2026-01-12T02:00:00", "total_scans": 1523 } ``` ### `POST /api/scanner/scan` Ejecutar escaneo manual. **Request:** ```json { "paths": ["/media/anime", "/media/movies"], "recursive": true } ``` **Response:** ```json { "scanned_files": 150, "matched_files": 25, "jobs_created": 25, "skipped_files": 125, "paths_scanned": ["/media/anime", "/media/movies"] } ``` ### `POST /api/scanner/scheduler/start` Iniciar escaneo programado. **Request:** ```json { "enabled": true, "cron_expression": "0 2 * * *", "paths": ["/media/anime"], "recursive": true } ``` **Response:** ```json { "message": "Scheduler started successfully" } ``` ### `POST /api/scanner/scheduler/stop` Detener escaneo programado. **Response:** ```json { "message": "Scheduler stopped successfully" } ``` ### `POST /api/scanner/watcher/start` Iniciar observador de archivos. **Request:** ```json { "enabled": true, "paths": ["/media/anime"], "recursive": true } ``` **Response:** ```json { "message": "File watcher started successfully" } ``` ### `POST /api/scanner/watcher/stop` Detener observador de archivos. **Response:** ```json { "message": "File watcher stopped successfully" } ``` ### `POST /api/scanner/analyze` Analizar un archivo específico. **Query Params:** - `file_path` (required): Ruta al archivo **Response:** ```json { "file_path": "/media/anime/episode.mkv", "audio_tracks": [ { "index": 0, "codec": "aac", "language": "ja", "channels": 2 } ], "embedded_subtitles": [], "external_subtitles": [ { "path": "/media/anime/episode.en.srt", "language": "en" } ], "duration_seconds": 1440.5, "is_video": true } ``` --- ## 🔐 Autenticación > **TODO**: Implementar autenticación con JWT tokens --- ## 📊 Códigos de Error - `200 OK`: Éxito - `201 Created`: Recurso creado - `400 Bad Request`: Parámetros inválidos - `404 Not Found`: Recurso no encontrado - `409 Conflict`: Conflicto (ej: duplicado) - `500 Internal Server Error`: Error del servidor --- ## 🧪 Testing ### cURL Examples ```bash # Get system status curl http://localhost:8000/api/status # Create a job curl -X POST http://localhost:8000/api/jobs \ -H "Content-Type: application/json" \ -d '{ "file_path": "/media/anime/episode.mkv", "file_name": "episode.mkv", "target_lang": "es", "quality_preset": "fast" }' # Add a GPU worker curl -X POST http://localhost:8000/api/workers \ -H "Content-Type: application/json" \ -d '{ "worker_type": "gpu", "device_id": 0 }' ``` ### Python Example ```python import requests # Base URL BASE_URL = "http://localhost:8000" # Create a job response = requests.post( f"{BASE_URL}/api/jobs", json={ "file_path": "/media/anime/episode.mkv", "file_name": "episode.mkv", "target_lang": "es", "quality_preset": "fast" } ) job = response.json() print(f"Job created: {job['id']}") # Check job status response = requests.get(f"{BASE_URL}/api/jobs/{job['id']}") status = response.json() print(f"Job status: {status['status']} - {status['progress']}%") ``` --- ## 📝 Notas - Todas las fechas están en formato ISO 8601 UTC - Los idiomas usan códigos ISO 639-1 (2 letras: ja, en, es, fr, etc.) - La paginación usa índices base-1 (primera página = 1) - Los workers se identifican por ID único generado automáticamente