Files
Transcriptarr/backend/API.md
Dasemu 6272efbcd5 feat(api): add REST API with 45+ endpoints
- Add workers API for pool management
- Add jobs API for queue operations
- Add scan-rules API for CRUD operations
- Add scanner API for control and status
- Add settings API for configuration management
- Add system API for resource monitoring
- Add filesystem API for path browsing
- Add setup wizard API endpoint
2026-01-16 16:57:59 +01:00

11 KiB

TranscriptorIO REST API

Documentación completa de las APIs REST del backend de TranscriptorIO.

🚀 Inicio Rápido

Ejecutar el servidor

# 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:

📋 Endpoints

System Status

GET /

Información básica de la API.

Response:

{
  "name": "TranscriptorIO API",
  "version": "1.0.0",
  "status": "running"
}

GET /health

Health check para monitoring.

Response:

{
  "status": "healthy",
  "database": "connected",
  "workers": 2,
  "queue_size": 5
}

GET /api/status

Estado completo del sistema.

Response:

{
  "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:

[
  {
    "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "message": "Job abc123 cancelled successfully"
}

POST /api/jobs/queue/clear

Limpiar trabajos completados.

Response:

{
  "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:

[
  {
    "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:

{
  "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:

{
  "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:

{
  "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:

{
  "paths": ["/media/anime", "/media/movies"],
  "recursive": true
}

Response:

{
  "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:

{
  "enabled": true,
  "cron_expression": "0 2 * * *",
  "paths": ["/media/anime"],
  "recursive": true
}

Response:

{
  "message": "Scheduler started successfully"
}

POST /api/scanner/scheduler/stop

Detener escaneo programado.

Response:

{
  "message": "Scheduler stopped successfully"
}

POST /api/scanner/watcher/start

Iniciar observador de archivos.

Request:

{
  "enabled": true,
  "paths": ["/media/anime"],
  "recursive": true
}

Response:

{
  "message": "File watcher started successfully"
}

POST /api/scanner/watcher/stop

Detener observador de archivos.

Response:

{
  "message": "File watcher stopped successfully"
}

POST /api/scanner/analyze

Analizar un archivo específico.

Query Params:

  • file_path (required): Ruta al archivo

Response:

{
  "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

# 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

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