> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pictor.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Rever a gravação

> Dois acervos independentes: o do alarme e a linha contínua.

"O que aconteceu às 3h12?" tem duas respostas, e elas vêm de acervos diferentes.
Escolher o errado devolve `404` numa câmera que tem o vídeo.

| Você quer…                         | Rota                          | O que é                                           |
| ---------------------------------- | ----------------------------- | ------------------------------------------------- |
| o clipe que o alarme guardou       | `GET /api/recording-segments` | gravações **intencionais**: alarme, ronda, manual |
| voltar a fita num horário qualquer | `GET /api/playback/{id}`      | a linha do tempo **contínua**                     |

<Warning>
  Os dois acervos são independentes. Uma câmera pode ter clipe de alarme e não
  gravar continuamente, e o contrário também. Se a primeira rota não achou, tente a
  segunda antes de dizer ao operador que não há vídeo.
</Warning>

## 1. O que o alarme mandou guardar

```bash theme={null}
curl -s -H "Authorization: Bearer $PICTOR_KEY" \
  "$PICTOR_API/api/recording-segments?from=2026-09-03T03:00:00Z&to=2026-09-03T04:00:00Z&limit=50"
```

```json theme={null}
{
  "items": [
    {
      "recording_id": "…",
      "camera_id": "G87232574",
      "camera_name": "Guarita - entrada",
      "site_name": "Cond. Alvorada — Portaria",
      "client_name": "Cond. Alvorada",
      "started_at": "2026-09-03T03:12:04Z",
      "duration_s": 62.0,
      "partial": false,
      "playback_url": "https://…/api/storage/recordings/….mp4?…",
      "alarm": {
        "alarm_id": "…",
        "event_label": "Abertura de porta",
        "event_group": "Alarme",
        "account": "4021",
        "occurred_at": "2026-09-03T03:12:02Z"
      }
    }
  ],
  "total": 1, "limit": 50, "offset": 0
}
```

**Cada linha já vem tocável**: `playback_url` é um endereço assinado, sem segunda
chamada e sem você saber nada do armazenamento.

Três campos que enganam:

* **`alarm` ausente** significa que a gravação **não** nasceu de um evento
  (manual, agendada). A ausência é a informação.
* **`partial: true`** significa que a gravação não fechou limpa: o vídeo pode
  truncar no fim. Mostre isso antes de o operador concluir que "acabou aí".
* **`camera_ref` vazio** acontece quando a câmera foi apagada do cadastro. A
  gravação sobrevive à câmera; o identificador que casa com o seu lado, não.

### Buscar por texto

`q` alcança nome de câmera, local, cliente, descrição do evento e conta:

```bash theme={null}
"…/api/recording-segments?q=alvorada&from=2026-09-03T00:00:00Z"
```

### Abrir uma, e achar os outros ângulos

```bash theme={null}
curl -s -H "Authorization: Bearer $PICTOR_KEY" \
  "$PICTOR_API/api/recording-segments/$RECORDING_ID"
```

Peça esta rota quando o operador **for de fato abrir** o vídeo: a assinatura sai
nova, com prazo cheio.

Ela traz `related`, as outras gravações do **mesmo alarme**:

```json theme={null}
{
  "recording_id": "…",
  "playback_url": "https://…",
  "related": [
    { "recording_id": "…", "camera_name": "Portaria - rua",   "started_at": "…" },
    { "recording_id": "…", "camera_name": "Garagem - rampa",   "started_at": "…" }
  ]
}
```

<Tip>
  Desenhe `related` na tela. Um evento num local com oito câmeras produz oito
  gravações, e sem essa lista quem abre uma não sabe que as outras sete existem.
  Varrer os ângulos do mesmo instante é o que resolve o atendimento.
</Tip>

## 2. Voltar a fita

Para câmera que grava sem parar, peça a janela direto:

```bash theme={null}
curl -s -H "Authorization: Bearer $PICTOR_KEY" \
  "$PICTOR_API/api/playback/CAM-001?start=2026-09-03T03:10:00Z&end=2026-09-03T03:20:00Z"
```

A resposta é a playlist HLS (`application/x-mpegURL`), pronta para o player:

```js theme={null}
async function reverTrecho(deviceId, inicio, fim) {
  // De novo: a chave fica no SEU backend. Ele repassa o texto da playlist,
  // ou a serve a partir de um endereço seu.
  const params = new URLSearchParams({ start: inicio, end: fim });
  const r = await fetch(`/minha-api/playback/${deviceId}?${params}`);
  if (!r.ok) return semVideo();

  const m3u8 = await r.text();
  const url = URL.createObjectURL(new Blob([m3u8], { type: 'application/x-mpegURL' }));
  const { default: Hls } = await import('https://cdn.jsdelivr.net/npm/hls.js@1/+esm');
  const hls = new Hls();
  hls.loadSource(url);
  hls.attachMedia(document.getElementById('cam'));
}
```

`start` e `end` são ISO 8601 **com fuso**. Sem vídeo guardado na janela, `404`,
que aqui quer dizer as duas coisas ao mesmo tempo ("não há vídeo" e "não é desta
chave"), de propósito.

## O prazo

Todo endereço de vídeo aqui é assinado e vence
([quem arquiva, arquiva os bytes](/limites)).

```python theme={null}
def anexar_na_ocorrencia(gravacao: dict) -> None:
    # Baixe no momento em que decide arquivar. O link não é o arquivo.
    bytes_ = httpx.get(gravacao["playback_url"]).content
    guardar_no_seu_storage(gravacao["recording_id"], bytes_)
```
