WP Manifestindependent plugin directory
manifest / ai / talking-head

Talking Head WP Registry grade B. Medium-severity findings · 2 findingsWP RegistryMedium-severity findings · 2 findingsOpen the reportB

Talking Head lets you write multi-speaker conversations in the WordPress block editor, then generate podcast-quality audio using AI text-to-speech. Each speaker ("head") gets their own voice, and the plugin stitches the segments together into a single audio file with configurable silence gaps.

★ 2stars
68release downloads
1.5.0latest · 6mo ago

Install

Download talking-head.zip From the 1.5.0 release on GitHub, Apr 20, 2026

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/soderlind/talking-head/releases/download/1.5.0/talking-head.zip
Updates itself. It ships its own updater (Plugin Update Checker), so new versions appear under Dashboard → Updates.

AI-generated podcast-style audio from turn-based conversations in WordPress.

No point watching without audio

Description

Talking Head lets you write multi-speaker conversations in the WordPress block editor, then generate podcast-quality audio using AI text-to-speech. Each speaker ("head") gets their own voice, and the plugin stitches the segments together into a single audio file with configurable silence gaps — or serves segments individually using virtual stitching for faster publishing.

Features

  • Episode editor — Gutenberg blocks for writing turn-based conversations
  • Speaker profiles — Custom post type for managing voices and personas
  • OpenAI TTS — Generate speech using OpenAI's text-to-speech API (alloy, echo, fable, onyx, nova, shimmer)
  • Azure OpenAI TTS — Alternative provider using Azure-hosted OpenAI deployments
  • WordPress AI (Core) — On WordPress 7.0+, use the built-in AI Client for TTS via Settings → Connectors (no API key required)
  • Background processing — Audio generation runs via Action Scheduler, with progress tracking
  • Audio stitching — FFmpeg-based concatenation with silence gaps and loudness normalization, or pure PHP fallback
  • Virtual stitching — Serve audio segments individually without server-side concatenation, with client-side sequential playback
  • Player block — Embed episode playback in any post or page, with optional transcript
  • Provider selector — Settings page dropdown to switch between providers; only relevant fields are shown
  • Provider interface — Extensible architecture for adding more TTS providers

Requirements

  • WordPress 6.8+
  • PHP 8.3+
  • (Although optional, FFmpeg installed on the server allows the user to download the conversation; PHP fallback is available.)

Installation

  1. Download the latest talking-head.zip.
  2. In WordPress, go to Plugins → Add New → Upload Plugin and upload the zip.
  3. Activate the plugin.

The plugin updates itself automatically via GitHub releases using plugin-update-checker.

Configuration

Go to Talking Head > Settings and configure. The settings page has three tabs:

TabSettingDescription
ProviderTTS ProviderOpenAI, Azure OpenAI, or WordPress AI (Core) (WP 7.0+)
Default VoiceDefault voice for new speaker profiles
OpenAI API KeyYour OpenAI API key for TTS
TTS Modeltts-1 (standard), tts-1-hd (high quality), or gpt-4o-mini-tts (supports instructions)
Azure OpenAI API KeyYour Azure OpenAI API key
Azure OpenAI EndpointAzure resource endpoint URL
Azure OpenAI Deployment IDName of your TTS deployment
Azure OpenAI API VersionAPI version string
AudioStitching ModeFile (concatenate on server) or Virtual (serve segments individually)
FFmpeg PathAbsolute path to the FFmpeg binary (optional — PHP fallback if not found)
Output FormatMP3 or AAC
Output Bitrate128k / 192k / 256k / 320k
Silence GapMilliseconds of silence between turns
LimitsMax SegmentsMaximum turns per episode (1–200)
Max CharactersMaximum text length per turn (100–4096)
Rate LimitAPI requests per minute (1–60)

Settings can also be set via constants in wp-config.php (highest priority) or environment variables. See CONFIG.md for the full list of 16 constants.

Usage

1. Create Speaker Profiles

Go to Talking Head > Heads and create speaker profiles. Each head has:

  • A name
  • A voice ID (e.g., nova, onyx)
  • A provider (openai, azure_openai, or wordpress on WP 7.0+)
  • Speed (0.25–4.0, default 1.0)
  • Optional speaking style/instructions (used with gpt-4o-mini-tts)
  • Optional avatar (featured image)

2. Write an Episode

Go to Talking Head > Add New Episode. The editor loads with an Episode container block and one Turn block. For each turn:

  • Select a speaker from the dropdown
  • Write the dialogue text

Add more turns with the block appender.

3. Generate Audio

Select the Episode block and click Generate Audio in the block toolbar. The plugin:

  1. Validates the manuscript (speakers assigned, text within limits)
  2. Creates a background job via Action Scheduler
  3. Generates TTS audio for each turn via the configured provider
  4. Stitches segments with FFmpeg into a single MP3 (file mode), or prepares segments for individual playback (virtual mode)
  5. Stores the result in wp-content/uploads/talking-head/

