Friday, January 20, 2006

Podcasting HowTo

As I mentioned in my last post, I added podcasting to our church web site. Today I thought I'd share how I did that for those interested in doing something similar.

To start with, I place all the sermon MP3 files into a separate directory. The MP3 files themselves adhere to the naming standard of:


Where the date is in YYMMDD format. So, this past weeks sermons file is named:

060115_John Keen_Mark 1;1-8.mp3

This makes it very easy add sermons to the list by just uploading the file. Note: the passage uses a ';' to delineate chapter and verse instead of a ':', this is because a ':' is not allowed in a filename on some systems (Windows). The code will correct that when displayed.

I started with a simple table to be shown on the web page. Shortly after that I thought an RSS feed of the contents would be handy so people could be motified of new sermons without visiting the web page. The RSS feed lead to the podcast feed, as they are nearly the same thing.

They all essentially do the same thing, so I'll skip right to the podcast version. However, I'll share one little tidbit from the web page version. From the table on the web page, you can either stream the MP3, or download the file. Streaming an MP3 file from your server doesn't take any special software. I wrote a simple PHP script which I name m3u.php which allows me to stream any MP3 file. The contents of the m3u.php looks like:

header("Content-Type: audio/x-mpegurl");
print '' . $_GET['filename'];

So, in my table code, invoke it as (in PHP):

<a href="sermons/m3u.php?filename=' . $filename . '">

To make the podcast feed, I started with Kai Blankenhorn's FeedCreator.class.php. It worked very well for the RSS feed, but it doesn't support the podcast extensions. So, I updated it to add the podcast and iTunes extensions to RSS. If you're interested, I'll be happy to provide my updated version. I'll summarize the changes in a separate post so I don't make this post too long.

Now, I'll walk through my podcast generating PHP class. First, I need a function to list all the files in my mp3 directory:

// Function list_dir will list all the files in a given directory
function list_dir($dirname)
static $result_array = array();
while ($file=readdir($handle)) {
// ignore the current and parent directory references ('.' and '..')
if (0 == strpos($file, '.')) {
closedir ($handle);
return $result_array;

I then set up the common header portion of the podcast feed:

$rss = new UniversalFeedCreator();
// if cache is less than 12 hours (43200 seconds) old, use it
// this is because the feed only changes once a week to begin with, and this
// script does a lot of ID3 tag manipulation which can take a while
$rss->useCached("PODCAST", "sermons/podcast.xml", 43200);
$rss->title = "Seminole PCA Sermons";
$rss->description = "Recent sermons from the pulpit of Seminole Presbyterian Church";
$rss->language = "en-us";
$rss->link = "";
$rss->webmaster = "";
$rss->docs = "";
$rss->syndicationURL = "".$PHP_SELF;
$rss->category = "Religion";

$image = new FeedImage();
$image->title = "Seminole PCA, Tampa, FL";
$image->url = "";
$image->link = "";
$image->description = "Feed provided by Click to visit.";
$image->width = 144;
$image->height = 204;
$rss->image = $image;

$itunes = new iTunes();
$itunes->category = "Religion & Spirituality";
$itunes->subcategory = "Christianity";
$itunes->subtitle = $rss->description;
$itunes->owner_email = "";
$itunes->owner_name = "Seminole Presbyterian Church";
$itunes->author = "Seminole Presbyterian Church";
$itunes->image = "";
$rss->itunes = $itunes;

Now, I loop through my list of mp3 files to build the contents of the podcast feed:

// PHP code to dynamically generate RSS feed
// Start of code -------
// get a list of the "mp3" directory
// reverse sort the array (puts newest sermon first)
rsort ($array);
reset ($array);

// for each file in the filelist, generate the appropriate HTML table code
foreach ($array as $filename) {
// break apart the filename into the 3 data fields; date, speaker,
// and subject using file naming convention:
// YYMMDD_Speaker_Passage.mp3
$tok = strtok($filename, "_");
$unixdate = mktime(0, 0, 0, substr($tok, 2, 2), substr($tok, 4, 2), substr($tok, 0, 2));
$sermondate=date("F d, Y", $unixdate);

$chapter = strtok($passage, ";");
$verse = strtok(".");
if ($verse !== false) {
$passage = $chapter . ':' . $verse;
} else {
$passage = $chapter;

at this point in my PHP code, I also use the getid3 PHP library to ensure that all the ID3 tags are set correctly in the mp3 file. This is not important for actually generating the podcast feed, so I'll omit it here.

$item = new FeedItem();
$item->title = $speaker . ' on ' . $passage;
$item->link = "" . $mp3dir . "/" . $filename;
$item->description = $sermondate . ' - ' . $speaker . ' expounds on ' . $passage;
$item->date = $unixdate;
$item->source = "";
$item->author = $speaker;
$item->enclosure = new Enclosure();
$item->enclosure->url = "" . $mp3dir . "/" . $filename;
$item->enclosure->length= $id3['filesize'];
$item->category[] = $itunes->category;

$item->itunes = new iTunes();
$item->itunes->category = $itunes->category;
$item->itunes->subcategory = $itunes->subcategory;
$item->itunes->keywords = "Seminole Presbyterian Church PCA Reformed Christian Sermon Tampa Florida FL";
$item->itunes->summary = $item->description;
$item->itunes->author = $item->author;
$item->itunes->duration = $id3['playtime_string'];
$item->itunes->image = "";
$rss->addItem( $item);

Finally, I save feed for caching purposes and return it:

$rss->saveFeed("PODCAST", "sermons/podcast.xml");

The resulting podcast XML can be seen here.

Once that's working, go register you feed at the various podcast directories such as:
iPassage (if you podcasting sermons)
Podcast Alley
iTunes (HowTo)

I published my feed in iPodder,, and Podcast Alley and was automatically propagated to other directories including the iTunes Store.

For reference, here is the official Apple iTunes/Podcasting specification.


Casey said...

This is great! We're thinking about implementing this on our Church website. Thanks for the helpful info!

Brian said...

Thanks for the great work on this. We are implementing it on

By the way, I enhanced your m3u.php so that when it prompts the user to open the file, it is properly named with the filename as opposed to showing the name m3u.php. Here is the code:

$filename = basename($_GET['filename']);
header('Content-Type: audio/x-mpegurl');
$ie = eregi("msie", $_SERVER['HTTP_USER_AGENT']);
if ($ie) $filename = rawurlencode($filename);

header("Content-Disposition: attachment; filename=\"$filename\"");
print '/directory/to/mp3s/' . $filename;