benecaster_bulk_import_completed
Fires once at every end of a run that fired benecaster_bulk_import_started: the feed could not be fetched (an HTTP error or non-200 response), the response was not RSS, or every item was processed (complete) — the same rule as benecaster_feed_sync_completed. Check $progress[‘status’] before treating it as success. On complete, the show’s Source feed URL has already been saved (when the show had none), and every benecaster_episode_imported for the run has already fired. Does not fire if the run dies mid-way (a PHP fatal inside a draft insert), because there is no end state to report.
Import a back catalogue from an add-on
Benecaster’s importer (Feed\FeedBulkImporter) is a stable API. An add-on that guides a podcaster through migration should call it, not copy it. It then gets deduplication by GUID, benecaster_episode_imported, the imported-drafts banner, and a show set up for Feed Sync afterwards, all for free.
Don’t save the feed URL into your own option for syncing later. A completed import already saves it as the show’s Source feed URL (Show Settings → Feed Sync) when the show has none, and the podcaster turns sync on there. A private copy would drift from the setting the podcaster can see.
In the browser, POST / GET /shows/{id}/import does the same as start_import() / get_progress(), with the same keys.
<?php
add_action( 'benecaster_boot', function ( \Benecaster\Container $container ): void {
$importer = $container->make( \Benecaster\Feed\FeedBulkImporter::class );
// Your own admin action. Validate before starting: start_import() does not.
add_action( 'admin_post_my_addon_import', function () use ( $importer ): void {
check_admin_referer( 'my_addon_import' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Not allowed.' );
}
$show_id = absint( $_POST['show_id'] ?? 0 );
$feed_url = esc_url_raw( wp_unslash( $_POST['feed_url'] ?? '' ) );
if ( 'benecaster_show' !== get_post_type( $show_id ) || ! filter_var( $feed_url, FILTER_VALIDATE_URL ) ) {
wp_die( 'Choose a show and a feed URL.' );
}
// One job per show at a time: there is no lock.
$active = $importer->get_active_job_id( $show_id );
if ( '' !== $active && 'running' === ( $importer->get_progress( $active )['status'] ?? '' ) ) {
wp_die( 'An import is already running for this show.' );
}
$importer->start_import( $show_id, $feed_url );
wp_safe_redirect( admin_url( 'admin.php?page=my-addon&import=started' ) );
exit;
} );
} );
// The reconciliation report: fires for failures too.
add_action( 'benecaster_bulk_import_completed', function ( int $show_id, array $progress ): void {
update_option( "my_addon_import_report_{$show_id}", [
'status' => $progress['status'],
'imported' => $progress['imported'],
'skipped' => $progress['skipped'],
'errors' => $progress['errors'],
'error' => $progress['error_msg'] ?? '',
], false );
}, 10, 2 );
Email the podcaster when an episode import finishes
Tell the site owner how a one-time episode import went, since it can still be running after they leave the setup wizard. The import runs in the background and a large back catalogue takes minutes, so the podcaster has often clicked Continue before it ends. This sends a summary either way — success or failure — from benecaster_bulk_import_completed.
Always branch on $progress['status']. The hook fires for failures too, and error_msg exists only on a failed run. A callback that assumes success emails “0 of 0 episodes imported” when the URL was wrong.
For a notification per episode rather than per run, use benecaster_episode_imported. For Feed Sync runs, use benecaster_feed_sync_completed; this hook never fires for them.
<?php
add_action( 'benecaster_bulk_import_completed', function ( int $show_id, array $progress, string $job_id, string $feed_url ): void {
$show = get_the_title( $show_id );
if ( 'failed' === $progress['status'] ) {
wp_mail(
get_option( 'admin_email' ),
sprintf( 'Episode import for %s failed', $show ),
sprintf( "The import from %s stopped: %s", $feed_url, $progress['error_msg'] ?? 'unknown error' )
);
return;
}
wp_mail(
get_option( 'admin_email' ),
sprintf( 'Episode import for %s finished', $show ),
sprintf(
"%d of %d episodes were imported as drafts (%d were already there, %d could not be created).\nReview them in Benecaster → Episodes.",
$progress['imported'],
$progress['total'],
$progress['skipped'],
$progress['errors']
)
);
}, 10, 4 );
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
$show_id |
int |
— | ID of the show imported into |
$progress |
array |
— | The job's final progress, the same array FeedBulkImporter::get_progress() returns: total (int), imported (int), skipped (int) (GUID already on the show), errors (int) (draft could not be created), status (string) — 'complete' or 'failed', and error_msg (string) only when status is 'failed' |
$job_id |
string |
— | The opaque job ID FeedBulkImporter::start_import() returned |
$feed_url |
string |
— | The feed that was imported |
Need this built rather than just documented? See our services →