# Media — Video Generation (runtime, a partir de .NET)

> **Scope:** stacks=["dotnet"]
> **Layer:** 2 (on-keyword)
> **Keywords:** video generation, veo, seedance, kling, runway, image to video, text to video, media video
> **Read by Claude in:** implement (quando o projeto precisa gerar vídeo em runtime)

**Verified against:** Google.GenAI 1.21.0 + OpenAI 2.13.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs` — `Models.GenerateVideosAsync(model, GenerateVideosSource, GenerateVideosConfig, ct)`, o polling por `Operations.GetAsync` + `operation.Done` e o sinal de bloqueio `RaiMediaFilteredCount` COMPILAM ali. Preço, duração, retenção e data de desligamento são **verificação documental** contra as páginas listadas em §Como re-verificar este standard. Last-verified: 2026-09-08.

---

**A fronteira, primeiro.** Este standard é sobre **gerar vídeo em runtime a partir de .NET**: o
backend cria uma tarefa paga, faz polling, baixa bytes e guarda. Footage de landing page
scroll-driven, produzido por MCP em **tempo de autoria**, é
`frontend/design-system/ai-video-generation.md` — loop × direcional, `endImage`, o contorno de
moderação E005 e a escada de custo daquele fluxo. Os dois se citam e não se sobrepõem: lá o
operador é um designer numa sessão; aqui é um worker no seu servidor.

O pipeline (job, revisão, publicação, marcação) é `ai-agents/media-pipeline.md`. Imagem é
`ai-agents/modalities-image-gen.md`.

---

## Quando usar

| Caso | Nota |
|---|---|
| **Vídeo curto de produto** (reels, stories, até 8 s com áudio) | O caso que paga a conta |
| **Animar render de cliente** | `img2vid` sobre a arte real, nunca recriação por IA |
| **Variação de cena** sobre um herói aprovado | Depois da aprovação da imagem, nunca antes |

**Nunca dentro de um turno de conversa.** A geração de vídeo leva de **11 s a 6 min** medidos na
documentação do Veo, é assíncrona **por desenho em todos os provedores** — nenhum deles devolve o
arquivo na resposta do POST — e custa por segundo de saída. Não existe forma de fazer isso caber num
turno de WhatsApp; a tentativa é `ai-agents/media-pipeline.md` §A regra dura.

---

## O caminho com SDK

`Google.GenAI` 1.21.0, `net8.0` + `netstandard2.0`. É o **único** caminho com SDK .NET oficial
depois do desligamento listado em §Deprecações.

```csharp
using Google.GenAI.Types;

// 1. Cria a operacao. Ela volta NAO-concluida: o video ainda nao existe.
//    O prompt vai DENTRO de GenerateVideosSource — nao e um parametro solto.
var operation = await client.Models.GenerateVideosAsync(
    "veo-3.1-fast-generate-preview",
    new GenerateVideosSource { Prompt = prompt },
    new GenerateVideosConfig { DurationSeconds = 8, AspectRatio = "16:9" },
    ct);

// 2. Polling. ~10 s entre voltas e o intervalo que a doc do Veo usa; back-off e
//    TETO DE TENTATIVAS — um laco sem saida e indistinguivel de um worker travado.
//    `Done` e bool? : `!operation.Done` NAO compila; compare com true.
for (var poll = 0; operation.Done != true && poll < 60; poll++)
{
    await Task.Delay(TimeSpan.FromSeconds(10), ct);
    operation = await client.Operations.GetAsync(operation, new GetOperationConfig(), ct);
}

// 3. Bloqueio de moderacao NAO e erro tecnico, e o sinal e este:
if (operation.Response?.RaiMediaFilteredCount is > 0) { /* rejected_by_policy, sem retry */ }

// 4. BAIXE OS BYTES AGORA (operation.Response.GeneratedVideos[..].Video.VideoBytes).
//    Ver a secao "O arquivo expira".
```

> **Assinaturas medidas contra `Google.GenAI` 1.21.0 por reflexão, não lidas em blog.** Três
> detalhes derrubam o build de quem copia de uma página de terceiro: o prompt viaja em
> `GenerateVideosSource`, `Operations.GetAsync` pede um `GetOperationConfig` (não só o token), e
> `operation.Done` é `bool?` — `while (!operation.Done)` não compila. O exemplar que compila está
> em `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs`.

Três exigências de desenho que caem deste formato, e não são opcionais:

1. **O `operation` é estado persistido**, não variável local. Se o worker reiniciar no meio, o id da
   operação tem de estar no banco — senão você paga de novo por um vídeo que já está pronto.
2. **O polling mora num job**, nunca numa request HTTP. Um `while` de 6 minutos dentro de um
   controller é um thread pool derretido.
3. **Timeout e teto de tentativas explícitos.** `Done` que nunca vira `true` é um caso real, e um
   loop sem saída é indistinguível de um worker travado.

---

## O caminho sem SDK

Seedance 2.0 (BytePlus ModelArk ou fal.ai), Kling 3.0 e Runway **não têm SDK .NET**. Sem exceção,
sem "biblioteca da comunidade": `HttpClient` **tipado** via `IHttpClientFactory`, DTOs próprios,
`System.Text.Json` com source generation, e Polly para 429/5xx/timeout.

O contrato é o mesmo nos três, e é o que torna o adaptador escrevível:

```
POST criar tarefa  →  { id }        (202 / 200, NUNCA o arquivo)
        ↓  persistir o id ANTES de qualquer outra coisa
