Como criar legendas SRT de um vídeo com uma API

Envie o vídeo para POST /transcribe com output_format definido como srt e word_timestamps como false, espere o processamento terminar e salve o texto que voltar em um arquivo .srt. Se quiser a legenda traduzida para outro idioma, basta incluir o parâmetro translate_to. Aqui você vê como montar a requisição exata, o que vem dentro do SRT, como usar os tempos das palavras para quebrar as linhas direitinho, como converter para WebVTT e como colocar a legenda no vídeo.

Obter uma chave de API Início rápido Idiomas suportados

Última atualização: · Markdown

A requisição

Duas chamadas: iniciar o trabalho e consultá-lo. O result de um trabalho srt concluído é uma string, não JSON. Esta versão só precisa de curl e jq:

# 1. Start the job. "word_timestamps": false gives one cue per segment.
JOB=$(curl -s https://app.kolwrite.com/transcribe \
  -H "x-api-key: $KOLWRITE_API_KEY" -H "Content-Type: application/json" \
  -d '{"audio_url": "https://example.com/talk.mp3", "model": "oracle", "output_format": "srt", "word_timestamps": false}' \
  | jq -r .jobId)

# 2. Poll until it is done and save the result string as a .srt file.
while true; do
  REPLY=$(curl -s "https://app.kolwrite.com/transcribe/$JOB" -H "x-api-key: $KOLWRITE_API_KEY")
  STATUS=$(echo "$REPLY" | jq -r .status)
  if [ "$STATUS" = "COMPLETED" ]; then echo "$REPLY" | jq -r .result > talk.srt; break; fi
  if [ "$STATUS" = "FAILED" ]; then echo "$REPLY" | jq -r .error; break; fi
  sleep 5
done

Você pode mandar um vídeo, já que a API lê a faixa de áudio. Arquivos locais passam antes por POST /upload (até 100 MiB). Se o vídeo for longo, extraia o áudio com o ffmpeg. A função auxiliar do guia de edição de vídeo faz as duas coisas em Python.

O que vem no SRT

Com "word_timestamps": false, a saída traz uma linha por trecho, igual à maioria dos arquivos de legenda:

1
00:00:01,000 --> 00:00:03,750
Hello everyone and welcome to the show.

2
00:00:05,000 --> 00:00:07,750
Today we talk about editing video from a transcript.

Por padrão, os tempos por palavra vêm ativados e a API devolve uma linha para cada palavra. Cada linha repete a frase inteira do trecho e coloca a palavra da vez numa tag <font> amarela. Isso serve para destacar a leitura em tempo real, não como legenda comum:

1
00:00:01,000 --> 00:00:01,350
<font color="yellow">Hello</font> everyone and welcome to the show.

2
00:00:01,400 --> 00:00:01,750
Hello <font color="yellow">everyone</font> and welcome to the show.

Se você quer legendas tradicionais, passe "word_timestamps": false ou monte as linhas por conta própria a partir das palavras do JSON, como mostramos abaixo.

Como montar linhas limpas a partir do tempo das palavras

Peça output_format: "json" e agrupe você mesmo as palavras: limite cada linha a 42 caracteres e 6 segundos, e quebre a linha depois de uma pausa. Assim você controla o comprimento exato, o que faz diferença quando a legenda é gravada direto na imagem do vídeo.

srt_tools.py

