How to Roll Your Own Simple WordPress Podcast Plugin

Avatar of Geoff Graham
Geoff Graham on (Updated on )

Let’s say you plan to get into podcasting. You have the recording equipment, an interesting topic, and a good voice that other people want to hear. You seem well on your way to filling earbuds everywhere.

Then there’s the issue of hosting your podcast. iTunes requires an RSS feed so it can distribute your episodes to subscribers when they’re available, but how do you do that? The good news is that there are plenty of ways to host a podcast. For example, you could use a hosting service that provides you storage and distribution in one tidy place, usually for a fee. If you use WordPress and have looked into podcasting, you may have seen all the powerful podcasting plugins that are available there as well.

We’re going to skip over those options in this post and see how we can host our own podcast using nothing more than a WordPress site and two additional files. Specifically, we’re going to:

  • Set up a new plugin
  • Register a custom RSS feed
  • Make a custom post type for our podcast
  • Assign custom fields to our custom post type
  • Create the template for our RSS feed

The goal is not to overthink this too much. If you already have a WordPress site and all you need is a feed to submit to iTunes, then that’s exactly what we’re going to do. Let’s rock ‘n’ roll.

Setting Up the Plugin

Creating a plugin is a nice alternative to writing code in your theme’s `functions.php` file. In addition to keeping your theme’s functions clean and clutter-free, it keeps the podcast feed functionality in your `/wp-content/plugins/` directory which stays with you even if you change themes.

Create the Plugin Directory

First things first. Let’s create a new folder called `my-awesome-podcast` in our `/wp-content/plugins/` directory.

Create the Plugin

Now let’s make a new PHP file in the folder we just created and call it `my-awesome-podcast.php`. This will be the primary file that registers our plugin and where we write the functionality for our podcast.

With that file in place, we will add the following code comments that tell WordPress the name, description and version of the plugin. There are other bits of information we can add, but this will do.

/**
Plugin Name: My Awesome Podcast
Description: A really simple RSS feed and custom post type for a podcast
Version: 1.0
**/

// This is where we will start writing our plugin

Register the RSS Feed

Next up, we need to register a new RSS feed. Thankfully, the WordPress API has a handy add_feed() function that we can hook into to make this relatively easy.

add_action('init', 'podcast_rss');
function podcast_rss(){
  add_feed('my-awesome-podcast', 'my_podcast_rss');
}

What’s happening here is we’ve defined a new function called podcast_rss() and are extending the WordPress add_feed() function to create a new RSS feed called “my_podcast_rss” that will live at `/feed/my-awesome-podcast` on our site.

Note: You can change “my-awesome-podcast” here to anything you want. This is the URL slug of your feed, so it could be the title of your podcast or whatever else you fancy.

Register a Podcast Custom Post Type

Once we have our feed established, we’ll need to set up a new post type that we can use to publish the posts for our episodes. So instead of a “Post” or “Page” that are default to WordPress, we’ll create a brand new one called “Podcast”. The WordPress Codex does a good job of explaining how to create custom post types, so there’s no need to rehash that here. There are even a handful of plugins that will do this, if that’s how you prefer to roll. In any case, let’s assume we’re doing it without the help of a plugin and make the Podcast custom post type here in our file.

function custom_post_type() {

  $labels = array(
    'name'                => _x( 'Podcasts', 'Podcast General Name', 'text_domain' ),
    'singular_name'       => _x( 'Podcast', 'Course Singular Name', 'text_domain' ),
    'menu_name'           => __( 'Podcasts', 'text_domain' ),
    'parent_item_colon'   => __( 'Parent Podcast:', 'text_domain' ),
    'all_items'           => __( 'All Podcasts', 'text_domain' ),
    'view_item'           => __( 'View Podcast', 'text_domain' ),
    'add_new_item'        => __( 'Add New Podcast', 'text_domain' ),
    'add_new'             => __( 'Add New', 'text_domain' ),
    'edit_item'           => __( 'Edit Podcast', 'text_domain' ),
    'update_item'         => __( 'Update Podcast', 'text_domain' ),
    'search_items'        => __( 'Search Podcasts', 'text_domain' ),
    'not_found'           => __( 'Not found', 'text_domain' ),
    'not_found_in_trash'  => __( 'Not found in Trash', 'text_domain' ),
  );
  $args = array(
    'label'               => __( 'podcasts', 'text_domain' ),
    'description'         => __( 'Podcast Description', 'text_domain' ),
    'labels'              => $labels,
    'supports'            => array( 'title', 'editor', 'thumbnail' ),
    'taxonomies'          => array( 'category', 'post_tag' ),
    'hierarchical'        => false,
    'public'              => true,
    'show_ui'             => true,
    'show_in_menu'        => true,
    'show_in_nav_menus'   => true,
    'show_in_admin_bar'   => true,
    'menu_position'       => 5,
    'menu_icon'           => 'dashicons-format-audio',
    'can_export'          => true,
    'has_archive'         => true,
    'exclude_from_search' => false,
    'publicly_queryable'  => true,
    'capability_type'     => 'page'
  );
  register_post_type( 'podcasts', $args );
}

