Skip to main content

benecaster_feed_podcast_episode_tags

Filter Free

Filters the per-episode Podcasting 2.0 tag array before XML output. Pre-populated from episode meta. Add-ons receive the populated array and may add, override, or remove entries. All keys are optional.

Key Type Podcast 2.0 tag Populated by
transcript {url: string, type: string, language?: string, rel?: string}, or a list of them podcast:transcript, one element per entry Benecaster, from the episode’s transcript URL (left out while the add-on that owns that URL is not loaded); Transcription Service add-on
soundbites list<{start_time: float, duration: float, title?: string}> podcast:soundbite —
feed_credits list<{name: string, role?: string, url?: string, image?: string}> podcast:person Guest Manager add-on
images list<{href: string, alt?: string, width: int, height: int, type: string}> podcast:image, one element per entry —
season {number: int, name?: string} podcast:season —
chapters {url: string, type?: string} podcast:chapters Benecaster (Chapter Markers)

images is a list, and an add-on assigning a single srcset string emits nothing. The Podcast Index spec retired <podcast:images srcset="…"> in favour of repeated <podcast:image> elements, so the key is a list and each entry becomes its own element. A string in this key is silently dropped — there is no error and no fallback, the artwork simply does not appear in the feed. Entries without an href are skipped individually.

Benecaster populates it with one entry per registered WordPress image size, largest width first, deduplicated by URL. alt comes from the attachment’s Alternative Text and is omitted entirely when blank rather than emitted as alt="", because an empty alt asserts the image is decorative. If you append entries, follow the same rule.

transcript can be a list. Each entry becomes its own <podcast:transcript>, in order, so an episode can carry a text/html transcript beside an SRT or VTT one. type is one of text/plain, text/html, application/srt, text/vtt or application/json, and rel is the spec’s captions. Benecaster fills in a single entry from the episode’s own transcript URL. To add one, turn it into a list rather than overwriting it: isset( $tags['transcript']['url'] ) ? [ $tags['transcript'] ] : …. Entries without a url are skipped, and an exact repeat of an earlier URL and type is emitted once.

$context: who the feed is being built for

The fourth argument lets you withhold a tag from one listener and not another, for example a transcript sold as a buy-up. Register your callback with four accepted arguments to receive it; a three-argument callback keeps working unchanged.

Key Type Meaning
tier_slug string The slug of the copy being built: a tier, public, or a buy-up or grant slug.
listener_slugs list of strings Every slug the listener’s feed is built from: their tier (or public), then each buy-up and standalone grant they hold.
anonymous bool true when the request carried no valid feed token: the public feed, and an invalid or revoked token sent there. false for every token holder, followers included.

Decide on listener_slugs, never on tier_slug. A listener with a tier and a buy-up is served one copy per slug, merged, and the merge keeps the tier’s copy of each episode. A rule asking “is this the buy-up’s own feed?” takes the tag away from every holder for every episode their tier also carries.

It fails closed. When no listener is known, listener_slugs is just [ tier_slug ] and anonymous is true, so a withhold unless held rule withholds. Never read a short list as “no restriction”.

For the audio variant a listener hears, call benecaster_get_audio_variant_slug( $episode_id, $context['tier_slug'] ). For a buy-up ID kept in your settings, benecaster_get_buyup_slug() gives the slug listener_slugs carries. Benecaster itself withholds nothing: transcript visibility is the Transcription add-on’s setting, or your own filter (see the recipe Make the transcript a buy-up perk).

Decide only from the episode, the show and $context, never the current user, the request or the clock. The feed cache is keyed by exactly those, so anything else is cached and served to the next listener. A rule kept in episode or show meta clears the show’s feed cache when it changes; a rule kept anywhere else must call do_action( 'benecaster_clear_feed_cache', $show_id ) when it changes. Keys may be added to $context later; none will be removed or change meaning.

Parameters

Name Type Default Description
$tags array — Per-episode Podcasting 2.0 tag array. See description for key reference. All keys optional.
$episode_id int — Episode post ID
$show_id int — Show post ID
$context array — Who the feed is being built for: tier_slug, listener_slugs and anonymous. See the description.

Returns: array<string, mixed>

Example

add_filter( 'benecaster_feed_podcast_episode_tags', function( $tags, $episode_id, $show_id ) {
    // Inject a soundbite from post meta (start/duration stored as JSON).
    $soundbite = get_post_meta( $episode_id, '_episode_soundbite', true );
    if ( $soundbite ) {
        $sb = json_decode( $soundbite, true );
        $tags['soundbites'][] = [
            'start_time' => (float) $sb['start'],
            'duration'   => (float) $sb['duration'],
            'title'      => $sb['title'] ?? '',
        ];
    }
    return $tags;
}, 10, 3 );

Need this built rather than just documented? See our services →