Ir al contenido

Seguir un job y obtener sus resultados

GET /v1/jobs/{id}

Mientras el job corre, el progreso se actualiza en cada petición. En vez de consultar una y otra vez, escucha los webhooks: job.succeeded, job.failed y job.canceled traen el job tal como lo muestra esta petición.

queued → processing → succeeded | failed | canceled

Un job espera en queued mientras tu organización ya tiene en marcha tantos jobs como permite su plan (max_concurrent_jobs); empieza en cuanto uno termina. progress (0–100) es el del job entero.

tasks lista todo lo que se está produciendo, cada cosa con su status (pending, running, done, stopped), progress (0–100, o null mientras corre sin un porcentaje medible) y tiempos:

type
import La copia desde input.url, con bytes y total_bytes.
upload Una subida progresiva que va llegando, con bytes y total_bytes.
analysis La lectura de la fuente.
video Una por calidad (resolution). Las calidades avanzan juntas, porque se codifican de los mismos fotogramas.
audio Una por idioma: track, language, name y mode: copy (se reutiliza el audio de la fuente, sin pérdida) o encode.
subtitles count pistas.
mp4, mp3, poster, storyboard Cada entregable.
streaming Los manifiestos HLS y DASH (formats).
Campo
queued_seconds La espera por hueco en tu plan.
import_seconds La copia desde input.url, si el job tenía una.
processing_seconds Desde el inicio del análisis hasta que los resultados están listos, sin la cola ni la subida. Si el video se procesó mientras llegaba, cuenta desde el final de la subida o la copia: es la espera una vez está el archivo.
speed Segundos de video por segundo de proceso: 33,5 es 33,5× tiempo real.
stages analysis_seconds, encoding_seconds, assembly_seconds.
timeline Cuándo empezó y terminó cada etapa (created_at, dispatched_at, import_started_at, imported_at, analysis_started_at, analyzed_at, encoded_at, completed_at), para dibujarlas en un mismo eje.
"outputs": {
"streaming": {
"hls_url": "https://…/master.m3u8",
"dash_url": "https://…/manifest.mpd",
"variants": [{ "resolution": "1080p", "width": 1920, "height": 1080, "size_bytes": 612345678 }],
"size_bytes": 640123456,
"audio_size_bytes": 27000000
},
"mp4": [{ "resolution": "720p", "width": 1280, "height": 720, "url": "https://…", "size_bytes": 301234567 }],
"mp3": { "url": "https://…", "size_bytes": 57600000 },
"posters": [{ "url": "https://…", "size_bytes": 84512 }],
"storyboard": { "image_url": "https://…/sprite-1-000.jpg", "image_urls": ["…"], "vtt_url": "https://…/storyboard.vtt", "size_bytes": 1203344 },
"audio_tracks": [{ "language": "spa", "name": "Español", "default": true }],
"subtitles": [{ "language": "spa", "name": "Español (forzados)", "forced": true, "url": "https://…/subtitles-0.vtt" }],
"quality": { "metric": "vmaf", "model": "vmaf_v0.6.1", "sampled_seconds": 96, "variants": [{ "resolution": "1080p", "vmaf": 95.8, "vmaf_min": 93.1 }] },
"total_size_bytes": 999123456,
"source_size_bytes": 4812345678
}

width y height son lo que salió, tras fit y la rotación. Un storyboard largo se divide en varias imágenes (hojas de 10 filas); image_url es la primera, image_urls todas, y el WebVTT asocia cada momento a su miniatura.

Cada resultado tiene size_bytes, lo que pesa publicado. En streaming, size_bytes es la carpeta entera: HLS y DASH comparten segmentos, así que cuentan una vez; el de cada variante es su video, y audio_size_bytes es el audio que comparten todas. total_size_bytes es todo lo publicado del job; source_size_bytes, la fuente.

Con quality_metrics: true, outputs.quality puntúa cómo se ve cada variante frente a la fuente, de 0 a 100, en muestras repartidas por el video (el video entero si es corto): vmaf es la media de las muestras y vmaf_min, la más débil. Desde 93 aproximadamente la diferencia apenas se ve; 80–93 es buena; por debajo de 60, pobre. Se compara a 1080p (o al tamaño de la fuente, si es menor) con el modelo estándar vmaf_v0.6.1. Las variantes con marca de agua no se puntúan, porque la marca contaría como defecto. Sin quality_metrics vale null.

  • source: qué es la fuente (duración, tamaño, frecuencia de fotogramas, bitrate, códecs, audio_tracks, subtitle_tracks).
  • adjustments: correcciones aplicadas para que los resultados se reproduzcan en todas partes: deinterlaced, frame_rate_normalized y frame_rate_capped (con from y to), rotation_applied (degrees), anamorphic_corrected, hdr_tone_mapped.
  • skipped: calidades omitidas por ser mayores que la fuente (on_upscale: "skip").
  • skipped_tracks: pistas pedidas que no salieron: not_found (una opcional), image_subtitles, too_many.
  • request: los ajustes con los que se creó el job, con los valores por defecto aplicados: de ahí parte un reintento.
  • billed_minutes: los minutos facturados del job por categoría, cuando terminó bien (solo en GET /v1/jobs/{id}).

error dice de quién es el problema y si volver a intentarlo puede servir:

"error": { "code": "unreadable_source", "category": "source", "retryable": false, "message": "…" }
code category retryable Qué pasó, qué hacer
unsupported_source source no El archivo no puede dar lo pedido: sin audio y se pidió MP3, menor que una calidad con on_upscale: "fail", una pista obligatoria que no existe, un video no admitido. Cambia la petición o el archivo.
unreadable_source source no El archivo está dañado, incompleto (un MP4 sin su índice, cortado a mitad de subida) o no es audio ni video. Súbelo de nuevo, completo.
source_import_failed source sí No se pudo copiar desde input.url (HTTP 4xx o 5xx, tiempo agotado, demasiado grande…). Revisa la URL y reintenta.
upload_incomplete source no Una subida progresiva dejó de llegar durante 30 minutos. Sube el archivo de nuevo.
validation_error request no Los ajustes no sirven para este archivo (una marca de agua que no se puede descargar, por ejemplo). Corrígelos.
timeout, launch_failed internal sí Nuestro y pasajero: reintenta.
processing_error internal no Nuestro: ya lo estamos revisando. El mensaje es genérico a propósito.

Los fallos momentáneos (red, capacidad) se reintentan dentro del job y nunca llegan a failed.

POST /v1/jobs/{id}/retry
POST /v1/jobs/{id}/cancel
DELETE /v1/jobs/{id}
  • Reintentar crea un job nuevo desde la misma fuente (sin subirla otra vez) con los mismos ajustes, salvo los que vengan en el cuerpo: outputs, audio, subtitles, on_upscale, quality_metrics, webhook_url, metadata e input.clip (null para el video entero). También sirve para hacer una variación de un job terminado. El retry_of del job nuevo apunta al original. Las fuentes se guardan 7 días: después, una traída de una URL se copia de nuevo desde ella, y una subida responde 409 (súbela de nuevo).
  • Cancelar detiene un job en cola o en marcha.
  • Borrar elimina los archivos de un job terminado. El job sigue en la lista, con deleted: true, para tu historial y tu facturación.
GET /v1/jobs?status=processing&limit=20

Del más nuevo al más antiguo. Pasa el next_cursor de la página como cursor para la siguiente; en la última vale null. Las páginas se mantienen coherentes aunque lleguen jobs nuevos.