add_action( 'init', 'custom_post_type', 0 );

This is a lot of code, but it’s just configuration to register a new post type called Podcasts. It defines the various labels for it in the WordPress dashboard, and tells WordPress how to handle it. Again, the WordPress Codex explains these things much more exhaustively, if you’re interested.

At this point in time, we can head into the Plugins section of our WordPress admin and see My Awesome Podcast listed as an available plugin. Go ahead and activate it.

Create Custom Fields for Podcast Posts

WordPress posts do a lot for us right out of the box, but sometimes we need additional fields that allow us to publish specific content. In this case, iTunes has a very specific format for what kind of information needs to be available in our RSS feed.

There is a way to add custom fields to our custom post type with extra development, but I actually prefer using the Advanced Custom Fields plugin for this sort of thing. It allows us to have a nice clean custom UI for each field we add, rather than the generic key/value pair UI you get with default custom fields.

Using Advanced Custom Fields, let’s set up a new field group called Podcasts, assign them to the Podcast post type and create the following fields:

  • podcast_file: Use the “File” field type so we can upload our podcast file directly to the post. Set the field to Return Value to “File ID” because this will help us auto-detect the file size later.
  • podcast_duration: This is a simple text field that we’ll use to publish the length of our episode in HH:MM:SS format.

Note: While I used the “File” field type in this example, you could theoretically use a text field if you plan on hosting your podcast files somewhere other than your server (say Amazon S3 or something like that), and paste the URL directly in there. However, we won’t be able to auto-detect the file size this way, though we could use another text field for that purpose.

Create the RSS Template

We’ve done a lot so far. The last thing to do is create the template that will be used to display the data for our podcast posts.

Apple has outlined the specs for an acceptable podcast RSS feed and it’s very specific. Using that outline as a guide, let’s create one more file in our plugin folder and call it `podcast-rss-template.php`. The name says it all, right?

The nice thing is that our RSS template is technically no different from any other WordPress template. We will query our posts, create a loop, and plug our data in where it needs to go.

/** 
Template Name: Podcast RSS
**/

// Query the Podcast Custom Post Type and fetch the latest 100 posts
$args = array( 'post_type' => 'podcasts', 'posts_per_page' => 100 );
$loop = new WP_Query( $args );

/**
 * Get the current URL taking into account HTTPS and Port
 * @link https://css-tricks.com/snippets/php/get-current-page-url/
 * @version Refactored by @AlexParraSilva
 */
function getCurrentUrl() {
    $url  = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
    $url .= '://' . $_SERVER['SERVER_NAME'];
    $url .= in_array( $_SERVER['SERVER_PORT'], array('80', '443') ) ? '' : ':' . $_SERVER['SERVER_PORT'];
    $url .= $_SERVER['REQUEST_URI'];
    return $url;
}

// Output the XML header
header('Content-Type: '.feed_content_type('rss-http').'; charset='.get_option('blog_charset'), true);
echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>';
?>