GET status(id)     →  queued | running | succeeded | failed
        ↓  polling com back-off, OU webhook (fal.ai, Runway)
GET resultado      →  URL temporaria  →  baixar  →  blob proprio
```

**Persistir o id antes de qualquer outra coisa** é a linha que separa "retry custa zero" de "retry
custa outra geração". O resto do desenho — chave de idempotência, estados, storage — está em
`ai-agents/media-pipeline.md`.

Webhook, onde existe, é preferível a polling: menos chamada, menos latência de detecção. Mas o
polling continua sendo o caminho de recuperação quando o webhook se perde — implemente os dois, e
faça o handler de webhook idempotente.

---

## Sora: não planejar

`sora-2`, `sora-2-pro` e o `VideoClient` do `openai-dotnet` (`CreateVideo`, `GetVideo`,
`DownloadVideo`, `CreateVideoRemix`) **saem de circulação sem substituto** — a data está em
§Deprecações. "Sem substituto" é literal: a OpenAI não anunciou modelo de vídeo sucessor.

Consequência prática: **não escreva adaptador para Sora**, não pine alias de Sora, e se encontrar um
num projeto existente, a migração é para o caminho com SDK do Google ou para um dos HTTP crus. Um
adaptador escrito hoje para Sora nasce morto.

---

## Custo por segundo

> **Toda a aritmética de dinheiro deste standard mora nesta seção, e em lugar nenhum mais.**

Preço de **lista pública**, em USD por segundo de saída, sem desconto de contrato. Consultadas em
**2026-09-07**: `https://ai.google.dev/gemini-api/docs/veo` com
`https://ai.google.dev/gemini-api/docs/pricing`, `https://docs.byteplus.com/en/docs/ModelArk/1520757`,
`https://fal.ai/models/bytedance/seedance-2.0/image-to-video`, `https://kling.ai/dev/pricing` e
`https://docs.dev.runwayml.com/guides/pricing/`.

| Modelo | Preço | Nota |
|---|---|---|
| Veo 3.1 **Lite** | $0,05 / s | Sem 4K |
| Veo 3.1 **Fast** — 720p | $0,10 / s | O default recomendado; áudio incluso |
| Veo 3.1 Standard — 720p/1080p | $0,40 / s | 4K: $0,60 / s |
| Seedance 2.0 (BytePlus oficial) | $0,04 – $0,78 / s | Varia por modelo e resolução; pacote pré-pago mínimo de $30,10 |
| Seedance 2.0 (fal.ai) | $0,3024 / s std 720p; $0,2419 / s fast | Mesmo modelo, agregador diferente, preço diferente |
| Kling 3.0 std | ≈ $0,42 por 5 s | Sem SDK |
| Runway gen4.5 | $0,12 / s | Créditos a $0,01; 12 cr/s |

Três leituras que decidem projeto:

1. **A faixa do Seedance é de 20× entre o piso oficial e o teto**, e o mesmo modelo custa preços
   diferentes conforme o agregador. Por isso **nenhum alias default é pinado** para Seedance, Kling
   ou Runway neste standard: escolher um seria escolher fornecedor por conveniência de escrita, e não
   por evidência. Meça o seu caso antes de pinar.
2. **Vídeo é caro por segundo e mínimo de 4 s.** Um rascunho de 4 s em Lite custa uma fração de um
   final de 8 s em Standard — a escada de custo é a mesma disciplina de imagem, com números maiores.
3. **Bloqueio de moderação no Veo não cobra.** Isso muda o desenho de retry: um bloqueio é
   `rejected_by_policy` e não deve ser retentado; um erro técnico é `failed` e deve.

---

## Duração, resolução e referências

Medido contra `https://ai.google.dev/gemini-api/docs/veo` (Veo 3.1) e a doc do BytePlus ModelArk
(Seedance 2.0), ambas consultadas na data declarada em §Custo por segundo.

| Parâmetro | Veo 3.1 | Seedance 2.0 |
|---|---|---|
| Duração | 4, 6 ou 8 s | 4 a 15 s |
| **8 s é obrigatório** para | 1080p, 4K e uso de imagens de referência | — |
| Aspect ratio | 16:9 ou 9:16 | 480p / 720p / 1080p |
| Frame rate | 24 fps | — |
| Áudio | nativo, incluso no preço | nativo |
| Imagem inicial | sim (primeiro frame) | JPEG/PNG/WebP até 30 MB |
| Interpolação | primeiro + último frame | imagem final opcional |
| Referências | **até 3 imagens** (exige 8 s) | referências de vídeo e áudio |
| Extensão | +7 s, até 20 vezes (720p) | — |

