# Enviar mídia: arquivo inteiro e partes

> Como enviar vídeo, capa e foto pela API do Praeko: o envio de uma vez (até 50 MiB), o envio em partes de 5 a 95 MiB, tipos aceitos, limites e erros, e como publicar uma foto única no Instagram.

Fonte: https://praeko.app/docs/guides/media

Todo vídeo, toda capa e toda foto passam por `/v1/media` antes de entrar em um post. Há dois caminhos, conforme o tamanho.

|  | Arquivo inteiro | Em partes |
|---|---|---|
| Quando | Até 50 MiB | Qualquer tamanho (vídeo até 5 GiB) |
| Como criar | `POST /v1/media` com `simple: true` | `POST /v1/media` sem `simple` |
| Como enviar | `PUT /v1/media/:id/content` com os bytes | `PUT /v1/media/:id/parts/:part` para cada parte |
| Como terminar | O próprio `PUT` conclui | `POST /v1/media/:id/complete` |

## Tipos e tamanhos aceitos

| Tipo (`type`) | `mime` aceito | Tamanho máximo |
|---|---|---|
| `video` | `video/mp4`, `video/quicktime` | 5 GiB |
| `cover` | `image/png`, `image/jpeg` | 20 MiB |
| `image` | `image/jpeg` (só JPEG) | 20 MiB |

`image` é a foto de um post de imagem. O servidor confere que o arquivo é um JPEG de verdade (`422 invalid_image` se não for: no envio inteiro, antes de gravar; no envio em partes, ao completar) e lê a largura e a altura do cabeçalho (`width` e `height` na mídia). Ele **não converte**: exporte a foto em JPEG sRGB (o Praeko Studio faz isso).

## Foto única no Instagram

Suba a foto com `type: image` e crie o post com `kind: image` e **uma** imagem em `media.images` (várias seriam um carrossel, que ainda não existe: `422 carousel_not_supported`). Só o Instagram aceita foto por enquanto, pelo Buffer ou direto pela Meta; YouTube e TikTok respondem `422 kind_not_supported_by_network`.

**json**

```json
{
  "kind": "image",
  "title": "Dica: PRs pequenos",
  "media": {
    "images": [
      {
        "media": "01KA3V9...",
        "alt": "Slide com o título da dica"
      }
    ]
  },
  "texts": {
    "instagram": {
      "text": "Um PR, uma ideia. #devbrasil"
    }
  },
  "items": [
    {
      "network": "instagram",
      "via": "meta",
      "queue": true
    }
  ]
}
```

| Regra do Instagram | Valor | Se não cumprir |
|---|---|---|
| Formato | JPEG | `422 invalid_image` |
| Tamanho | 8 MB | `422 image_too_large` |
| Proporção | de 4:5 a 1,91:1 (use 1080x1350) | `422 image_aspect_ratio` |
| Largura | 320 a 1440 px (a Meta amplia ou reduz o resto) | aviso `image_small` ou `image_will_be_resized` |
| Legenda | até 2200 caracteres e 30 hashtags | `422 invalid_field` |
| Texto alternativo (`alt`) | até 1000 caracteres | pelo Buffer é ignorado: aviso `alt_ignored` |

Os avisos voltam em `items[].warnings` (cada um com `code` e `message`, estáveis) e não impedem a criação. Pela Meta, o `alt` vai no contêiner da foto; pelo Buffer, que não tem texto alternativo documentado, ele é ignorado e o item avisa. O post volta com `kind` e `media.images`; a imagem de um post de imagem não troca por `PATCH` (cancele e crie de novo).

## Arquivo inteiro

**curl**

```bash
curl -X POST https://praeko.app/v1/media \
  -H "Authorization: Bearer $PRAEKO_KEY" -H "X-Praeko-Contract: en" \
  -H "Content-Type: application/json" \
  -d '{ "type": "video", "name": "v.mp4", "mime": "video/mp4", "size": 8123456, "simple": true }'
curl -X PUT https://praeko.app/v1/media/$MEDIA_ID/content \
  -H "Authorization: Bearer $PRAEKO_KEY" -H "Content-Type: video/mp4" \
  --data-binary @v.mp4
```

Com `simple: true` e `size` acima de 50 MiB a criação é recusada (`422 file_too_large`): use partes. O corpo do `PUT` precisa ter exatamente o `size` combinado (`422 size_mismatch`).

## Envio em partes

A resposta de `POST /v1/media` (sem `simple`) traz `minPartSize` (5 MiB), `maxPartSize` (95 MiB) e `suggestedPartSize` (16 MiB). Numere as partes de 1 a 10000, envie cada uma em `PUT /v1/media/:id/parts/:part` com os bytes crus e termine com `POST /v1/media/:id/complete`.

- Todas as partes têm o mesmo tamanho; só a última pode ser menor (`422 parts_unequal`). Toda parte, menos a última, precisa ter pelo menos 5 MiB (`422 part_too_small`); nenhuma passa de 95 MiB (`413 part_too_large`).
- Reenviar a mesma parte é seguro: ela é sobrescrita.
- Se faltar alguma parte da sequência, `complete` responde `422 parts_missing`; se o total não bate com o `size` combinado, `422 size_mismatch`.
- Um envio que não termina em 24 horas é descartado (a mídia sai da biblioteca).

**curl**

```bash
# 16 MiB por parte; a última leva o que sobrar
split -b 16m v.mp4 parte_
n=1; for f in parte_*; do
  curl -X PUT "https://praeko.app/v1/media/$MEDIA_ID/parts/$n" \
    -H "Authorization: Bearer $PRAEKO_KEY" -H "Content-Type: video/mp4" \
    --data-binary @"$f"
  n=$((n+1))
done
curl -X POST https://praeko.app/v1/media/$MEDIA_ID/complete \
  -H "Authorization: Bearer $PRAEKO_KEY" -H "X-Praeko-Contract: en"
```

## Estado e uso

A mídia nasce `uploading` e vira `ready` quando o envio termina. Vídeos `ready` trazem `durationMs`, `width` e `height` quando o cabeçalho do arquivo informa (vídeos antigos ganham os valores na primeira leitura), e `GET /v1/media/:id` traz também `publicUrl`. Só use no post uma mídia `ready` (senão, `409 media_not_ready`). `DELETE /v1/media/:id` recusa mídia usada por um post pendente (`409 media_in_use`).

`durationMs` é a duração em milissegundos lida do arquivo. Não confunda com o `duration` de `GET /v1/youtube/videos`, que é a duração que o YouTube informa, em ISO 8601 (por exemplo, `PT58S`).

> **Pelo SDK ou por um agente** O `uploadFile` do `@praeko/sdk/node` envia o arquivo do disco e escolhe o caminho sozinho (um `PUT` até 32 MiB; acima, partes de 16 MiB): [SDK](https://praeko.app/docs/sdk). Um agente com shell usa as ferramentas `create_media_upload` e `complete_media_upload` do [servidor MCP](https://praeko.app/docs/mcp).
