Vídeo com IA: Claude Code, Codex e ElevenLabs · Capítulo 24 de 25 · 25 min
Publicar por API no YouTube, Reels e TikTok com OAuth e rótulo de IA
Envie o vídeo aprovado ao YouTube, ao Instagram e ao TikTok por API em privado, com token renovável, cota consultada e declaração de IA no próprio upload.
Este capítulo faz parte do curso gratuito Vídeo com IA: Claude Code, Codex e ElevenLabs.
Publicar é a única etapa do pipeline com consequência pública. Um script pode terminar sem erro e deixar o vídeo em processamento, privado ou à espera de uma ação sua na plataforma, e um segundo envio por engano vira um post duplicado diante dos seguidores.
Sem método, o token expira no meio da madrugada, a cota do dia acaba no terceiro corte e o rótulo de IA fica para depois. Nesta aula você autoriza cada conta com o menor escopo possível, renova o token sem pedir senha, consulta a cota antes do lote e envia em privado com a declaração de IA no próprio upload. A primeira execução de cada script é sempre de preparação; o envio só acontece com PUBLICAR=SIM.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Autorizar a conta e planejar a cota
OAuth é a autorização delegada: uma procuração limitada em que o aplicativo recebe permissão para certas ações, nunca a senha da pessoa. O escopo é a lista do que a procuração permite, como youtube.upload. Da autorização saem dois tokens: o access token, curto, que vai em cada chamada, e o refresh token, de vida longa, que renova o primeiro sem nova tela de consentimento. O curso de OAuth detalha essa diferença em tokens de acesso e de renovação.
Nenhum desses segredos vai para o prompt do agente. O client secret e os tokens ficam na pasta privado/, fora do git e legível só pelo seu usuário, e os scripts os leem do disco ou do ambiente. A tabela resume autorização e cota de cada destino.
Autorização e cota por plataforma
| Plataforma | Autorização e fluxo | Cota ou limite |
|---|---|---|
| YouTube | OAuth com escopo youtube.upload; videos.insert resumível | 1 unidade no balde Video Uploads, padrão de 100 por dia; arquivo até 256 GB |
| Instagram Login | Conta profissional; instagram_business_basic e instagram_business_content_publish | A página da Meta diz 100 publicações por API em 24 horas e, na seção de carrossel, 50; vale o número de content_publishing_limit |
| TikTok | Direct Post com video.publish aprovado; rascunho pelo Inbox com video.upload | Até 6 inicializações por minuto por token nos dois fluxos |
A conta antiga de cerca de 1.600 unidades por upload no YouTube não vale mais. Shorts não têm endpoint separado.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
No YouTube, a biblioteca do Google faz a renovação: se o token salvo expirou e tem refresh token, creds.refresh(Request()) renova sem abrir o navegador; se o refresh foi revogado, o script para e pede nova autorização humana. Sobre projetos de API não verificados, a documentação do Google diverge, e a divergência continuava em 12/10/2026: a página do videos.insert diz que esses envios não ficam restritos a privado, enquanto o histórico de revisões e a Central de Ajuda ainda descrevem o bloqueio em privado até a auditoria de conformidade, sem direito a recurso. Trate o projeto não verificado como sujeito ao bloqueio; de todo modo, o curso envia sempre em privado e publica depois da conferência. No Instagram e no TikTok, os scripts partem de um token obtido pelo fluxo oficial de cada plataforma; quando a API responde 401, eles param e mandam renovar por esse fluxo, sem recriar a publicação para testar a credencial.
Enviar com comprovante e sem repetir envio ambíguo
O arquivo comum cria um registro local por envio, aberto em modo exclusivo: a mesma identidade de vídeo não é enviada duas vezes. Esse registro prefere parar a duplicar. Se a máquina cair entre o envio e a gravação da resposta, ele fica pendente e exige reconciliação na plataforma; não é uma chave de idempotência aceita pelo fornecedor. A lógica de repetição segura vem da aula do pipeline idempotente.
"""scripts/publicacao_comum.py: utilitários dos três scripts de publicação.
Um envio iniciado nunca é repetido automaticamente."""
import hashlib
import json
import os
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
PASTA = Path("publicacao")
PASTA.mkdir(parents=True, exist_ok=True)
def resumo(arquivo):
h = hashlib.sha256()
with open(arquivo, "rb") as f:
for bloco in iter(lambda: f.read(1024 * 1024), b""):
h.update(bloco)
return h.hexdigest()
def http(url, token, dados=None, formulario=False):
"""POST quando há dados, GET quando não há. Nunca imprime o token."""
headers = {"Authorization": f"Bearer {token}"}
corpo = None
if dados is not None:
if formulario:
corpo = urllib.parse.urlencode(dados).encode()
headers["Content-Type"] = "application/x-www-form-urlencoded"
else:
corpo = json.dumps(dados).encode()
headers["Content-Type"] = "application/json; charset=UTF-8"
req = urllib.request.Request(url, data=corpo, headers=headers)
try:
with urllib.request.urlopen(req, timeout=120) as r:
return json.load(r)
except urllib.error.HTTPError as e:
if e.code == 401:
raise SystemExit("401: token expirado ou revogado. Renove pelo fluxo oficial "
"da plataforma; não repita o envio para testar.")
raise SystemExit(f"HTTP {e.code} em {urllib.parse.urlsplit(url).path}. "
"Consulte o registro antes de qualquer nova tentativa.")
def autorizado():
return os.environ.get("PUBLICAR") == "SIM"
def ia():
valor = os.environ.get("ROTULO_IA")
if valor not in ("SIM", "NAO"):
raise SystemExit("Defina ROTULO_IA=SIM ou NAO depois da revisão editorial.")
return valor == "SIM"
def iniciar(plataforma, identidade):
chave = hashlib.sha256(json.dumps(identidade, sort_keys=True).encode()).hexdigest()[:16]
p = PASTA / f"{plataforma}-{chave}.json"
try:
with p.open("x", encoding="utf-8") as f: # 'x' falha se o arquivo já existe
json.dump({"estado": "iniciado", "identidade": identidade}, f, ensure_ascii=False)
except FileExistsError:
raise SystemExit(f"Registro já existe: {p}. Consulte e reconcilie; não repita o envio.")
return p
def registrar(p, dados):
tmp = p.with_suffix(".tmp")
tmp.write_text(json.dumps(dados, ensure_ascii=False, indent=2), encoding="utf-8")
os.replace(tmp, p)O script do YouTube guarda o token em privado/youtube-token.json com permissão só para o dono, renova quando expira e envia em privado com status.containsSyntheticMedia. Esse campo existe desde 30/10/2024, é gravável no insert e no update e representa a autodeclaração do criador. Ele não devolve rótulos aplicados por detecção, por C2PA ou por ferramentas como Veo e Dream Screen; a decisão de quando declarar é tema da aula de rótulos, direitos e custo.
"""scripts/publicar_youtube.py: upload privado com OAuth renovável e declaração de IA.
pip install google-api-python-client google-auth-oauthlib
Preparar: ROTULO_IA=SIM python scripts/publicar_youtube.py
Enviar: PUBLICAR=SIM ROTULO_IA=SIM YT_CONTA_REFERENCIA=cozinha-da-rafa python scripts/publicar_youtube.py"""
import os
from pathlib import Path
from publicacao_comum import autorizado, ia, iniciar, registrar, resumo
ARQUIVO = "renders/final_9x16.mp4"
ESCOPOS = ["https://www.googleapis.com/auth/youtube.upload"] # escopo mínimo
TOKEN = Path("privado/youtube-token.json") # fora do git e fora do alcance do agente
titulo = os.environ.get("TITULO", "Bolo de rolo em 60 segundos | Cozinha da Rafa")
rotulo = ia()
if not autorizado():
print("PREPARAÇÃO: YouTube em privado |", titulo, "| containsSyntheticMedia:", rotulo)
raise SystemExit(0)
from google.auth.exceptions import RefreshError
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
def credenciais():
creds = Credentials.from_authorized_user_file(str(TOKEN), ESCOPOS) if TOKEN.exists() else None
if creds and creds.valid:
return creds
if creds and creds.expired and creds.refresh_token:
try:
creds.refresh(Request()) # renova sem tela de consentimento
except RefreshError:
raise SystemExit("Refresh token revogado ou expirado. Apague "
"privado/youtube-token.json e autorize de novo.")
else:
# Primeira vez: uma pessoa autoriza no navegador, uma única vez.
flow = InstalledAppFlow.from_client_secrets_file("privado/client_secret.json", ESCOPOS)
creds = flow.run_local_server(port=0)
TOKEN.parent.mkdir(exist_ok=True)
TOKEN.write_text(creds.to_json(), encoding="utf-8")
os.chmod(TOKEN, 0o600) # legível só pelo dono
return creds
if not Path(ARQUIVO).is_file():
raise SystemExit("Vídeo não encontrado.")
youtube = build("youtube", "v3", credentials=credenciais())
registro = iniciar("youtube", {"conta": os.environ["YT_CONTA_REFERENCIA"],
"sha256": resumo(ARQUIVO), "titulo": titulo, "ia": rotulo})
corpo = {
"snippet": {"title": titulo, "description": os.environ.get("DESCRICAO", ""),
"defaultLanguage": "pt-BR"},
"status": {"privacyStatus": "private", "containsSyntheticMedia": rotulo},
}
pedido = youtube.videos().insert(
part="snippet,status", body=corpo,
media_body=MediaFileUpload(ARQUIVO, mimetype="video/mp4",
chunksize=8 * 1024 * 1024, resumable=True))
resposta = None
while resposta is None:
progresso, resposta = pedido.next_chunk(num_retries=0) # sem repetição automática
if progresso:
print(f"Enviado: {progresso.progress():.0%}")
registrar(registro, {"estado": "enviado", "video_id": resposta["id"],
"privacidade_solicitada": "private",
"containsSyntheticMedia_solicitado": rotulo})
print("Upload concluído. Confira processamento, privacidade e rótulo no YouTube Studio.")
print("https://youtu.be/" + resposta["id"])
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Instagram e TikTok seguem o mesmo esqueleto, com uma diferença importante em cada um. No Instagram, o parâmetro is_ai_generated=true enviado na criação do container aplica o rótulo de informação de IA, tanto no Instagram Login quanto no Facebook Login for Business; num carrossel, ele vai só no container pai. Os exemplos usam graph.instagram.com/v25.0, válida até 29/07/2028; a versão mais nova da Graph API é a v26.0, de 29/07/2026. Se migrar, troque o número em base e rode de novo a preparação antes de qualquer envio. No TikTok, privacy_level precisa ser um dos valores devolvidos por creator_info, e um aplicativo ainda não auditado publica em privado.
Publicar no Instagram Reels e no TikTok
image Instagram Reels

Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
O fluxo tem três chamadas: POST /{IG_ID}/media com media_type=REELS, um video_url público e, quando há IA, is_ai_generated=true, GET /{CONTAINER_ID}?fields=status_code até FINISHED e POST /{IG_ID}/media_publish com creation_id. Antes de um lote, o script lê content_publishing_limit e só segue com a sua confirmação.
"""Instagram Login, Graph API v25.0 (válida até 29/07/2028; a v26.0 é a mais nova).
O rótulo de IA vai no container (is_ai_generated)."""
import os
import time
from publicacao_comum import autorizado, http, ia, iniciar, registrar
ig, token = os.environ["IG_ID"], os.environ["IG_TOKEN"]
video_url = os.environ["VIDEO_URL"] # URL pública do MP4 aprovado
rotulo = ia()
base = "https://graph.instagram.com/v25.0"
legenda = os.environ.get("DESCRICAO", "Bolo de rolo da Cozinha da Rafa")
if not autorizado():
print("PREPARAÇÃO: Reel para a conta", ig, "| exige revisão da cota antes do envio")
raise SystemExit(0)
print("Cota atual:", http(f"{base}/{ig}/content_publishing_limit", token))
if os.environ.get("COTA_IG_CONFERIDA") != "SIM":
raise SystemExit("Leia a cota acima e defina COTA_IG_CONFERIDA=SIM.")
registro = iniciar("instagram", {"conta": ig, "url": video_url, "legenda": legenda, "ia": rotulo})
campos = {"media_type": "REELS", "video_url": video_url, "caption": legenda}
if rotulo:
campos["is_ai_generated"] = "true" # aplica o rótulo de informação de IA
container = http(f"{base}/{ig}/media", token, campos, formulario=True)
if "id" not in container:
raise SystemExit("Resposta sem container. Reconcilie o registro.")
cid = container["id"]
registrar(registro, {"estado": "container_criado", "container_id": cid})
for _ in range(5): # uma consulta por minuto, por até 5 minutos, como pede a Meta
estado = http(f"{base}/{cid}?fields=status_code", token).get("status_code")
print("Container:", estado)
if estado == "FINISHED":
break
if estado in ("ERROR", "EXPIRED"):
raise SystemExit("Container falhou ou expirou. Consulte o registro.")
time.sleep(60)
else:
raise SystemExit("Prazo local excedido. Consulte o mesmo container depois.")
resultado = http(f"{base}/{ig}/media_publish", token, {"creation_id": cid}, formulario=True)
registrar(registro, {"estado": "publicado", "container_id": cid, "resultado": resultado})
print(resultado)play TikTok

Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
O Direct Post consulta creator_info, inicia com video/init e acompanha com status/fetch. O envio por URL (PULL_FROM_URL) exige domínio verificado no aplicativo. A mídia aceita é MP4, WebM ou MOV, de 23 a 60 quadros por segundo, de 360 a 4.096 pixels por lado e até 4 GB; o envio admite até 10 minutos, mas a duração publicável depende da conta.
"""TikTok Direct Post por URL de domínio verificado. Escopo video.publish."""
import os
from publicacao_comum import autorizado, http, ia, iniciar, registrar
token, video_url = os.environ["TT_TOKEN"], os.environ["VIDEO_URL"]
privacidade = os.environ["TT_PRIVACIDADE"] # tem de vir de creator_info
rotulo = ia()
base = "https://open.tiktokapis.com/v2/post/publish"
titulo = os.environ.get("TITULO", "Bolo de rolo da Cozinha da Rafa")
if not autorizado():
print("PREPARAÇÃO: TikTok | privacidade pedida:", privacidade, "| is_aigc:", rotulo)
raise SystemExit(0)
criador = http(f"{base}/creator_info/query/", token, {})
erro = criador.get("error", {}).get("code")
if erro not in (None, "ok"):
raise SystemExit(f"Falha ao consultar a conta: {erro}")
dados = criador.get("data", {})
opcoes = dados.get("privacy_level_options", [])
if privacidade not in opcoes:
raise SystemExit(f"Privacidade não permitida. Opções desta conta: {opcoes}")
print("Confira a conta e a duração máxima publicável:", dados)
if os.environ.get("TT_CONTA_CONFERIDA") != "SIM":
raise SystemExit("Revise os dados acima e defina TT_CONTA_CONFERIDA=SIM.")
registro = iniciar("tiktok", {"conta": os.environ["TT_CONTA_REFERENCIA"], "url": video_url,
"titulo": titulo, "privacidade": privacidade, "ia": rotulo})
resultado = http(f"{base}/video/init/", token, {
"post_info": {"title": titulo, "privacy_level": privacidade,
"disable_comment": False, "disable_duet": False, "disable_stitch": False,
"is_aigc": rotulo}, # True aplica Creator labeled as AI-generated
"source_info": {"source": "PULL_FROM_URL", "video_url": video_url},
})
pid = resultado.get("data", {}).get("publish_id")
if not pid:
raise SystemExit("Sem publish_id. Reconcilie antes de qualquer reenvio.")
registrar(registro, {"estado": "inicializado", "publish_id": pid})
status = http(f"{base}/status/fetch/", token, {"publish_id": pid})
registrar(registro, {"estado": "status_consultado", "publish_id": pid, "resposta": status})
print(status, "| confirme o estado final na conta do TikTok.")Ler a cota do Instagram pela consulta
Em 12/10/2026, a página de publicação de conteúdo da Meta diz 100 publicações por API em 24 horas e, na seção de carrossel, 50, sem conciliar os dois números. Não planeje o lote por nenhum deles: o script lê content_publishing_limit, que devolve o uso e o total daquela conta naquele momento, e só segue com COTA_IG_CONFERIDA=SIM. Se o lote tem mais cortes do que a cota restante, deixe o resto para o dia seguinte em vez de esperar o erro. Erro comum: tratar o limite do aplicativo como o da conta. Está pronto quando o log de cada lote mostra a leitura da cota antes do primeiro container.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Conferir a publicação e manter a aprovação humana
HTTP 200 não é vídeo no ar. O YouTube ainda processa, o container do Instagram pode expirar e o TikTok devolve um publish_id que só diz que o envio começou. A conferência final acontece na interface de cada plataforma: privacidade, áudio, legenda e rótulo de IA iguais aos que você aprovou. Desde 03/09/2026, o YouTube anuncia também um rótulo automático para conteúdo de marca não declarado; a Data API não tem campo de patrocínio, então a promoção paga se marca no YouTube Studio e o rótulo de marca entra na mesma conferência, como detalha a aula de rótulos. Para o formato de cada posicionamento, o curso Meta Ads 2027 mostra como adaptar a peça a Feed, Stories e Reels, e o curso de podcasts reúne as práticas do canal no playbook do YouTube.
Publicar uma peça de teste nos três destinos
Use um corte curto e uma conta de teste antes do perfil principal.
Passo 1: Confira arquivo, direitos e metadados
Deu certo quando: O hash do MP4 é o mesmo da versão aprovada.
Erro comum: Enviar um rascunho antigo com o mesmo nome de arquivo.
Passo 2: Autorize cada conta com o escopo mínimo
Deu certo quando: Os tokens estão em privado/, fora do git, com permissão só do dono.
Erro comum: Colar o token no prompt do agente para ele testar.
Passo 3: Rode cada script sem PUBLICAR=SIM
Deu certo quando: O terminal mostra só a preparação, e nada é enviado.
Erro comum: Testar direto no perfil público.
Passo 4: Consulte cota e elegibilidade
Deu certo quando: content_publishing_limit e creator_info respondem e a privacidade pedida aparece nas opções.
Erro comum: Confundir o limite do aplicativo com o da conta.
Passo 5: Envie uma peça por plataforma com PUBLICAR=SIM
Deu certo quando: Cada envio deixa um registro em publicacao/ com o ID remoto.
Erro comum: Repetir o init do TikTok depois de uma resposta perdida.
Passo 6: Acompanhe o processamento pelo ID
Deu certo quando: O status final aparece na plataforma, não só na resposta da API.
Erro comum: Tratar HTTP 200 como vídeo publicado.
Passo 7: Confira privacidade, legenda e rótulo na interface
Deu certo quando: O que aparece na plataforma é igual ao que foi aprovado.
Erro comum: Confiar apenas no corpo enviado pelo script.
No fim você tem: Três envios de teste com comprovante remoto, privacidade conferida e rótulo de IA verificado.
Guia prático: agendar e completar a publicação
A aula envia em privado com o rótulo de IA. Este guia acrescenta o que transforma o envio em post pronto: horário agendado no YouTube, legenda revisada e miniatura pela API, capa e teste de Reels com quem ainda não segue o perfil, e capa e marcação de conteúdo de marca no TikTok. Você sai com o scripts/completar_youtube.py, um token separado com o escopo que a legenda exige e a conta de cota do dia antes de soltar o lote.
Funções, casos de uso e aplicações
Funções
Campos e métodos que completam o post
| Função ou recurso | O que faz | Quando usar | Data |
|---|---|---|---|
| status.publishAt (YouTube) | Agenda a publicação; só com privacyStatus privado e em vídeo nunca publicado | Enviar de madrugada e publicar no horário do público, sem script acordado | Em vigor em 12/10/2026 |
| captions.insert e thumbnails.set (YouTube) | Sobem a legenda (400 unidades, escopo youtube.force-ssl) e a miniatura (50 unidades, JPEG ou PNG) | Depois do upload, com a legenda revisada do vídeo cortado e a storyboard/thumb.png | Miniatura até 50 MB desde 14/09/2026 |
| Baldes de cota da YouTube Data API | videos.insert e search.list têm baldes próprios de 100 chamadas por dia; os demais métodos dividem 10.000 unidades | Contar legenda e miniatura no balde geral antes do lote | 01/06/2026 |
| Parte brandPartner (YouTube) | Liga ao vídeo o canal da marca parceira, para o acesso de parceiro de marca iniciado pelo criador | Publi com marca que acompanha os números; não substitui a marcação de promoção paga no Studio | 07/07/2026 |
| trial_params no Reels | Publica como Reel de teste, mostrado primeiro a quem não segue; MANUAL ou SS_PERFORMANCE para promover | Testar as variantes de gancho da aula 20 sem encher o perfil | 03/12/2025 |
| thumb_offset, cover_url, share_to_feed e collaborators (Reels) | Quadro da capa em ms ou imagem própria, exibição também no feed e até três colaboradores | Capa com o prato pronto em vez do primeiro quadro | Em vigor em 12/10/2026 |
| video_cover_timestamp_ms, brand_content_toggle e brand_organic_toggle (TikTok) | Quadro da capa; marcação de parceria paga com terceiro ou de promoção do próprio negócio | Toda peça com marca; a capa no quadro do produto | Página de 24/08/2026 |
Casos de uso
Cursinho pré-vestibular em Manaus com três vídeos de revisão por semana
Educação
Dor
Os vídeos subiam à noite e alguém precisava acordar às 7h para trocar para público e colar a legenda.
Solução
O upload sai privado com publishAt às 7h, e o completar_youtube.py sobe a legenda revisada e a miniatura logo depois do envio.
Resultado
Observar no Studio, na véspera, que cada vídeo aparece como agendado com legenda e miniatura; medir em um mês se a audiência da primeira hora melhora.
Cenário construído para fins didáticos. Números não correspondem a cliente específico.
Loja de utensílios de cozinha em Florianópolis com parceria de uma criadora
Marketing digital
Dor
Três ganchos do mesmo Reel pesavam no perfil, e a publi no TikTok saía sem a marcação de parceria.
Solução
Publica os três ganchos como Reels de teste com MANUAL e envia o TikTok com brand_content_toggle=true.
Resultado
Observar, em 72 horas, qual Reel de teste retém mais e promovê-lo pelo app; conferir no TikTok o selo de parceria antes de divulgar o link.
Cenário construído para fins didáticos. Números não correspondem a cliente específico.
Na prática
Duas mudanças. No corpo do publicar_youtube.py, acrescente "publishAt": os.environ["PUBLICAR_EM"] dentro de status, no formato 2026-10-14T10:00:00Z, mantendo privacyStatus privado. E o script abaixo, que roda depois do envio com um token próprio: captions.insert não aceita o escopo youtube.upload, e misturar escopos num só token amplia a procuração do script de upload.
"""completar_youtube.py: sobe a legenda revisada e a miniatura de um vídeo já enviado.
Custa 400 + 50 unidades do balde geral (10.000 por dia), fora do balde de uploads.
Uso (na raiz video-ia): VIDEO_ID=abc123 python scripts/completar_youtube.py"""
import os
from pathlib import Path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
ESCOPOS = ["https://www.googleapis.com/auth/youtube.force-ssl"] # exigido por captions.insert
TOKEN = Path("privado/youtube-token-legendas.json") # separado do token de upload
creds = Credentials.from_authorized_user_file(str(TOKEN), ESCOPOS) if TOKEN.exists() else None
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request()) # renova sem tela
elif not (creds and creds.valid):
fluxo = InstalledAppFlow.from_client_secrets_file("privado/client_secret.json", ESCOPOS)
creds = fluxo.run_local_server(port=0) # uma pessoa autoriza, uma vez
TOKEN.write_text(creds.to_json(), encoding="utf-8")
os.chmod(TOKEN, 0o600)
yt = build("youtube", "v3", credentials=creds)
vid = os.environ["VIDEO_ID"]
# legenda do vídeo JÁ cortado (aula 17), publicada, não rascunho
yt.captions().insert(
part="snippet",
body={"snippet": {"videoId": vid, "language": "pt-BR", "name": "Português", "isDraft": False}},
media_body=MediaFileUpload("legendas/cortado.srt", mimetype="application/octet-stream"),
).execute()
# miniatura da aula 13
yt.thumbnails().set(videoId=vid, media_body=MediaFileUpload("storyboard/thumb.png",
mimetype="image/png")).execute()
print("Legenda e miniatura enviadas. Confira no Studio antes do horário agendado.")Do envio privado ao post agendado e completo
Passo 1: Some a cota do lote antes de começar
Por vídeo: 1 no balde de uploads e 450 no balde geral (legenda e miniatura).
Deu certo quando: Com três vídeos, 1.350 de 10.000 unidades; sobra para videos.list de conferência.
Passo 2: Envie com PUBLICAR_EM e privado
Deu certo quando: O Studio mostra o vídeo como agendado no horário pedido.
Erro comum: Tentar agendar um vídeo que já foi público uma vez; o campo é recusado.
Passo 3: Rode o completar_youtube.py com o VIDEO_ID do registro
Deu certo quando: A faixa Português aparece publicada e a miniatura troca no Studio.
Erro comum: Subir a legenda do bruto, com tempos que não batem depois dos cortes.
Passo 4: No Instagram, crie o container com thumb_offset e, no teste de gancho, trial_params
Deu certo quando: O container chega a FINISHED e o Reel de teste aparece no app como teste.
Erro comum: Deixar o container para o dia seguinte; ele expira em 24 horas.
Passo 5: No TikTok, ligue a marcação de marca quando houver
Deu certo quando: O post mostra a marcação certa e a capa no quadro escolhido.
Armadilhas
Post agendado ainda pede conferência
Confira no Studio, no app do Instagram e no TikTok o post inteiro antes do horário em que ele fica público.
- publishAt só vale com privado e vídeo nunca publicado
- Legenda pede youtube.force-ssl; mantenha o token de upload só com upload
- brandPartner não marca promoção paga; isso segue no Studio
- Container do Instagram expira em 24 horas sem publicação
- Reel de teste promovido pelo app sai do controle do registro do pipeline
O teste: Na véspera, abra cada destino e ache horário, legenda, capa e rótulos.
alexandrecaramaschi.com/educacao/frontends-com-vibecoding
Quatro decisões aparecem já no primeiro envio. Se o token expirou, renove pelo fluxo oficial e não recrie a publicação para testar a credencial. Se o TikTok não devolve a privacidade que você quer, pare em vez de forçar outro valor. Se o Reel exige divulgação, envie is_ai_generated=true no container e confira o rótulo de informação de IA no post antes de divulgar o link. Se há ID remoto no registro, consulte o envio existente, mesmo depois de uma interrupção local.
Na Cozinha da Rafa, canal de receitas regionais em Recife com três vídeos por semana, o vídeo longo 16:9 entra primeiro no YouTube, em privado. Os dois cortes 9:16 seguem filas independentes para Instagram e TikTok, e uma falha no TikTok não dispara outro upload no YouTube, porque cada destino tem o seu registro.
Publique uma peça de teste nos três destinos com os scripts desta aula. Está pronto quando cada envio tem comprovante remoto no registro, a privacidade e o rótulo de IA conferem na interface, o token do YouTube renova sem abrir o navegador e nenhum destino recebeu o mesmo vídeo duas vezes.
Seu caderno neste capítulo
Abrir o caderno completoSelecione um trecho do capítulo para destacar ou anotar. Nos vídeos e áudios, use Anotar este momento. No teclado, selecione com Shift e as setas e use Alt+Shift+D para destacar ou Alt+Shift+N para anotar.
Salvo neste navegador. Entre na sua conta para levar o caderno a outros aparelhos.
Entre na sua conta para compartilhar o que aprendeu e convidar alguém para estudar com você.
Conexões deste capítulo
Veja o que estudar antes e para onde seguir depois, explore os conceitos e compare abordagens em outros cursos. Sugestões marcadas como “sugerido pelo grafo” vêm dos assuntos em comum, não do índice do curso.
Estude antes
- CursoClaude Code CLI: Programação com IA
- CursoAstra profundosugerido pelo grafoA quinta parte do Astra planeja o vídeo e edita o material bruto com o modelo dirigindo a ferramenta. O curso de vídeo com IA aprofunda esse trabalho com Claude Code, Codex e ElevenLabs, do roteiro à publicação. O grafo já sugeria esta ordem pela regra de entrada da trilha; com a ordem anterior curada, ela passa a ser declarada (Etapa 4, wave 3).
- CursoSetup Completo: do zero ao Claude Codesugerido pelo grafo7 capítulos de “Vídeo com IA: Claude Code, Codex e ElevenLabs” usam conceitos apresentados em “Setup Completo: do zero ao Claude Code”.
Depois, siga para
- CursoFrontends com Vibecodingsugerido pelo grafo21 capítulos de “Frontends com Vibecoding” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
- CursoDo Agile ao Agentic Operating Modelsugerido pelo grafo5 capítulos de “Do Agile ao Agentic Operating Model” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
- CursoAgentes de IA Autônomos: Do Conceito à Produçãosugerido pelo grafo7 capítulos de “Agentes de IA Autônomos: Do Conceito à Produção” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
- CursoLLM FinOps: o custo da IA para quem assina o orçamentosugerido pelo grafo4 capítulos de “LLM FinOps: o custo da IA para quem assina o orçamento” usam conceitos apresentados em “Vídeo com IA: Claude Code, Codex e ElevenLabs”.
O mesmo assunto em outros cursos
- TikTok Ads e Live Commerce 2027Produzir com o Symphony e revisar tudo o que a IA gerouExaminar conexões de Produzir com o Symphony e revisar tudo o que a IA gerou
- Meta Ads 2027Produzir Reels e Stories que seguram a atenção nos primeiros segundosExaminar conexões de Produzir Reels e Stories que seguram a atenção nos primeiros segundos
- SEO e GEO para PodcastsLigue o feed RSS ao YouTube Studio sem duplicar episódiosExaminar conexões de Ligue o feed RSS ao YouTube Studio sem duplicar episódios
Voltar ao capítulo anterior: Orquestrar o pipeline com etapas idempotentes e teto de gasto
Leve uma ideia para a prática
Escolha uma atividade no painel e volte a este curso quando quiser.
Atividades e evolução ficam reunidas no painel da sua conta.
Laboratórios ao vivo deste curso
Cada laboratório monta a biblioteca real dentro da aula, com controles para você mexer e o código do exemplo para copiar. O texto de reserva descreve a demonstração para quem lê sem JavaScript. O link abre o capítulo já no ponto do laboratório.
MediaRecorder, Canvas 2D e Long Animation Frames (plataforma)
Grave a cena em tempo real e veja os quadros que se perdem
Suba as partículas, force um bloqueio de 300 ms e grave seis segundos; compare com o render quadro a quadro desta aula.
Pacote
Abrir no capítuloMontar vídeo em HTML com HyperFrames e escolher entre ele e o Remotionsem pacoteMotion
Ver agentes em paralelo trocando mensagens numa linha do tempo
Reproduza a cena, percorra o tempo pelo controle e compare o tempo de parede com a soma das barras de cada agente.
Pacote
Abrir no capítuloParalelizar cenas com subagentes sem estourar o limite das APIsmotionReact (sem dependência extra), com as APIs do Veo e do fal.ai
Prompt de vídeo em cinco camadas, pronto para a API
Escolha plano, câmera, luz, ação e áudio e copie o prompt e o JSON para o Veo ou para o fal.ai.
Pacote
Abrir no capítuloEscrever prompt de vídeo em cinco camadas e manter o personagem consistentereactReact (sem dependência extra), com o comando para o FFmpeg
Corte o vídeo apagando palavras da transcrição
Aperte as palavras para removê-las, ligue a remoção de pausas e copie o comando FFmpeg e a legenda SRT gerados.
Pacote
Abrir no capítuloRemover pausas e reenquadrar o vídeo de 16:9 para 9:16 com legenda por palavrareactReact (sem dependência extra)
Quanto custa cada etapa do seu pipeline de vídeo
Troque a ferramenta de cada etapa e o percentual de b-roll e veja qual etapa pesa no custo por minuto e por vídeo.
Pacote
Abrir no capítuloOrquestrar o pipeline com etapas idempotentes e teto de gastoreact
Capítulos vizinhos em Vídeo com IA: Claude Code, Codex e ElevenLabs
- 21Chamar Veo, Kling, Seedance e Runway por API com polling e custo controlado
- 22Remover pausas e reenquadrar o vídeo de 16:9 para 9:16 com legenda por palavra
- 23Orquestrar o pipeline com etapas idempotentes e teto de gasto
- 24Publicar por API no YouTube, Reels e TikTok com OAuth e rótulo de IA
- 25Rotular o conteúdo de IA, documentar direitos e medir o custo por minuto final