# srt_tools.py — neat subtitle cues from word timings
def srt_time(seconds):
    ms = round(seconds * 1000)
    h, ms = divmod(ms, 3_600_000)
    m, ms = divmod(ms, 60_000)
    s, ms = divmod(ms, 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"


def cues_from_words(words, max_chars=42, max_seconds=6.0, max_gap=0.8):
    """Group timed words into subtitle cues: (start, end, text)."""
    cues, line = [], []
    for word in words:
        text = word["word"].strip()
        if not text:
            continue
        if line:
            length = sum(len(w["word"]) + 1 for w in line) + len(text)
            if (length > max_chars
                    or word["end"] - line[0]["start"] > max_seconds
                    or word["start"] - line[-1]["end"] > max_gap):
                cues.append(line)
                line = []
        line.append({"start": word["start"], "end": word["end"], "word": text})
    if line:
        cues.append(line)
    return [(c[0]["start"], c[-1]["end"], " ".join(w["word"] for w in c)) for c in cues]


def cues_from_result(result):
    """Cues for a whole JSON result; a segment without word times becomes one cue."""
    cues = []
    for segment in result["segments"]:
        if segment.get("words"):
            cues += cues_from_words(segment["words"])
        elif segment["text"].strip():
            cues.append((segment["start"], segment["end"], segment["text"].strip()))
    return cues


def write_srt(cues, path):
    with open(path, "w", encoding="utf-8") as out:
        for number, (start, end, text) in enumerate(cues, 1):
            out.write(f"{number}\n{srt_time(start)} --> {srt_time(end)}\n{text}\n\n")

cues_from_result(result) devolve tuplas (start, end, text) para o resultado todo e usa os tempos do trecho quando o modelo não traz tempos por palavra. Depois, write_srt(cues, "talk.srt") salva o arquivo.

Legendas traduzidas

Passe translate_to com o código do idioma e escolha um modelo Oracle (o oracle_mini é o mais barato que faz tradução). O texto já sai traduzido; mantenha word_timestamps como false para receber um trecho por linha.

{
  "audio_url": "https://example.com/lecture-he.mp4",
  "model": "oracle_mini",
  "language": "he",
  "translate_to": "en",
  "output_format": "srt",
  "word_timestamps": false
}

Para legendar o mesmo vídeo em vários idiomas, rode um trabalho para cada idioma de destino. Como toda tradução automática é um rascunho, revise nomes, números e termos específicos antes de publicar.

WebVTT, ASS e outros formatos

A API gera apenas json, txt ou srt. Se precisar de WebVTT para usar na tag <video> do HTML5, converta o SRT com o ffmpeg:

ffmpeg -i video.srt video.vtt

O ffmpeg faz a mesma conversão para a maioria dos formatos de legenda; basta mudar a extensão do arquivo de saída.

Grave a legenda no vídeo ou anexe como faixa

Gravada direto na imagem:

ffmpeg -i edited.mp4 -vf "subtitles=edited.srt" -c:a copy burned.mp4

Como faixa separada dentro do MP4 (quem assiste pode ligar ou desligar no player):

ffmpeg -i edited.mp4 -i edited.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng soft.mp4

O YouTube Studio e a maioria dos players e editores de vídeo aceitam o arquivo .srt direto.

Legendas em hebraico e árabe

  • A API entrega texto Unicode comum. Quem cuida da leitura da direita para a esquerda é o player ou o renderizador de legendas do ffmpeg, que lida bem com hebraico e árabe desde que a fonte escolhida tenha os caracteres.
  • Indique uma fonte compatível com o alfabeto e confira um quadro renderizado antes de converter o vídeo inteiro. Nos nossos testes, a fonte Arial funcionou bem para hebraico, mas falhou em um glifo numa palavra em árabe; por isso, teste outra fonte quando for usar árabe.
ffmpeg -i video.mp4 -vf "subtitles=video.he.srt:force_style='FontName=Arial,FontSize=24'" -c:a copy burned.mp4
  • Se a linha terminar com pontuação ou caracteres latinos (como uma marca ou um número), a ordem pode sair invertida em players que não tratam bem texto bidirecional. Faça um teste nos players que o seu público costuma usar.

Quanto custa

TrabalhoModeloCusto
Aula de 45 minutos, com legendasoracleUS$ 1,08
A mesma aula, com legendas traduzidasoracle_miniUS$ 0,72
Uma hora de áudio limpo, com legendasbaseUS$ 0,48

Cobramos por segundo, sem valor mínimo. Veja a tabela em /llms/pricing.md.

Perguntas e respostas

Como gero um arquivo SRT a partir de um vídeo?

Chame POST /transcribe passando a URL do vídeo, output_format: "srt" e word_timestamps: false. Acompanhe o andamento do trabalho e, quando terminar, salve a string result como um arquivo .srt.

A API devolve WebVTT?

Não, apenas json, txt ou srt. Para ter WebVTT, converta o SRT rodando ffmpeg -i video.srt video.vtt.

Por que meu SRT veio com uma linha para cada palavra?

Por padrão, a marcação de tempo por palavra vem ativa, então o SRT traz uma linha para cada palavra e destaca a que está sendo falada. Para receber uma linha por trecho, envie word_timestamps: false ou monte você mesmo as linhas usando o JSON.

Consigo gerar legendas já traduzidas?

Sim. Basta incluir translate_to com o código do idioma desejado e usar um modelo Oracle. As linhas já voltam traduzidas.

Qual a diferença entre legendas comuns e legendas para surdos e ensurdecidos?

A legenda comum só traz o que é falado (transcrito ou traduzido) para quem consegue ouvir o áudio. Já a legenda para surdos e ensurdecidos inclui também a descrição dos sons do ambiente. A API entrega o texto da fala com os tempos; se precisar descrever os sons, adicione essas marcações manualmente.