YouTube
Status: 🟢 Estável (Hub.6 deployado) Code: backend/app/modules/youtube UI: frontend/src/app/(admin)/[slug]/admin/marketing (seção YouTube) Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: marketing (Hub framework), attribution (campaign bridge)
1. Identidade
O que faz (uma frase)
Plugin YouTube do Mandir Hub — observa canal via Data API v3 + Analytics API v2, persiste canal, vídeos, métricas diárias, traffic sources, search terms, geography, demographics, playlists em 9 tabelas L0, e expõe 6 tools pro Conselheiro de Conteúdo & Marca.
Por que existe (negócio)
YouTube é canal de aquisição de longo prazo do Mais Consciente. Sem este módulo:
- Felipe abre YouTube Studio toda vez.
- Conselheiro Content & Brand não tem acesso a métricas pra responder "qual vídeo tá performando?".
Hub.6 traz histórico no DB + 6 tools no Conselho:
- Top videos por views/watch_time.
- Traffic sources (busca interna, sugeridos, externo).
- Search terms (o que público buscou pra chegar).
- Geography + demographics.
Status atual
- Hub.6 deployado em 2026-05-13 (commit
613450e, alembic 0107). - OAuth Brand Account configurado — quirks documentados na memória [[oauth-brand-account-youtube]].
2. Cases de uso reais
Case 1: Felipe pergunta "qual vídeo bombou semana passada?"
Fluxo: Conselheiro Content & Brand chama read_youtube_top_videos(window_days=7, sort_by=views) → top 10 vídeos com views, watch_time, likes → Conselheiro sintetiza e propõe ação ("repostar formato X").
Case 2: Detecção de queda de subs
Beat marketing.run_observe(provider=youtube) → updates youtube_channel_daily.subscribers_lost → se queda > threshold → emit anomaly → intelligence.
3. Oportunidades de negócio
- YouTube SEO insights: search_term_daily ajuda otimizar títulos/descrições — produto pra creators.
- Cross-channel content tracking: vídeo X gera tráfego pro site (via GA4) — bridge YouTube → GA4.
- Brand Account multi-channel: se cliente tem N canais, consolidar tudo num painel.
Riscos: Brand Account exige Workspace owner direto (não Manager pessoal); Analytics API quota 500M units/dia/projeto; subscribers_gained pode ser 0 em dias com baixa atividade.
4. Arquitetura interna
Arquivos
| Arquivo | Propósito |
|---|---|
__init__.py | register_plugin(YouTubePlugin()) |
plugin.py | Implementa observe() (Operate vem em Hub.9) |
client.py | Wrappers Data API v3 + Analytics API v2 (OAuth user consent) |
models.py | 9 tabelas |
sync.py | sync_channel, sync_videos_catalog, sync_channel_daily, sync_video_daily (top 200), sync_traffic_sources, sync_search_terms, sync_geography, sync_demographics, sync_playlists |
attribution_bridge.py | Cria attribution_campaign(kind=organic_youtube) |
Auth
- OAuth user consent (
access_token+refresh_token) cifrado emmarketing_credential.secret_envelope. - Refresh automático via
credentials.mark_token_refreshed. - Brand Account: requer Workspace owner direto, app GCP em Production.
5. Tabelas (9)
youtube_channel
Snapshot canal (Data API channels.list).
| Coluna | Notas |
|---|---|
external_id | Channel ID (ex: UCH6kecvLOgXgk3HmJ0CuF1w) |
handle / title / description / custom_url | |
country / default_language | |
thumbnail_url / published_at | |
subscriber_count / hidden_subscriber_count | |
view_count / video_count | |
uploads_playlist_id | Pra paginação de vídeos |
youtube_channel_daily
Métricas diárias (Analytics API).
| Coluna | Notas |
|---|---|
views / estimated_minutes_watched | INT |
average_view_duration_seconds / average_view_percentage | |
subscribers_gained / subscribers_lost | |
likes / dislikes / comments / shares | |
annotation_* / card_* | Engagement detail |
estimated_revenue / estimated_ad_revenue / estimated_red_partner_revenue | DECIMAL |
cpm / playback_based_cpm | |
monetized_playbacks / ad_impressions |
youtube_video
Catálogo (Data API videos.list).
| Coluna | Notas |
|---|---|
external_id | Video ID |
title / description / published_at | |
thumbnail_url / duration_seconds | |
definition / caption_available / licensed_content | |
privacy_status / upload_status | |
category_id / default_language | |
made_for_kids | |
tags (JSONB) | |
is_short (BOOL) | <60s vertical |
view_count / like_count / comment_count / favorite_count |
youtube_video_daily
Métricas por vídeo × dia (Analytics API + filter video=).
Similar a channel_daily mas com video_external_id. Limita top 200 vídeos por economia de quota.
youtube_traffic_source_daily
| Coluna | Notas |
|---|---|
source | YT_SEARCH / EXTERNAL / RELATED_VIDEO / SUBSCRIBER / BROWSE_FEATURES / SHORTS / etc. |
views |
youtube_search_term_daily
| Coluna | Notas |
|---|---|
search_term | Texto buscado |
views / estimated_minutes_watched |
youtube_geography_daily
| Coluna | Notas |
|---|---|
country | ISO code |
views |
youtube_demographics
Snapshot mensal (não diário — dedupe por (tenant, channel, snapshot_date, age_group, gender)).
| Coluna | Notas |
|---|---|
age_group | age13-17 / age18-24 / ... / age65- |
gender | female / male / user_specified / unknown |
youtube_playlist
Catálogo playlists.
Relacionamentos cross-módulo
| Direção | Outro módulo | Como |
|---|---|---|
| ↘ Escreve | attribution | Cria attribution_campaign(kind=organic_youtube) por novo channel/video |
| ↘ Tools | council | 6 tools |
6. API / Endpoints
| Método | Rota | O que faz |
|---|---|---|
| GET | /api/marketing/youtube/summary | Views, minutes, subs gained/lost, likes, comments, revenue (window) |
| GET | /api/marketing/youtube/videos | Top vídeos por métrica (views/minutes/likes/comments/revenue) |
| GET | /api/marketing/youtube/traffic-sources | Breakdown por source |
| GET | /api/marketing/youtube/geography | Views por país |
| GET | /api/marketing/youtube/demographics | Age/gender (latest snapshot) |
| GET | /api/marketing/youtube/search-terms | Top searches |
7. Tools no Council (6)
read_youtube_summary, read_youtube_top_videos, read_youtube_traffic_sources, read_youtube_search_terms, read_youtube_geography, read_youtube_demographics.
8. Configuração
Credencial
| Campo | Valor |
|---|---|
provider | youtube |
account_external_id | channel_id |
auth_kind | oauth2 |
secret_envelope | {channel_id, access_token, refresh_token} |
scope | ["https://www.googleapis.com/auth/youtube.readonly", "https://www.googleapis.com/auth/yt-analytics.readonly"] |
Setup OAuth Brand Account (memória [[oauth-brand-account-youtube]])
- App GCP em Production (não Testing).
- Login com Workspace dona direta (não Manager pessoal).
- OOB deprecou — usar loopback
localhost. - Felipe ignora warning "app não verificado" (interno).
9. Operações
Como conectar canal
- UI Marketing → Conectar YouTube → OAuth flow.
- User autoriza no Google.
- Backend cifra secret + cria credencial.
- Trigger sync inicial:
POST /sync/run?provider=youtube&credential_id=Y.
Troubleshooting
Sintoma: Sync falha com unauthorized
Causa: Token expirou e refresh falhou. Fix: Reconectar (gera novo refresh_token).
Sintoma: Demographics vazio
Causa: Canal pequeno (< threshold YouTube reporta). Tratamento: Esperado.
10. Limitações e débitos técnicos
| # | Item |
|---|---|
| 1 | 2 dias delay reporting |
| 2 | Brand Account quirks |
| 3 | Top 200 vídeos no daily |
| 4 | Demographics snapshot mensal |
| 5 | MIGRATION_DATABASE_URL hardcoded |
11. Histórico
- 2026-05-13 (commit
613450e, alembic 0107) — Hub.6 deployado. 9 tabelas + 6 tools. Memória [[sessao-2026-05-13-hub56]].