<?php // Start the iTunes RSS Feed: https://www.apple.com/itunes/podcasts/specs.html ?>
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
  
  <?php 
    // The information for the podcast channel 
    // Mostly using get_bloginfo() here, but these can be custom tailored, as needed
  ?>
  <channel>
    <title><?php echo get_bloginfo('name'); ?></title>
    <link><?php echo get_bloginfo('url'); ?></link>
    <language><?php echo get_bloginfo ( 'language' ); ?></language>
    <copyright><?php echo date('Y'); ?> <?php echo get_bloginfo('name'); ?></copyright>
    
    <itunes:author><?php echo get_bloginfo('name'); ?></itunes:author>
    <itunes:summary><?php echo get_bloginfo('description'); ?></itunes:summary>
    <description><?php echo get_bloginfo('url'); ?></description>
    
    <itunes:owner>
      <itunes:name><?php echo get_bloginfo('name'); ?></itunes:name>
      <itunes:email><?php echo get_bloginfo('admin_email'); ?></itunes:email>
    </itunes:owner>
    
    <?php // Change to your own image. Must be at least 1400 x 1400: https://www.apple.com/itunes/podcasts/creatorfaq.html
    <itunes:image href="http://your-site.com/path/to/podcast/image.png" />
    
    <itunes:category text="Technology">
      <itunes:category text="Tech News"/>
    </itunes:category>
    <itunes:explicit>yes</itunes:explicit>
    
    <?php // Start the loop for Podcast posts
    while ( $loop->have_posts() ) : $loop->the_post(); ?>
    <item>
      <title><?php the_title_rss(); ?></title>
      <itunes:author><?php echo get_bloginfo('name'); ?></itunes:author>
      <itunes:summary><?php echo get_the_excerpt(); ?></itunes:summary>
      <?php // Retrieve just the URL of the Featured Image: http://codex.wordpress.org/Function_Reference/wp_get_attachment_image_src
      if (has_post_thumbnail( $post->ID ) ): ?>
        <?php $image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' ); ?>
        <itunes:image href="<?php echo $image[0]; ?>" />
      <?php endif; ?>
      
      <?php // Get the file field URL, filesize and date format
        $attachment_id = get_field('podcast_file');
        $fileurl = wp_get_attachment_url( $attachment_id );
        $filesize = filesize( get_attached_file( $attachment_id ) );
        $dateformatstring = _x( 'D, d M Y H:i:s O', 'Date formating for iTunes feed.' );
      ?>
      
      <enclosure url="<?php echo $fileurl; ?>" length="<?php echo $filesize; ?>" type="audio/mpeg" />
      <guid><?php echo $fileurl; ?></guid>
      <guid><?php the_field('podcast_file'); ?></guid>
      <pubDate><?php echo date($dateformatstring, $show_date); ?></pubDate>
      <itunes:duration><?php the_field('podcast_duration'); ?></itunes:duration>
    </item>
    <?php endwhile; ?>
  
  </channel>

</rss>

Note: the code above assumes you’re uploading the MP3 file to your own site/server. When that’s the case, it can get the file size in bytes, which iTunes requires as part of the RSS feed, which is super handy. However, if you’re hosting the MP3 file elsewhere (can be smart, most web hosts aren’t built for serving large assets), this won’t work. Instead, add an additional field with ACF for the byte size and output it where we’re outputting $filesize above.

Now let’s call this RSS template in our `my-awesome.podcast.php` file by adding the following:

function my_podcast_rss(){
  require_once( dirname( __FILE__ ) . '/podcast-rss-template.php' );
}

Putting it All Together

Let’s recap how everything fits together.

The Files

In short, we now have a new folder in our plugin directory called `my-awesome-podcast`.

WordPress Root Directory
└── wp-content
    └── plugins
        └── my-awesome-podcast
            ├── my-awesome-podcast.php
            └── podcast-rss-template.php
  • my-awesome.podcast.php: This registers our plugin, creates a custom RSS feed, defines a new custom post type and calls our RSS template.
  • podcast-rss-template.php: This contains the template for our RSS feed, providing the information from our Podcasts posts in a format that iTunes can read and use to deliver our content to subscribers.

Publishing a Podcast

Go ahead and publish a new Podcast post. The option will be in your WordPress menu under Podcasts.

  • Give the podcast a title
  • Upload the podcast episode file
  • Specify the episode length (in HH:MM:SS format)
  • (if not hosting locally) Specify the episode file size (in bytes)
  • Provide a summary of the episode in the content area

Awesome, we just published your first podcast episode!

Submit the Podcast to iTunes

Finally, go visit the URL of the feed at: `[your-site]/feed/[slug]`, where `[slug]` is what we defined in the add_feed() function back in the second step. It wouldn’t hurt to validate the feed just to make sure it’s healthy and happy. This is the URL you will submit to iTunes, which you can do from the iTunes app under iTunes Store > Podcasts > Quick Links > Submit a Podcast.

If you’re getting a Page Not Found (404), try flushing your permalinks. This can be done by heading to Settings > Permalinks from your WordPress dashboard and doing nothing more than clicking the Save Settings button. Strange, but true.

Go Forth and Podcast

There you go! A simple, but effective way to host your podcast using resources you probably already have lying around. Sure, there may be more powerful or even more elegant solutions, but this gives us exactly what we need with nothing more and nothing less.

Update: A huge shout-out to David Scanu. He wrote in with some updates to that correct a few RSS validation issues, which are now reflected in the article. Thanks a ton!