Upload de vídeos

Crie vídeos e envie arquivos para a Spalla, inclusive em partes para acompanhar o progresso do upload.

O upload de arquivos é feito pelo host https://upload.spalla.io. Você pode enviar o arquivo para um vídeo existente ou criar um vídeo automaticamente durante o upload. Para arquivos grandes, use o envio em partes de 10 MiB demonstrado neste guia.

Proteja seu token

Envie o token de API no header Authorization. Não o inclua em código JavaScript público nem o versione no repositório. Em produção, faça o upload no back-end ou forneça ao navegador uma URL de upload protegida pelo seu próprio back-end.

Escolha o fluxo

Enviar para um vídeo existente

Crie o vídeo pela API e guarde o videoId retornado:

curl -X POST https://api.spalla.io/v1/videos/vod \
  -H "Authorization: $SPALLA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Aula 01 — Introdução"}'

Envie o arquivo para https://upload.spalla.io/v1/videos/{videoId}/source.

Criar um vídeo durante o upload

Envie o arquivo para https://upload.spalla.io/v1/videos/upload. O título do vídeo criado será o nome do arquivo. O parâmetro accountId é opcional:

https://upload.spalla.io/v1/videos/upload?accountId=<account_id>

Nas duas opções, o corpo da requisição é multipart/form-data com o campo file. O upload completo retorna 202 Accepted e o objeto do vídeo, incluindo videoId. Quando o arquivo é enviado em partes, cada parte intermediária retorna 201 Created; a última retorna 202 Accepted.

Enviar em partes

Os exemplos abaixo dividem o arquivo em partes de 10 MiB. Para cada parte, envie o header Content-Range no formato bytes início-fim/tamanho_total; os offsets são baseados em zero e o fim é inclusivo.

Python

Instale a dependência com python -m pip install requests. Salve como upload_video.py e execute, por exemplo:

export SPALLA_API_TOKEN="<seu-token>"
python upload_video.py ./aula-01.mp4

Para enviar a um vídeo existente, use --video-id <videoId>. Para indicar a conta ao criar o vídeo durante o upload, use --account-id <accountId>.

#!/usr/bin/env python3
import argparse
import json
import os
from pathlib import Path

import requests

CHUNK_SIZE = 10 * 1024 * 1024
UPLOAD_BASE_URL = "https://upload.spalla.io/v1/videos"


def upload_video(file_path, token, video_id=None, account_id=None):
    path = Path(file_path)
    file_size = path.stat().st_size
    if file_size == 0:
        raise ValueError("O arquivo está vazio.")

    if video_id:
        url = f"{UPLOAD_BASE_URL}/{video_id}/source"
    else:
        url = f"{UPLOAD_BASE_URL}/upload"
        if account_id:
            url = f"{url}?accountId={account_id}"

    headers = {"Authorization": token}
    with path.open("rb") as video_file:
        start = 0
        while start < file_size:
            chunk = video_file.read(CHUNK_SIZE)
            end = start + len(chunk) - 1
            headers["Content-Range"] = f"bytes {start}-{end}/{file_size}"

            response = requests.post(
                url,
                headers=headers,
                files={"file": (path.name, chunk)},
                timeout=300,
            )

            if response.status_code == 201:
                start = end + 1
                print(f"Upload: {start * 100 // file_size}%")
                continue
            if response.status_code == 202:
                print(json.dumps(response.json(), indent=2, ensure_ascii=False))
                return response.json()

            raise RuntimeError(
                f"Falha no upload (HTTP {response.status_code}): {response.text}"
            )

    raise RuntimeError("O upload terminou sem uma resposta 202 Accepted.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Envia um vídeo para a Spalla.")
    parser.add_argument("file", help="Caminho do arquivo de vídeo")
    parser.add_argument(
        "--token",
        default=os.environ.get("SPALLA_API_TOKEN"),
        help="Token da API (ou defina SPALLA_API_TOKEN)",
    )
    target = parser.add_mutually_exclusive_group()
    target.add_argument("--video-id", help="ID de um vídeo já criado")
    target.add_argument("--account-id", help="ID da conta para criar o vídeo")
    args = parser.parse_args()
    if not args.token:
        parser.error("informe --token ou defina SPALLA_API_TOKEN")

    upload_video(args.file, args.token, args.video_id, args.account_id)

JavaScript no navegador

O exemplo mantém a mesma sequência de partes e permite atualizar a interface por meio de onProgress. Não publique um token de API no front-end; integre a função a um back-end que controle a autenticação.

const CHUNK_SIZE = 10 * 1024 * 1024;

async function uploadVideoInChunks(file, { uploadUrl, token, onProgress }) {
  if (!file || file.size === 0) {
    throw new Error("Selecione um arquivo de vídeo não vazio.");
  }

  let start = 0;
  while (start < file.size) {
    const endExclusive = Math.min(start + CHUNK_SIZE, file.size);
    const chunk = file.slice(start, endExclusive);
    const end = endExclusive - 1;
    const formData = new FormData();
    formData.append("file", chunk, file.name);

    const response = await fetch(uploadUrl, {
      method: "POST",
      headers: {
        Authorization: token,
        "Content-Range": `bytes ${start}-${end}/${file.size}`,
      },
      body: formData,
    });

    if (response.status === 201) {
      start = endExclusive;
      onProgress?.(Math.floor((start / file.size) * 100));
      continue;
    }
    if (response.status === 202) {
      onProgress?.(100);
      return response.json();
    }

    const errorBody = await response.text();
    throw new Error(`Falha no upload (HTTP ${response.status}): ${errorBody}`);
  }

  throw new Error("O upload terminou sem uma resposta 202 Accepted.");
}

const input = document.querySelector("#file");
input.addEventListener("change", async (event) => {
  const file = event.target.files[0];
  if (!file) return;

  try {
    const video = await uploadVideoInChunks(file, {
      uploadUrl: "https://upload.spalla.io/v1/videos/upload",
      token: "<token fornecido com segurança pelo seu back-end>",
      onProgress: (percentage) => {
        console.info(`Upload: ${percentage}%`);
      },
    });
    console.info("Upload concluído:", video);
  } catch (error) {
    console.error(error);
  }
});

Acompanhar a transcodificação

Após o upload, consulte o progresso da transcodificação pelo videoId:

curl "https://api.spalla.io/v1/videos/$VIDEO_ID/transcode/progress" \
  -H "Authorization: $SPALLA_API_TOKEN"

A resposta inclui progress (progresso numérico), running (se o trabalho continua ativo) e updatedAt. O objeto final do upload também inclui o videoId para usar nesta consulta.

Nesta página