"8 s obrigatório para referências" é a pegadinha cara: pedir 4 s **com** imagem de referência é uma
requisição inválida, e descobrir isso em produção custa uma rodada de suporte.

---

## O arquivo expira

O Veo retém o arquivo gerado por **2 dias**. Depois disso a URL devolve 404 e o vídeo pelo qual você
pagou não existe mais em lugar nenhum.

**Baixar e gravar no blob próprio é obrigação, não otimização.** O download entra no mesmo passo do
worker que detectou `Done`, antes de qualquer outra coisa; o banco guarda caminho relativo + sha256,
nunca a URL do provedor. O mesmo vale para todo agregador: URL de resultado é sempre temporária.

Um job que marcou `generated` sem ter baixado os bytes está mentindo sobre o próprio estado. Ver
`ai-agents/media-pipeline.md` §Storage.

---

## Moderação e pessoas

- **Bloqueio não cobra** (Veo). Trate-o como `rejected_by_policy`: estado terminal, **sem retry**.
  Retentar um bloqueio é gastar dinheiro para receber o mesmo "não" — e nos provedores que cobram o
  bloqueio, é gastar duas vezes.
- **`personGeneration` é restrito por região.** O mesmo prompt aprovado num mercado é recusado em
  outro; a política tem de ser por tenant, não global.
- **Rostos disparam moderação com frequência.** O motivo e o contorno operacional (enquadrar do
  pescoço para baixo, mãos, produto; reservar rosto para imagem estática) estão escritos em
  `frontend/design-system/ai-video-generation.md` §Section 3 — este standard cita e não repete.
- **Nunca gere rosto de cliente real.** Além do risco jurídico, é o caso que a marcação obrigatória
  do `media-pipeline.md` foi feita para cobrir.

---

## Deprecações

> **Toda data de desligamento deste standard mora nesta seção, e em lugar nenhum mais.**

Fonte, consultada em **2026-09-07**: `https://developers.openai.com/api/docs/deprecations`.

| Alias / superfície | Situação | Data |
|---|---|---|
| `sora-2`, `sora-2-pro` e a Videos API | **desligam, sem substituto** (anunciado 2026-03-24) | 2026-09-24 |
| `VideoClient` do `openai-dotnet` | morre junto com a API acima | 2026-09-24 |

**Próximo desligamento conhecido: 2026-09-24.** Se esta data já passou quando você está lendo, este
standard não está apenas velho — ele está **errado** em pelo menos uma afirmação, e a §Como
re-verificar diz qual página conferir. É falsificável em segundos, sem pesquisa.

---

## Checklist (verifiable by morph-eval)

- [ ] Nenhum código novo aponta para um alias de §Deprecações
- [ ] O id da operação/tarefa é **persistido antes** do primeiro polling
- [ ] O polling roda num job, com back-off e teto de tentativas — nunca numa request HTTP
- [ ] Os bytes são baixados assim que a tarefa conclui, e gravados em blob próprio
- [ ] O banco guarda caminho relativo + sha256, nunca a URL do provedor
- [ ] Bloqueio de moderação é estado terminal `rejected_by_policy`, sem retry
- [ ] Duração pedida respeita o mínimo do provedor (8 s no Veo quando há referência)
- [ ] Custo estimado gravado antes da chamada, com a tarifa de §Custo por segundo
- [ ] Nenhuma geração dentro de um turno de conversa
- [ ] Nenhum teste toca API paga — provider fake, sempre

---

## Como re-verificar este standard

| Página | O que conferir |
|---|---|
| `https://developers.openai.com/api/docs/deprecations` | A tabela de §Deprecações e a linha "próximo desligamento conhecido" |
| `https://ai.google.dev/gemini-api/docs/veo` | Duração, resolução, limite de referências, retenção de 2 dias, `personGeneration` |
| `https://ai.google.dev/gemini-api/docs/pricing` | As três linhas Veo de §Custo por segundo |
| `https://docs.byteplus.com/en/docs/ModelArk/1520757` e `https://fal.ai/models/bytedance/seedance-2.0/image-to-video` | A faixa de preço do Seedance nos dois canais |
| `https://www.nuget.org/packages/Google.GenAI` | Se `GenerateVideosAsync`/`Operations.GetAsync` mudaram de forma |

Ao terminar, atualize `Last-verified` **e** o `ai-pin` citado no header.

---

## References

- `ai-agents/media-pipeline.md` — o job, os estados, a idempotência, a marcação
- `ai-agents/modalities-image-gen.md` — imagem em runtime, e a escada de custo irmã desta
- `frontend/design-system/ai-video-generation.md` — footage direcional para scroll scrub via MCP:
  loop × direcional, `endImage`, moderação E005. Consumidor diferente, tempo diferente
- `ai-agents/providers/model-registry.md` — o alias por provedor, com `api: videos`
- `backend/integrations/hangfire/hangfire-jobs.md` — o mecanismo de job que o polling usa

---

*MORPH-SPEC by Polymorphism Tech — ai-agents/media-video.md v1.0*
