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.mp4Para 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.