Skip to main content

benecaster_bulk_import_completed

Action Free

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

Free Intermediate

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 );

View on GitHub →

Email the podcaster when an episode import finishes

Free Beginner

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 );

View on GitHub →

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 →