benecaster_podcast2_transcript_autodetect
Fills an episode’s Podcasting 2.0 Transcript URL from somewhere other than the media library. Core’s own auto-detection scans the episode’s attached media for a single .vtt, .srt or .txt file and writes the one it finds; this filter is the seam for everything else — a transcription service, an S3 bucket, a naming convention on your own CDN.
Runs on every episode save, but only when the Transcript URL field is empty, so a URL you return can never overwrite one the podcaster typed — you don’t need to check that yourself. $url arrives as core’s own detected URL, or null when core found nothing usable: no transcript-shaped attachment, or more than one, which core deliberately refuses to guess between. You can’t tell those two cases apart from here; read the attachments yourself if you need to.
Returning a non-empty string also suppresses the “several transcript files are attached” admin notice, since the field no longer needs a human to fill it in. Return null (or '') to leave the field alone — that’s how you switch core detection off entirely.
The stored MIME type is then inferred from the returned URL’s extension (.vtt → text/vtt, .srt → application/srt, .json → application/json, .txt → text/plain, .html → text/html). An extension not in that list leaves the stored type untouched rather than forcing a wrong one.
Fires from Benecaster\Episode\TranscriptAutodetector, called at the end of EpisodeMetaSaver::save() — every save path routes through there, so one callback covers the React editor’s REST save, the classic editor, and your own save() calls. ⚠ It does not fire on a save that never reaches the saver — a bare wp_update_post() with no Benecaster meta in play writes nothing and detects nothing.
Supply a transcript URL from your own service
Core’s Podcasting 2.0 auto-detection scans an episode’s attached media for a single .vtt, .srt or .txt file and writes the one it finds to the Transcript URL field. This filter is the seam for everything else — a transcription service, an S3 bucket, a naming convention on your own CDN.
It runs only when the Transcript URL field is empty, so a URL you return can never overwrite one the podcaster typed — you don’t need to check that yourself.
$url is core’s detected URL, or null when core found nothing usable — either no transcript-shaped attachment, or more than one, which core deliberately refuses to guess between. You cannot tell those two apart from here; if you need to, read the attachments yourself.
Returning a non-empty string also suppresses the “several transcript files are attached” admin notice, because the field no longer needs a human to fill it. Return null (or '') to leave the field alone — that is how you switch core detection off.
The stored type is inferred from the URL’s extension (.vtt → text/vtt, .srt → application/srt, .json → application/json, .txt → text/plain, .html → text/html). An extension not in that list leaves the stored type untouched rather than forcing a wrong one — so a URL with no extension, or one behind a signing endpoint, keeps whatever the podcaster chose in the dropdown.
It fires inside EpisodeMetaSaver::save(), which every save path routes through, so one callback covers the React editor’s REST save, the classic editor and your own save() calls. ⚠ It does not fire on a save that never reaches the saver — a bare wp_update_post() with no Benecaster meta in play writes nothing and detects nothing.
To disable core detection entirely — for instance because your own admin screen owns the field:
add_filter( 'benecaster_podcast2_transcript_autodetect', '__return_null', 99 );
<?php
add_filter(
'benecaster_podcast2_transcript_autodetect',
function ( ?string $url, int $episode_id ): ?string {
// Let core's media-library detection win when it found something.
if ( null !== $url ) {
return $url;
}
$external_id = get_post_meta( $episode_id, '_my_service_transcript_id', true );
if ( '' === $external_id ) {
return null; // Nothing to offer — leave the field empty.
}
// Keep the extension: it is what sets the transcript type.
return 'https://transcripts.example.com/' . rawurlencode( (string) $external_id ) . '.vtt';
},
10,
2
);
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
$url |
?string |
— | Core's detected transcript URL, or null when core found nothing usable. |
$episode_id |
int |
— | ID of the episode being saved. |
Returns:
?string
Examples
Supply a transcript URL from your own service
add_filter(
'benecaster_podcast2_transcript_autodetect',
function ( ?string $url, int $episode_id ): ?string {
// Let core's media-library detection win when it found something.
if ( null !== $url ) {
return $url;
}
$external_id = get_post_meta( $episode_id, '_my_service_transcript_id', true );
if ( '' === $external_id ) {
return null; // Nothing to offer — leave the field empty.
}
// Keep the extension: it is what sets the transcript type.
return 'https://transcripts.example.com/' . rawurlencode( (string) $external_id ) . '.vtt';
},
10,
2
);
Disable core detection entirely
add_filter( 'benecaster_podcast2_transcript_autodetect', '__return_null', 99 );
Need this built rather than just documented? See our services →