# Contracts: Vídeo de Apresentação do Imóvel (033) **Gerado por**: /speckit.plan **Data**: 2026-04-22 --- ## Endpoint Afetado: PUT /api/admin/properties/:id **Autenticação**: JWT admin obrigatório (Bearer token) ### Request Body — Campos adicionados ```json { "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "video_position": "section" } ``` | Campo | Tipo | Obrigatório | Valores Aceitos | Default | |-------|------|-------------|-----------------|---------| | `video_url` | `string \| null` | Não | Qualquer string válida ou `null` | Campo não enviado = não alterado | | `video_position` | `string` | Não | `"carousel"` \| `"section"` | `"section"` | **Comportamento especial**: - `video_url: ""` (string vazia) → equivalente a `null` (remove o vídeo) - `video_url` com espaços → sanitizado via `.strip()` antes de persistir - Omitir `video_url` e `video_position` → campos não são alterados (comportamento PUT parcial existente) ### Response Body — Campos adicionados ```json { "id": "uuid", "title": "...", "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "video_position": "section", "..." } ``` --- ## Endpoint Afetado: GET /api/properties/:slug ### Response Body — Campos adicionados em `PropertyDetailOut` ```json { "id": "uuid", "slug": "apartamento-centro", "title": "...", "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "video_position": "section", "photos": [...], "..." } ``` | Campo | Tipo | Nullable | Descrição | |-------|------|----------|-----------| | `video_url` | `string \| null` | Sim | URL do vídeo ou `null` se não configurado | | `video_position` | `"carousel" \| "section"` | Não | Posição de exibição; default `"section"` | --- ## Contrato Frontend — Utilitário `getEmbedUrl` **Arquivo**: `frontend/src/utils/getEmbedUrl.ts` ```ts export type VideoType = 'youtube' | 'vimeo' | 'direct' | 'unknown' export interface EmbedResult { type: VideoType embedUrl: string | null } export function getEmbedUrl(url: string): EmbedResult ``` ### Tabela de transformação | URL de entrada | `type` | `embedUrl` | |----------------|--------|-----------| | `https://www.youtube.com/watch?v=VIDEO_ID` | `youtube` | `https://www.youtube.com/embed/VIDEO_ID` | | `https://youtu.be/VIDEO_ID` | `youtube` | `https://www.youtube.com/embed/VIDEO_ID` | | `https://www.youtube.com/embed/VIDEO_ID` | `youtube` | URL original (já é embed) | | `https://vimeo.com/VIDEO_ID` | `vimeo` | `https://player.vimeo.com/video/VIDEO_ID` | | `https://player.vimeo.com/video/VIDEO_ID` | `vimeo` | URL original (já é embed) | | `https://example.com/video.mp4` | `direct` | URL original | | `https://example.com/video.webm` | `direct` | URL original | | Qualquer outro formato | `unknown` | `null` | --- ## Contrato Frontend — Componente `VideoPlayer` **Arquivo**: `frontend/src/components/PropertyDetail/VideoPlayer.tsx` ```ts interface VideoPlayerProps { url: string className?: string } ``` ### Comportamento de renderização | `type` retornado por `getEmbedUrl` | Renderização | |------------------------------------|-------------| | `youtube` \| `vimeo` | `