Sube un archivo de video
Sube el archivo directo al storage, en una parte o en varias, y recibe el video_id para generar clips.
Usa la subida cuando el video lo tienes tú y no está en un enlace público. El archivo va directo de tu servidor al storage, por URL firmada, y no pasa por la API. Al final tienes un video_id igual al de POST /clips/info.
Formatos aceptados: .mp4, .mov, .webm y .mkv. El tamaño máximo viene del plan.
Pide la URL de subida
curl -X POST https://api.cut.pro/api/v1/videos/upload \
-H "X-Api-Key: $CUTPRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "file_name": "podcast-ep-42.mp4", "file_size": 734003200, "content_type": "video/mp4" }'{
"mode": "single",
"video_id": "7412911002237764",
"upload_url": "https://...",
"expires_in": 3600
}mode dice cómo enviar: single (un PUT) o multipart (varias partes). Un archivo demasiado grande responde 400 FILE_TOO_LARGE con el tope en extra.max_size.
Envía los bytes
Con mode: "single", un PUT con el mismo Content-Type que declaraste:
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/mp4" \
--upload-file podcast-ep-42.mp4Con mode: "multipart", ve Archivos grandes.
Registra el video
Después de que el PUT respondió 200, informa duración, ancho y alto:
curl -X POST https://api.cut.pro/api/v1/videos/upload/complete \
-H "X-Api-Key: $CUTPRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_id": "7412911002237764",
"file_name": "podcast-ep-42.mp4",
"duration": 3712,
"width": 1920,
"height": 1080
}'La respuesta tiene el mismo formato que POST /clips/info, con credits_cost y current_balance. Envía el id en POST /clips y sigue como en Genera clips de un video.
Archivos grandes
Cuando el archivo supera el límite de un solo PUT, POST /videos/upload responde mode: "multipart":
{
"mode": "multipart",
"video_id": "7412911002237764",
"upload_id": "2~kB1x...",
"part_urls": ["https://...", "https://...", "https://..."],
"part_size": 104857600,
"expires_in": 3600
}- Corta el archivo en pedazos de
part_sizebytes. Solo el último puede ser menor. - Haz un PUT de cada pedazo en la URL de la misma posición en
part_urls. La posición 0 es la parte 1. - Guarda el header
ETagde cada respuesta. - Cierra la subida con las partes en orden:
curl -X POST https://api.cut.pro/api/v1/videos/upload/multipart/complete \
-H "X-Api-Key: $CUTPRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_id": "7412911002237764",
"upload_id": "2~kB1x...",
"parts": [
{ "part_number": 1, "etag": "\"a54357aff0632cce46d942af68356b38\"" },
{ "part_number": 2, "etag": "\"0c78aef83f66abc1fa1e8477f296d394\"" },
{ "part_number": 3, "etag": "\"acbd18db4cc2f85cedef654fccc4a4d8\"" }
]
}'- Registra con
POST /videos/upload/complete, como en el paso 3 de arriba.
import { open } from "node:fs/promises";
const file = await open("podcast-ep-42.mp4");
const parts = [];
for (const [index, url] of upload.part_urls.entries()) {
const buffer = Buffer.alloc(upload.part_size);
const { bytesRead } = await file.read(buffer, 0, upload.part_size, index * upload.part_size);
const response = await fetch(url, { method: "PUT", body: buffer.subarray(0, bytesRead) });
parts.push({ part_number: index + 1, etag: response.headers.get("ETag") });
}
await file.close();¿Desististe a mitad de camino? Llama a POST /videos/upload/multipart/abort con video_id y upload_id. Sin eso, las partes ya enviadas siguen ocupando el storage.
Errores
| Code | Cuándo |
|---|---|
INVALID_FILE_TYPE | Extensión fuera de la lista. extra.allowed_extensions trae las aceptadas. |
FILE_TOO_LARGE | Supera el tope del plan, informado en extra.max_size. |
FILE_NOT_FOUND | El complete llegó antes de que el archivo estuviera en el storage. Revisa el PUT. |
VIDEO_TOO_LONG, VIDEO_TOO_SHORT | Duración fuera de lo permitido. extra.duration y extra.max_duration dicen cuánto. |