Geek & Mild by Sean Sperte

Powering an Audio Archive With ExpressionEngine

One of the most often-asked questions I receive is about how The City Church’s audio archive is powered. It’s easier than you’d expect, but does require a bit of explanation. So, as promised, here’s a look under the hood.

Custom fields and weblog preferences

ExpressionEngine’s weblogs, or “databases” as I’ve labeled them, are much more flexible than a typical two- or three-field weblog. They can have custom fields, and have dozens of settings. I’ve assigned our Audio Archive database a custom field group which, beyond “title” and “URL title”, has an additional 13 fields. They include:

Screenshot of the audio archive custom field group

Each custom field comes with preference settings for type, formatting, hiding/showing, and instructions. Setting these preferences, I’m able to simplify the publish form as much as possible, but still give some advanced options to the publisher.

In addition to the custom fields, each entry (or “message” in this case) also has date, category, and status assignment(s). The nice thing about using a custom field for the service time is I don’t have to rely on our audio engineers (who do all the archive maintenance) to manually enter the service time in the entry date field – they simply click on the date itself. Also, since entries can have multiple categories assigned to them, a message can appear as a “Sunday Messages” and “Guest Speakers” entry simultaneously.

Speaking of categories, I’ve also set up a custom category group which has almost 30 categories (and sub-categories) for message assignment. They cover areas of ministry as well as special events. A few examples would be: “Sunday Messages”, “City Kids”, “Generation Church”, and “Men’s Ministry.”

The Audio Archive database also has some unique preferences that set it apart as a non-textual-based weblog. For instance, I’ve set the default status to be “closed” so that an entry can be created without it being immediately published and live. This enables an audio engineer to create the entry during a service while the message is being preached, but not have it available until he/she confirms the details of, say, the title or summary.

I’ve also set the database to: 1) disallow trackbacks (and auto-discovery code) in entries; 2) notify me via email of each entry published; 3) not show formatting buttons, author menu or forum topic on the publish page.

Setting up the templates

The fun stuff is in the templates. That’s where the magic happens. When displayed on a webpage, each message entry has links to listen, download, buy, etc.

Screenshot of a styled message entry

Here’s an example of a typical archive listing:

  1. {exp:weblog:entries weblog="audioarchive" limit="5"}
  2. <div class="message">
  3. <p><strong><a href="{url_title_path=message}">{title}{if subtitle} ({subtitle}){/if}</a></strong><br />
  4. <span>{if speaker != "other"}{speaker}{if:else}{speaker_alt}{/if}</span> {entry_date format="%M %j, %Y"} &bull; {if servicetime != "other"}{servicetime}{if:else}{servicetime_alt}{/if} &bull; {campus}</p>
  5. <ul>
  6. <li class="listen"><a href="/listen/{entry_id}" onclick="NewWindow(this.href,'Listen','400','150','no');return false">Listen</a></li>
  7. <li class="download"><a href="/download.php?file={fileid}.mp3">Download</a></li>
  8. <li class="buy"><form name="post" method="post" action="http://www.ewebcart.com/cgi-bin/cart.pl">
  9. <!– shopping cart tags here //–>
  10. <a href="#"><input type="image" src="http://www.thecity.org/images/spacer.gif" width="16" height="16" name="add" /></a>
  11. </form></li>
  12. {if notes}<li class="notes"><a href="/pdf/{notes}">Notes</a></li>{/if}
  13. <li class="comments"><a href="{url_title_path=message}">{if comment_total >= '2'}{comment_total} Comments{if:elseif comment_total == '1'}{comment_total} Comment{if:else}Add Comment{/if}</a></li>
  14. </ul>
  15. </div>
  16. {/exp:weblog:entries}

This code-block is found on the index page of the audio sub-section. Breaking it down, you can see I’ve wrapped each message in its own div with the class message. Inside the div I use a paragraph for {title} and some meta-info about the message. Notice the use of conditionals for {subtitle}, speaker and servicetime. If, for instance, the publisher gave the message a subtitle, it will appear in parenthesis after the title; if not, nothing will show.

