April 2, 2007 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:

  • subtitle
  • speaker (a drop-down menu, pre-populated with our pastors’ names)
  • speaker_alt (a text field for the speaker name if it’s not listed in the drop-down)
  • campus
  • servicetime (a drop-down menu)
  • servicetime_alt (in case of an alternate service time)
  • summary
  • fileid
  • graphic
  • notes
  • keywords
  • scriptures

spacer

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.

spacer

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 class="{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 class="/listen/{entry_id}">
  7. <li class="download"><a class="/download.php?file={fileid}.mp3">Download</a></li>
  8. <li class="buy"><form name="post" method="post" action="www.ewebcart.com/cgi-bin/cart.pl">
  9. <!— shopping cart tags here //—>
  10. <a class="#"><input type="image" src="/img/spacer.gif">
  11. </form></li>
  12. {if notes}<li class="notes"><a class="/pdf/{notes}">Notes</a></li>{/if}
  13. <li class="comments"><a class="{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:

  • For the listen link a JavaScript is used to open a new window and call a single-entry template (/message/{entry_id}). The single-entry template is basically just a few EE variables and an embedded MP3.
  • The download link utilizes a PHP script that forces the users’ browser to download the file ({fileid}.mp3) instead of load it.
  • The buy link is just a submit button inside a form with a bunch of ecommerce tags that pass the message data onto our third-party shopping cart app.
  • The notes link only appears if the {notes} field has a value.
  • The comments link uses a bunch of conditionals to either display the number of comments or “Add comment”.

spacer When 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 class="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.


Comments

Sounds like a great setup, automation’s brilliant once you get over the initial creation and definately pays dividends in the long run. :)

One thing I noticed though; your use of inline javascript for the pop-up window:
<code</code>

Could you not separate this from the markup and instead use the listen class, that’s already defined, as a hook to add the behaviour? I know the link will still function with javascript disabled, but this might make changing the pop-up a little easier down the line. That is, assuming the title and dimensions of the new window are consistent with each listing.

Thanks for the write-up. ExpressionEngine is definately one of the most flexible CMSs and the City Church site is a wonderful example of what it’s capable of.

Sam
April 2, 2007

Yes, Sam, absolutely. I’m glad you brought that up!

It’s definitely a goal to separate the JavaScript call from the markup — my use of the inline code is not recommended. Since I’m not a JavaScript guy, and haven’t made it a priority to find a premade solution, I’ve just left it.

If you have a suggestion for a better solution, please tell!

Sean
April 2, 2007

I’m afraid I’m no JavaScript guru, but the best method for applying behaviour to classes would be Dustin Diaz’s getElementsByClass function. There are quite a few out there, but his seems to be the most efficient.

The only thing to watch out for is if you’re wanting to support IE5. The script uses an asterisk as a catch-all if a tagName isn’t specified. As long as the listen class is always applied to a list item you can just include li as the parent tag. Then it should work fine in IE5 and won’t have to loop through the entire DOM.

Your code would be something like:

function poplinks() { var listen = getElementsByClass('listen',document,'li'); listen.childNodes[0].onclick = window.open(this.href,'Listen','400','150','no'); return false; } window.onload = poplinks;
</pre>

Please don’t take my word for it though, my JavaScript knowledge is pretty much non-existent. That’s just a crude example of what you’d need. I’m sure you know many developers who could help you out with something similar but functional :P

Sam
April 3, 2007

Looks like it’s eaten the code, but you get the idea.

Sam
April 3, 2007

Sean –

Good stuff. I’m currently developing a site for a church that will need similar functionality. Unfortunately for me, it’s in Rails, so I can just swipe your code!

Noah Stokes
April 3, 2007

Awesome, Sam, thanks. I’ll look into implementing that and post results later.

(Looks like the Textile EE plugin doesn’t process code blocks that well or something.)

Sean
April 3, 2007

Noah, what about swipe and swap? Change out the EE tags for whatever CMS you’re using … ?

Sean
April 3, 2007

Sean
Fantastic! I have been meaning to email you and ask how you do this very thing! Thanks much for detailing this process. Any time you want to give more details on how to power a site using EE, I’ll be here learning!

Jesse
April 5, 2007

Glad I found this blog. I’m working a site for my church, and will give ee a look. I was working with joomla, but am still hoping to find something more flexible. And thanks for providing such a detailed explanation of your process. It’s given me some new ideas to think about.

Greg
April 7, 2007

Sean
Thanks for the blog. I’m still learning EE from the ground up, but it’s beginning to come together finally. This info has definitely helped.

Rob
February 5, 2008

Thanks so much for explaining this. I am starting a website for my church, but I was a little torn with what cms to go with, but after much research and tutorials, EE is the way to go!
Thanks once again for the article!

Jared
February 8, 2008

Im wondering how I could get my hands on the css used within the City Church website? :)

Nathan Brown
February 10, 2008

Sean,
Forgive if this seems like a dumb question but is there enough given on this to actually re-create a similiar tool to categorize, download sermons? I created all the custom fields and set preferences, replicated your template code and I can’t even get anything to show. I’ve created a weblog, and used two methods – one using “{ exp:weblog:entries weblog=” (space added after left bracket) and the other “{ embed=” calling the code as an embed (this one I copied, pasted and slightly altered the code to my particlular naming.
Our church is being charged an arm and a leg due to high bandwith from sermon downloads on our dedicated server. If I can learn how to do this & host the sermons elsewhere for much less then funds can go to much more worthy causes.
Thank you for your reply.

Pete
February 20, 2008

is there enough given on this to actually re-create a similiar tool to categorize, download sermons?

For the most part, yes. You should submit your actual code and setup to the EE forums. They’re awesome, and very responsive.

Sean
February 21, 2008

Hmm, great site Sean, and great tutorial as well! I was wondering though, do you upload the mp3 files to the server via the publish tab or do you do it via ftp and then take a note of the file id/name and enter that into the weblog field?

I’m thinking about how to setup an audio archive of my own and the main hurdle I can see is the file upload bit. I was hoping to avoid ftp since I want non-technical staff to be able to upload their own stuff, but mp3 files can be large and hitting ‘browse’ and waiting for ages doesn’t sound user-friendly either.

Adam
April 27, 2008

Hi. I was wondering if you could address how you tackle streaming as well.

Jon Thomas
June 11, 2008

Commenting is not available in this weblog entry.
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.