Skip to main content

EpisodeChaptersController

\Benecaster\REST\EpisodeChaptersController

Class Free

REST controller serving the Podcasting 2.0 chapters JSON endpoint and the auto-suggest endpoint. GET /wp-json/benecaster/v1/episodes/{id}/chapters — public, unauthenticated. Response follows the Podcasting 2.0 chapters JSON schema v1.2.0: {version, title, chapters: [{startTime, title, url?, img?}]}. Emits Content-Type: application/json+chapters; charset=UTF-8, Cache-Control: public, max-age=300, an ETag, and supports If-None-Match for 304 responses. Returns 404 for unknown or non-published episode IDs. Referenced from RSS via <podcast:chapters url="…" type="application/json+chapters" />. Podcast apps fetch it on playback start, hence the caching. POST /wp-json/benecaster/v1/episodes/{id}/chapters/auto-suggest — authenticated (edit_posts, a wp_rest nonce, and permission to edit that episode). This is a stub in Benecaster: it defers to the benecaster_chapter_autosuggest filter, which the Transcription Service add-on (or similar) hooks to read the episode transcript and return suggested chapter rows. Nothing is written to post_meta by this endpoint — the response only pre-fills the chapter editor, and the podcaster must save the episode to persist it.

Methods

Method Visibility Since Description
register_routes(): void Public — Registers GET /episodes/{id}/chapters (public) and POST /episodes/{id}/chapters/auto-suggest (edit_posts, a wp_rest nonce and permission to edit that episode). Both validate the {id} path param is numeric via a validate_callback.
get_chapters( WP_REST_Request $request ): WP_REST_Response|WP_Error Public — 404s (benecaster_episode_not_found / benecaster_episode_not_public) for a missing episode or one not in publish status. Otherwise loads chapters via EpisodeMeta::get_chapters(), maps each row through format_row() into the Podcasting 2.0 shape, and builds the response body. Computes an ETag as md5() of the JSON-encoded body and, when the request's If-None-Match header matches (via client_etag_matches()), swaps the response to a 304 with a null body instead of resending the payload.
auto_suggest_chapters( WP_REST_Request $request ): WP_REST_Response|WP_Error Public — 404s for an unknown episode ID. Applies the benecaster_chapter_autosuggest filter (default []) with the episode ID and post object; 501 benecaster_chapter_autosuggest_unavailable when the filter returns a non-array or empty array (no add-on hooked it, or the add-on found nothing usable). Rows are then normalised: each must pass EpisodeMeta::is_valid_timestamp() or is dropped silently; url is kept only when non-empty, image_id only when a positive int. A second 501 with a different message fires if normalisation drops every row. Returns {chapters: [...]} on success — the same row shape the chapter editor already uses.
format_row( array $row ): array — — Translates one stored chapter row ({timestamp, title, url?, image_id?}) into the Podcasting 2.0 schema shape ({startTime, title, url?, img?}). startTime comes from timestamp_to_seconds(); image_id is resolved to a URL via wp_get_attachment_url() and only included when that resolves to a non-empty string.
timestamp_to_seconds( string $value ): float — — Converts an HH:MM:SS or HH:MM:SS.mmm timestamp string to float seconds. Returns 0.0 on a malformed value rather than throwing — EpisodeMeta::set_chapters() already rejects malformed rows on write, so this is a defence-in-depth fallback, not the primary validation path. Public and static so callers processing chapter data independently (outside this controller) can reuse the same conversion.
client_etag_matches( WP_REST_Request $request, string $etag ): bool — — Compares the request's If-None-Match header against the computed ETag, treating a literal '*' as always matching. Returns false when the header is absent.

Hooks Fired

Notes

Extends the plugin's internal RestController base — not part of the public API; its own docblock tells add-on developers not to extend it, and to register independent routes on rest_api_init with the public benecaster_rest_permission_admin() function instead. Inherits register() (hooks register_routes() onto rest_api_init), the permission_callback_admin / permission_callback_editor / permission_callback_subscriber callbacks, rest_error(), and not_found().

benecaster_chapter_autosuggest is a filter this class applies to source suggested chapter rows from an add-on (Transcription Service or similar) — it is not something this class writes to post_meta itself.