Then (on line 5) comes the unordered list – with links to listen, download, buy, view notes or comment. The markup is pretty simple:

Screenshot of an unstyled message entryWhen unstyled, this code-block is relatively plain and [arguably] semantic. The only point of contention comes from the use of a transparent GIF for the form submit button. In hindsight I suppose I could’ve just used input type="image", but I was preoccupied in trying to utilize CSS for all images.

Of course, ExpressionEngine has built-in support for XML templates, so serving a podcast is just as simple:

  1. {exp:weblog:entries weblog="audioarchive" category="4" orderby="date" sticky="off" limit="10" rdf="off"}
  2. <item>
  3. <title>{exp:xml_encode}{title}{/exp:xml_encode}{if subtitle} ({exp:xml_encode}{subtitle}{/exp:xml_encode}){/if}</title>
  4. <link>{url_title_path=message}</link>
  5. <guid isPermaLink="false">{fileid}</guid>
  6. <description><![CDATA[{exp:markdown}{if summary}{summary}{if:else}Speaker: {if speaker_alt}{speaker_alt}{if:else}{speaker}{/if} - This message was preached on {entry_date format="%F %j%S"} at the {servicetime} service, at the {campus} Campus. No additional summary available.{/if}{/exp:markdown}]]></description>
  7. <dc:date>{entry_date format="%Y-%m-%d"}</dc:date>
  8. <!– itunes-specific item tags –>
  9. <itunes:author>{if speaker_alt !=''}{speaker_alt}{/if}{if speaker_alt ==''}{speaker}{/if}</itunes:author>
  10. <itunes:subtitle>{if summary}{summary}{if:else}Speaker: {if speaker_alt}{speaker_alt}{if:else}{speaker}{/if} - This message was preached on {entry_date format="%F %j%S"} at the {servicetime} service, at the {campus} Campus. No additional summary available.{/if}</itunes:subtitle>
  11. <itunes:explicit>no</itunes:explicit>
  12. {if keywords}<itunes:keywords>{keywords}</itunes:keywords>{/if}
  13. <itunes:summary>{if summary}{summary}{if:else}Speaker: {if speaker_alt}{speaker_alt}{if:else}{speaker}{/if} - This message was preached on {entry_date format="%F %j%S"} at the {servicetime} service, at the {campus} Campus. No additional summary available.{/if}</itunes:summary>
  14. <!– end itunes-specific tags –>
  15. {exp:feed_enclosures}<a href="http://www.thecity.org/audioarchive/{fileid}.mp3" rel="enclosure">{title}</a>{/exp:feed_enclosures}
  16. </item>
  17. {/exp:weblog:entries}

… Okay, so it doesn’t look so simple – but with proper code formatting and highlighting it becomes much easier to understand. I won’t go into detail about every line, but I will point out a few key elements.

First, notice the use of <![CDATA[ ... ]]> for the {summary} field. I use it because the field is set with no formatting, allowing me to format on the template-side – in this case, using the Markdown plugin.

Secondly, you can see I’m using a lot of iTunes-specific code. I suppose it goes without saying, but a podcast without these tags will have an uphill battle getting exposure on the iTunes Podcast Directory.

Finally, notice the actual file link is wrapped in {exp:feed_enclosures}. This tag performs some wizardry and somehow quantifies the file’s bit-size and serves a correctly formatted enclosure element. At least, I think that’s what it does.

Maintaining the archives

So with this infrastructure in place, maintenance of the audio archive has been effortless for me. The only thing I need to do is quality control; making sure message details are correct, no weird symbols are used, and the website stays online. I’m completely out of the loop during the process, which (I’ve been told) goes something like this:

  1. The message is recorded to a master CD during the service
  2. The master CD is then ripped and (quickly) edited for content and equalization
  3. The audio file is compressed and encoded as an MP3 (64k kbps, 22.050kHz, mono)
  4. The file is uploaded to the server
  5. A new entry is created in the audio archive EE database

I’m yawning just thinking about how little I have to do to keep it all going. ExpressionEngine rocks.