Subir archivos

Convierte un archivo local en una URL pública que FFHub pueda leer.

Los workers de FFHub se ejecutan en la nube y solo aceptan entradas que sean URLs públicas. Si tu archivo está en tu laptop, en tu teléfono o en un bucket privado, primero súbelo al almacenamiento de FFHub y pasa la URL devuelta a tu comando ffmpeg.

Cómo funciona la subida

Son dos pasos, y el archivo nunca pasa por nuestros servidores. Tu cliente obtiene una URL firmada de un solo uso y hace PUT directamente a R2.

  1. Firmar. POST /v1/uploads/sign devuelve una URL prefirmada válida por 15 minutos.
  2. PUT. Sube los bytes a esa URL con curl, fetch o cualquier cliente HTTP.

Esto te permite subir archivos de hasta 5 GB sin que nuestros servidores intervengan en la transferencia de datos.

Paso 1 — Obtén una URL firmada

curl -X POST https://api.ffhub.io/v1/uploads/sign \
  -H "Authorization: Bearer $FFHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "input.mp4",
    "size": 12345678,
    "content_type": "video/mp4"
  }'

Respuesta:

{
  "upload_url": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...",
  "public_url": "https://storage.ffhub.io/tmp/uploads/<user>/<rand>.mp4",
  "key": "tmp/uploads/<user>/<rand>.mp4",
  "content_type": "video/mp4",
  "expires_at": "2026-05-12T05:30:00Z"
}

Paso 2 — Sube el archivo

curl -fsS -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --data-binary "@./input.mp4"

El encabezado Content-Type debe coincidir exactamente con el content_type que enviaste en el paso 1 — R2 rechaza la firma si no coincide.

Un PUT fallido pasa desapercibido con facilidad: sin -f, curl termina con código 0 incluso ante un 403, y te quedas con una public_url que no tiene ningún archivo detrás. La tarea que envíes después fallará con HTTP Error 404: Not Found sobre una URL que parece perfectamente válida. Comprueba siempre que el PUT haya funcionado antes de enviar una tarea.

Una vez que el PUT devuelva 200/204, el campo public_url del paso 1 ya estará disponible para lectura.

Ejemplo completo de principio a fin

FILE=./input.mp4
SIZE=$(stat -f%z "$FILE" 2>/dev/null || stat -c%s "$FILE")

# 1. Firmar
SIGNED=$(curl -s -X POST https://api.ffhub.io/v1/uploads/sign \
  -H "Authorization: Bearer $FFHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"filename\":\"input.mp4\",\"size\":$SIZE,\"content_type\":\"video/mp4\"}")

UPLOAD_URL=$(echo "$SIGNED" | jq -r '.upload_url')
PUBLIC_URL=$(echo "$SIGNED" | jq -r '.public_url')

# 2. Subir directamente a R2
curl -fsS -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --data-binary "@$FILE" || { echo "Fallo al subir el archivo"; exit 1; }

# 3. Enviar una tarea usando public_url como entrada
curl -X POST https://api.ffhub.io/v1/tasks \
  -H "Authorization: Bearer $FFHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"command\":\"-i $PUBLIC_URL -c:v libx264 -crf 28 output.mp4\"}"

El CLI hace todo este proceso automáticamente — consulta CLI.

Límites

  • Tamaño máximo: 5 GB por subida (límite de single-PUT en R2)
  • Validez de la URL firmada: 15 minutos a partir de expires_at; vuelve a firmar si se vence
  • Tiempo de vida del archivo: 7 días, luego se elimina de R2

Si necesitas conservar los archivos más de 7 días, cópialos a tu propio bucket en cuanto la tarea finalice.

¿Ya tienes una URL pública?

Omite todo el flujo. La API de tareas acepta cualquier URL pública como entrada -i:

curl -X POST https://api.ffhub.io/v1/tasks \
  -H "Authorization: Bearer $FFHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command":"-i https://example.com/clip.mp4 -c:v libx264 output.mp4"}'

Tu S3, tu CDN, nuestro almacenamiento — todos son equivalentes. Al módulo de tareas no le importa dónde vivan los bytes.