Progress is shown in the sidebar via polling.

4. Embed the Player

Use the Talking Head Player block in any post or page. Select an episode from the searchable dropdown and optionally enable transcript display. The block renders a native <audio> element.

Development

See DEVELOPER.md for build commands, REST API, architecture, and workflow details.

License

GPL-2.0-or-later

Changelog

See CHANGELOG.md.

Releases

15 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
1.5.0 latest
Apr 20, 2026 6mo ago
talking-head.zip
45
Apr 20, 2026 6mo ago
talking-head.zip
0
Mar 30, 2026 6mo ago
talking-head.zip
0
Feb 18, 2026 8mo ago
talking-head.zip
2
Feb 18, 2026 8mo ago
talking-head.zip
3
Feb 18, 2026 8mo ago
talking-head.zip
1
Feb 18, 2026 8mo ago
talking-head.zip
1
Feb 18, 2026 8mo ago
talking-head.zip
1
Feb 18, 2026 8mo ago
talking-head.zip
2
Feb 17, 2026 8mo ago
talking-head.zip
3
Feb 17, 2026 8mo ago
talking-head.zip
1
Feb 17, 2026 8mo ago
talking-head.zip
1
Feb 17, 2026 8mo ago
talking-head.zip
3
Feb 17, 2026 8mo ago
talking-head.zip
3
Feb 17, 2026 8mo ago
talking-head.zip
2
B grade

Security

Talking Head 1.5.0 · audited by WP Registry

Medium-severity findings.

2 medium
Audited release
1.5.0
Findings
2
Worst severity
medium
Content hash
376b085e15a74e98a0965b07…

Findings

  • medium Job and manuscript REST routes lack per-episode authorization

    missing_capability

    Every talking-head/v1 jobs and manuscript route uses permission_callback fn() => current_user_can('edit_posts') and never checks current_user_can('edit_post', $episode_id) (or ownership of the job's episode). WordPress maps that cap to Contributor and above, while the episode CPT uses default capability_type 'post', so Contributors cannot edit other users' drafts via /wp/v2/th-episodes. The custom routes ignore that. Observable effects for a Contributor with a REST nonce: (1) GET /talking-head/v1/episodes/{id}/manuscript returns the canonical manuscript (full unpublished dialogue) for any talking_head_episode ID; (2) POST /episodes/{id}/manuscript/validate rebuilds and update_post_meta's _th_manuscript and _th_episode_status=ready on that post; (3) POST /jobs with another author's episodeId calls JobScheduler::schedule, which writes generating status, inserts a wp_th_jobs row, and enqueues talking_head_process_job — burning the site-wide WordPress AI connector and potentially overwriting _th_audio_url; (4) GET/cancel/retry /jobs/{id} operates on any job, including canceling in-flight generation (virtual-mode public players hide audio while status is generating). Subscriber and unauthenticated callers are blocked. HeadMetaBox correctly uses edit_post, which makes the REST gap inconsistent rather than by-design collaboration.

    src/REST/JobController.php:25-122

    Recommendation

    Change permission_callback to require current_user_can('edit_post', $episode_id) after resolving the episode (for /jobs POST and manuscript routes). For /jobs/{id} GET/cancel/retry, load the job, then require edit_post on job['episode_id']. Return 403 when the caller cannot edit that episode. Optionally restrict job creation to publish_posts if TTS spend should be editor-only.

  • medium Public player REST discloses password-protected episode transcripts and audio URLs

    information_disclosure

    GET /talking-head/v1/episodes/{id}/player is registered with permission_callback __return_true. get_player_data only 404s when the post is missing, not talking_head_episode, or post_status !== 'publish'. Password-protected posts remain status=publish with post_password set; there is no post_password_required() or current_user_can('read_post') gate. An unauthenticated client that can enumerate sequential episode IDs receives JSON with title, audioUrl (_th_audio_url), duration, stitchingMode, and a transcript built from _th_manuscript segments (headName + text). Core /wp/v2/th-episodes would hide rendered content behind the password; this custom route is a side channel. The same publish-only check is the only confidentiality control — drafts and private posts are correctly excluded. Audio files under wp-content/uploads/talking-head/ are also directly fetchable (inherent to HTML5 audio), but the REST body additionally leaks the transcript text without the wp-postpass cookie.

    src/REST/PlayerController.php:25-70

    Recommendation

    After loading the episode, require current_user_can('read_post', $episode_id) or reject when post_password_required($post). For password-protected episodes, return 401/403 unless the request presents a valid post-password cookie. Keep __return_true only for truly public, non-password published episodes.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.