Skip to main content

benecaster_episode_audio_url

Filter Free

Filters the audio file URL wherever Benecaster resolves one. It fires in four places.

The front-end player and episode page. $tier_slug is null — there is no tier in scope here. Every call passes all three arguments, the website passing an explicit null, so a callback declaring three parameters never throws ArgumentCountError. null means the website; a string means a feed or a tracked download. [benecaster_player] makes the same call. Core registers its own callback at priority 10 (Episode\PreviewClipPlayer): on a locked web render it swaps in the episode’s preview clip, and it passes every call carrying a tier slug through untouched. A callback registered at priority > 10 runs after it and wins.

Each feed item, immediately before benecaster_feed_enclosure_url, carrying the tier slug of the feed being compiled.

Each channel-level <podcast:trailer>, carrying that trailer episode’s ID — so a channel tag advertising an episode’s media cannot bypass a rewrite the item respects.

A tracked download’s redirect target (Analytics\DownloadProxy, bugfix/download-proxy-audio-url-filter). When download tracking is on and the app follows a token feed’s /benecaster-download/{id}/?token=… enclosure, the proxy runs this filter on the 302 target with three arguments. $tier_slug is the tier the media was resolved at: the token’s snapshot tier, or public for a follower — always a string, so a download is feed context and PreviewClipPlayer’s clip swap never fires here. For a video episode the URL is the video file, matching the feed. The call lands after the per-tier variant cascade and before two things: the show’s Enclosure URL prefix, which wraps whatever your callback returns, and the download-log write — return '' to refuse the download, which gives the app a 404 and writes no benecaster_download_log row, the same as a missing file. benecaster_feed_enclosure_url does not run on this redirect: core’s ProxyUrlRewriter is on that filter and would hand back the proxy’s own URL, looping the download.

Use this when you want the same rewrite everywhere — player, feed, and tracked downloads alike. Use benecaster_feed_enclosure_url instead when you want to change only what goes in the feed, and leave the on-site player untouched.

Let visitors who can't hear an episode play a preview clip

Free Beginner

No code needed for the common case. In the episode editor, open Media and paste the clip’s URL into Preview clip URL — a trailer or the first few minutes, hosted wherever the episode is. On the website, anyone who can’t hear the episode then gets a player with the clip where the locked message was: on the episode page, on its archive card, and in [benecaster_player]. Subscribers who can hear it get the full file. The clip is never put in a feed. Benecaster does this with its own callbacks on both filters at priority 10 (Episode\PreviewClipPlayer). The episode needs its main audio URL: one with only an embed or a video keeps its locked message.

This code example is for the advanced case: a clip you don’t store, e.g. one your host cuts on request. Benecaster opens the player only for episodes with a stored clip, so a custom clip source needs both filters. Whatever you open, you must swap — a player opened by benecaster_show_episode_player plays the URL benecaster_episode_audio_url hands it, so opening the player without the swap gives a locked visitor the whole episode. $tier_slug is null when the web player makes the call and a string in a feed or tracked download.

<?php
// Open the player for locked visitors, but only where a clip exists and the
// episode has a main audio URL. Without one, the player template falls back
// to the episode's embed or video, and that would be the full episode.
add_filter( 'benecaster_show_episode_player', function ( bool $show, int $episode_id ): bool {
    if ( $show ) {
        return true;
    }
    $audio = (string) get_post_meta( $episode_id, '_benecaster_audio_url', true );
    return '' !== $audio && '' !== my_host_preview_url( $episode_id );
}, 20, 2 );

// Swap in the clip for anyone who can't hear the episode, on the web only.
add_filter( 'benecaster_episode_audio_url', function (
    string  $url,
    int     $episode_id,
    ?string $tier_slug = null
): string {
    if ( null !== $tier_slug ) {
        return $url;
    }
    $show_id = (int) get_post_field( 'post_parent', $episode_id );
    if ( benecaster_user_can_access_episode( $episode_id, $show_id ) ) {
        return $url;
    }
    $clip = my_host_preview_url( $episode_id );
    return '' !== $clip ? $clip : $url;
}, 20, 3 );

View on GitHub →

Swap the audio enclosure URL per subscriber tier at feed compile time

Premium Intermediate

Benecaster picks the audio URL for a given subscriber tier in three steps: it looks for a variant matching that tier exactly, then cascades down to broader tiers, and finally falls back to the episode’s base URL.

The benecaster_episode_audio_url filter runs after that choice is made, so an add-on can override the result per request — geo-routing, per-listener CDN tokens, dynamic ad insertion, and similar.

Give the parameter a ?string $tier_slug = null default — the website always passes an explicit null there, and null is how you tell a player call apart from a feed or tracked-download call.

The filter fires once per episode/tier pair while a feed compiles, and again on a tracked download’s redirect target (Analytics\DownloadProxy) — there $tier_slug is always a string (the token’s snapshot tier, or public for a follower), so this same callback also reaches downloads without any extra wiring. Whatever your callback returns in that context becomes the redirect target, not the enclosure URL verbatim — it still passes through the show’s Enclosure URL prefix afterward, and download tracking logs the request only once the filter returns a non-empty string. A signed CDN URL used here should carry a short TTL: a tracked download is a one-time redirect, not a cached feed entry, so there’s no benefit to a long-lived signature.

<?php
add_filter(
    'benecaster_episode_audio_url',
    function ( string $url, int $episode_id, ?string $tier_slug = null ): string {
        // Web player call — no tier in scope, leave it alone.
        if ( null === $tier_slug ) {
            return $url;
        }
        // Feed and tracked-download calls: route paid tiers through a signed-URL CDN;
        // leave the public/free audio alone.
        if ( in_array( $tier_slug, [ 'public', 'free' ], true ) ) {
            return $url;
        }
        return my_addon_sign_cdn_url( $url, [ 'ttl' => 900 ] );
    },
    10,
    3
);

View on GitHub →

Parameters

Name Type Default Description
$url string The audio URL
$episode_id int ID of the episode
$tier_slug string|null The tier being served in a feed or tracked download; null on the website (the player, archive cards, [benecaster_player])

Returns: string

Example

add_filter( 'benecaster_episode_audio_url', function( $url, $episode_id, $tier_slug = '' ) {
    // Serve audio through a CDN subdomain, on the player and in the feed.
    return str_replace( 'https://files.example.com/', 'https://cdn.example.com/', $url );
}, 10, 3 );

Affects

  • Episode page player

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