Files
claude-plugins/wordpress/skills/seriously-simple-podcasting/SKILL.md
T
ealmeida 82c800bfe0 feat(wordpress): novas skills seriously-simple-podcasting + mcp-seriously-simple-podcasting
Documenta as 14 tools do MCP dedicado (mcp-seriously-simple-podcasting, porta 3203) para
episódios/shows/definições de show em emanuelalmeida.pt, complementado pela skill de
conhecimento seriously-simple-podcasting (achado central: REST nativo do plugin é
escrita-capaz para episódios mas read-only para definições de show).
2026-08-19 06:14:32 +01:00

163 lines
11 KiB
Markdown

---
name: seriously-simple-podcasting
description: Conhecimento profundo do Seriously Simple Podcasting (Castos, plugin `seriously-simple-podcasting`) — arquitectura, CPT `podcast` (episódios) e taxonomia `series` (shows), meta fields custom, REST API nativo (o que é escrita-capaz vs read-only), wp_options de definições de show (`ss_podcasting_*`), categorias iTunes/Apple Podcasts, feed RSS, e o estado real em emanuelalmeida.pt (39 episódios, 8 shows, sem addon Castos/Pro). Usar quando "seriously simple podcasting", "podcast wordpress", "episódio podcast", "show podcast", "feed rss podcast", "categorias itunes", "apple podcasts categoria", "castos", "ss_podcasting", "cpt podcast", ou qualquer trabalho de configuração/estratégia de podcast que não seja apenas chamar o MCP. Para execução directa (CRUD de episódios/shows), usar a tool MCP `mcp-seriously-simple-podcasting`.
layer: wiki
---
# /seriously-simple-podcasting — Seriously Simple Podcasting (Castos) — conhecimento de plugin
Plugin `seriously-simple-podcasting` (Free, `SSP_VERSION` 3.17.0), instalado e activo em
**emanuelalmeida.pt**. **Sem addon Castos/Pro** — não há `fluentcampaign-pro`-equivalente neste
site (confirmado: pasta `php/` do plugin não tem addon Pro, `ssp_is_connected_to_castos()` devolve
falso, sem hospedagem/sincronização externa de ficheiros). Mapeamento feito por leitura completa
do código-fonte (`wp-content/plugins/seriously-simple-podcasting/php/`) via SSH, 19-08-2026, antes
de construir o MCP `mcp-seriously-simple-podcasting`.
---
## 0. Free vs addon Castos (não instalado aqui)
| | Free (activo) | Addon Castos/Pro (ausente) |
|---|---|---|
| Episódios (CPT `podcast`), shows (taxonomia `series`), feed RSS | ✅ core | — |
| Upload/hosting de ficheiros de áudio | ❌ (URL externo só) | ✅ hosting Castos + analytics avançado |
| Sincronização automática (`podmotor_*` meta) | campos existem mas vazios (nunca usados) | ✅ |
| Transcrições, capítulos | não encontrado no código lido (sem módulo dedicado nesta versão) | — |
Todos os 39 episódios reais têm `audio_file` a apontar para `episodes.castos.com` (CDN legado de
uma ligação Castos anterior, hoje desligada) — os ficheiros continuam a servir de lá, mas o plugin
já não está autenticado/sincronizado com a conta.
---
## 1. Onde vivem os dados
### 1.1 Episódios — CPT `podcast` (constante `SSP_CPT_PODCAST`)
Post standard (`show_in_rest => true`), **sem tabela própria** — tudo em `wp_posts`/`wp_postmeta`.
Meta fields registados via `register_meta()` (todos `show_in_rest => true`, confirmado em
`CPT_Podcast_Handler::register_meta()`):
| Meta key | Tipo | Nota |
|---|---|---|
| `episode_type` | `audio`\|`video` | |
| `audio_file` | URL | ficheiro (ou stream) do episódio |
| `cover_image` / `cover_image_id` | URL / attachment ID | imagem quadrada, `cover_image_id` é interno (não escrever directamente) |
| `duration` | texto | ex. `00:32:14`, calculado automaticamente se possível, mas aceita override manual |
| `filesize` / `filesize_raw` | texto / bytes | `filesize_raw` é interno/calculado, usado no RSS — não escrever directamente |
| `date_recorded` | `Y-m-d` | distinto de `post_date` (data de publicação) |
| `explicit` / `block` | checkbox (`'on'`\|`''`) | `block` esconde de directórios iTunes/Google |
| `itunes_episode_number` / `itunes_season_number` | número | tags iTunes WWDC 2017 |
| `itunes_title` | texto | título específico para o feed iTunes (sem número de episódio/show) |
| `itunes_episode_type` | `full`\|`trailer`\|`bonus` | |
| `podmotor_file_id` / `podmotor_episode_id` / `castos_file_data` / `sync_status` | — | só-Castos, sempre vazios neste site |
### 1.2 Shows — taxonomia `series` (função `ssp_series_taxonomy()`, devolve `'series'`)
Termo hierárquico, `show_in_rest => true`, `rest_base => 'series'`. Um episódio pode pertencer a
0-N shows (`wp_set_post_terms`). Tags normais (`post_tag`) também estão ligadas ao CPT por
omissão (filtro `ssp_use_post_tags`, `true` neste site) — existe também uma taxonomia alternativa
`podcast_tags`, mas só é registada se `ssp_use_post_tags` for desligado (não é o caso aqui).
### 1.3 Definições de show — `wp_options`, NUNCA numa tabela/post
Cada show tem um bloco de opções `ss_podcasting_data_{campo}_{series_id}` (título, subtítulo,
descrição, autor, imagem, owner_name/email, idioma, copyright, category/subcategory 1-3) **mais**
duas excepções sem o infixo `data_`: `ss_podcasting_explicit_{series_id}` e
`ss_podcasting_complete_{series_id}` (gotcha — ver §6). Confirmado por SQL directo em
`emanuelalmeida.pt`: 18 opções distintas escritas para o show "Afirmações" (id 120).
Existe também um conjunto **sem sufixo `_{series_id}`** — os defaults globais do site (usados
quando um show não tem override próprio), lidos em
`Rest_Api_Controller::get_default_podcast_settings()`.
---
## 2. REST API — o que é escrita-capaz vs read-only (achado central)
| Endpoint | Leitura | Escrita |
|---|---|---|
| `GET/POST/PUT /wp/v2/podcast` | ✅ episódios completos + todos os meta fields do §1.1 | ✅ (CPT standard, `show_in_rest`, capacidades `edit_podcasts` mapeadas para editor/administrator) |
| `GET/POST/PUT /wp/v2/series` | ✅ termos + **campos extra injectados** (title/subtitle/author/owner_name/owner_email/language/copyright/image/category1-3/explicit_option/complete_option) | ❌ os campos extra são só-leitura — `register_rest_field()` em `Rest_Api_Controller::create_api_series_fields()` só regista `get_callback`, **sem `update_callback`**. Confirmado lendo o código-fonte; um `PUT` no termo não altera nenhuma destas definições. |
| `GET /ssp/v1/episodes` | ✅ query cross-post-type (namespace próprio) | endpoint dedicado só para `podmotor_episode_id`/`audio_file` (uso interno Castos) |
**Implicação prática:** episódios podem ser geridos 100% via REST nativo (Application Password),
mas **definições de show só podem ser escritas via `wp_options` directamente** (WP-CLI `wp option
update` ou, como o MCP faz, `update_option()` dentro de um `wp eval`). É por isto que
`mcp-seriously-simple-podcasting` usa o bootstrap WordPress completo (`wp eval`) para tudo, em vez
de misturar REST para episódios e SQL para shows — um único mecanismo, sempre consistente.
---
## 3. wp-admin — páginas principais
| Página | Conteúdo |
|---|---|
| **Podcasting → All Episodes** | lista de episódios (CPT `podcast`), com metabox de todos os campos do §1.1 |
| **Podcasting → Add New Episode** | criação, campo de show (taxonomia `series`) na sidebar |
| **Podcasting → Settings → General** | defaults globais do site |
| **Podcasting → Settings → Feed Details** | por show (selector "feed-series" no topo) — título/subtítulo/owner/categorias/idioma/copyright/imagem — é aqui que se editam manualmente as opções do §1.3 |
| **Podcasting → Settings → Hosting** | ligação Castos (desligada neste site) |
| **Podcasting → Settings → Import** | importar de outro feed RSS |
---
## 4. Categorias iTunes/Apple Podcasts
19 categorias de topo (`Arts`, `Business`, `Comedy`, `Education`, `Fiction`, `Government`,
`History`, `Health & Fitness`, `Kids & Family`, `Leisure`, `Music`, `News`,
`Religion & Spirituality`, `Science`, `Society & Culture`, `Sports`, `Technology`, `True Crime`,
`TV & Film`), a maioria com subcategorias próprias (`Government`/`History`/`Technology`/
`True Crime` não têm) — é a taxonomia oficial Apple Podcasts, lida de
`php/config/settings/feed-categories.php` e `feed-subcategories.php`. Cada show suporta até 3
pares categoria/subcategoria (`category`/`subcategory`, `category2`/`subcategory2`,
`category3`/`subcategory3`). Lista completa disponível na tool `ssp_get_itunes_categories` do MCP
(sem chamada SSH — é referência estática).
---
## 5. Feed RSS
URL construído a partir de `home_url()` + `feed/podcast` (ou `?feed=podcast` sem permalinks
bonitos) + `/{slug-do-show}` para o feed de um show específico. Cada show tem o seu próprio feed
filtrado (`Episode_Repository::get_feed_url()`); o feed "geral" (sem `series_id`) inclui episódios
de todos os shows.
---
## 6. Estado real em emanuelalmeida.pt (verificado 19-08-2026)
- **39 episódios**, todos `publish`, distribuídos por **8 shows**: Meditação (11), DDE Desafio
Detox Emocional (10), Afirmações (8), Reprogramação mental (6), Motivacionais (3), + 3 shows
menores/vazios (incl. "Emanuel Almeida", 0 episódios).
`audio_file` sempre aponta para o CDN legado `episodes.castos.com` (ligação Castos desligada,
ficheiros continuam a servir de lá).
- **Nenhum episódio marcado `explicit`** (único valor visto na coluna: `''`).
- Plugins activos relevantes no mesmo site: Rank Math SEO PRO, Wordfence, WP Meteor, WP Fastest
Cache, `seguranca-descomplicar` — nenhum interfere com REST/feeds do SSP nesta verificação
(`curl` a `/wp-json/wp/v2/podcast` e `/feed/podcast/...` devolveram 200 sem bloqueio).
---
## 7. Gotchas
| Sintoma | Causa | Nota |
|---|---|---|
| `wp/v2/series` devolve `explicit_option`/`complete_option` sempre vazios ou iguais ao default global, mesmo com override por show definido | Bug do próprio plugin: `series_get_field_value()` faz fallback genérico para `ss_podcasting_data_{campo}_{series_id}`, mas `explicit`/`complete` são gravados em `ss_podcasting_{campo}_{series_id}` (sem `data_`) — o REST nunca encontra o override | Ler via `ssp_get_series_settings` do MCP (lê directamente com o prefixo correcto) em vez de confiar no REST nativo para estes dois campos |
| `PUT /wp/v2/series/{id}` com `title`/`owner_email`/etc no payload não faz nada | Campos são `get_callback`-only (§2) | Escrever via `ssp_update_series_settings` do MCP (`update_option`) |
| `ssp_delete_series`/qualquer verificação de "show tem episódios" baseada em `term->count` dá 0 mesmo com episódios associados | `term_taxonomy.count` do WordPress só conta posts **publicados** — episódios em `draft`/`pending`/`future` não entram na contagem | Não assumir "count=0 → seguro apagar"; confirmar sempre com `ssp_list_episodes` incluindo `status=any` antes de remover um show |
| Nenhum registo de transcrições/capítulos encontrado no código | Este site usa a versão Free sem addon; não há módulo de transcrição na árvore lida (`grep -i transcript` só encontra uma referência de string solta num metabox, sem implementação funcional associada) | Não assumir suporte — verificar de novo se o plugin for actualizado/o addon Castos for religado |
---
## Fonte
Leitura completa do código-fonte `seriously-simple-podcasting` 3.17.0 via SSH em
emanuelalmeida.pt (`php/classes/handlers/class-cpt-podcast-handler.php`,
`class-series-handler.php`, `php/classes/repositories/class-episode-repository.php`,
`php/classes/rest/class-rest-api-controller.php`, `class-episodes-rest-controller.php`,
`php/classes/controllers/class-settings-controller.php`, `php/config/settings/*.php`,
`php/includes/ssp-functions.php`), cruzada com `SHOW TABLES`/`DESCRIBE`/agregações reais via SQL e
chamadas REST reais (`curl`), 19-08-2026 — sessão de construção do MCP
`mcp-seriously-simple-podcasting`. Ver essa skill para a lista completa de 14 tools programáticas.