diff --git a/exec/load/podcast_routines.js b/exec/load/podcast_routines.js
new file mode 100644
index 0000000000000000000000000000000000000000..9bbe8586559918f819f70e559d72bbb32ba41c59
--- /dev/null
+++ b/exec/load/podcast_routines.js
@@ -0,0 +1,50 @@
+if (js.global.MSG_DELETE == undefined)
+	js.global.load("sbbsdefs.js");
+
+function podcast_load_headers(base, from, to)
+{
+	var hdrs = [];
+	var i;
+	var hdr;
+
+	for (i = base.first_msg; i <= base.last_msg; i++) {
+		hdr = base.get_msg_header(i);
+		if (hdr == null)
+			continue;
+		if (hdr.attr & MSG_DELETE)
+			continue;
+		if (hdr.attr & MSG_MODERATED) {
+			if (!(hdr.attr & MSG_VALIDATED))
+				break;
+		}
+		if (hdr.thread_back != 0)
+			continue;
+		if (hdr.from.toLowerCase() != from.toLowerCase() || hdr.to.toLowerCase() != to.toLowerCase())
+			continue;
+		if (hdr.from_net_type != NET_NONE)
+			continue;
+		hdrs.push(hdr);
+	}
+	return hdrs;
+}
+
+function podcast_get_info(base, hdr)
+{
+	var body;
+	var m;
+	var ret={};
+
+	body = base.get_msg_body(hdr.number);
+	if (body == null)
+		return;
+	body = word_wrap(body, 65535, 79, false).replace(/\r/g, '');
+	m = body.match(/^[\r\n\s]*([\x00-\xff]+?)[\r\n\s]+(https?:\/\/[^\r\n\s]+)[\r\n\s]*$/);
+	if (m==null)
+		return;
+	ret.title = hdr.subject;
+	ret.description = m[1];
+	ret.enclosure = m[2];
+	ret.guid = hdr.id;
+	ret.pubDate = (new Date(hdr.when_written_time * 1000)).toUTCString();
+	return ret;
+}
diff --git a/exec/podcast.js b/exec/podcast.js
index 8a56160b39db1adec0bd095a395148a802cddf6a..b1b2165cfa2642beb5004034764fae51064cf17a 100644
--- a/exec/podcast.js
+++ b/exec/podcast.js
@@ -1,7 +1,7 @@
-if (js.global.MSG_DELETE == undefined)
-	js.global.load("sbbsdefs.js");
 if (js.global.HTTP == undefined)
 	js.global.load("http.js");
+if (js.podcast_load_headers == undefined)
+	js.global.load("podcast_routines.js");
 
 var opts = load({}, "modopts.js", "Podcast");
 var base;
@@ -19,6 +19,7 @@ var http;
 var item_headers;
 var item_length;
 var item_type;
+var item_info;
 
 function encode_xml(str)
 {
@@ -99,25 +100,7 @@ if (!out.open("web")) {
 	exit(1);
 }
 
-hdrs = [];
-for (i = base.first_msg; i <= base.last_msg; i++) {
-	hdr = base.get_msg_header(i);
-	if (hdr == null)
-		continue;
-	if (hdr.attr & MSG_DELETE)
-		continue;
-	if (hdr.attr & MSG_MODERATED) {
-		if (!(hdr.attr & MSG_VALIDATED))
-			break;
-	}
-	if (hdr.thread_back != 0)
-		continue;
-	if (hdr.from.toLowerCase() != opts.From.toLowerCase() || hdr.to.toLowerCase() != opts.To.toLowerCase())
-		continue;
-	if (hdr.from_net_type != NET_NONE)
-		continue;
-	hdrs.push(hdr);
-}
+hdrs = podcast_load_headers(base, opts.From, opts.To);
 
 // TODO: iTunes tags?
 out.write('<?xml version="1.0"?>\n');
@@ -157,15 +140,11 @@ add_channel_opt_attribute('SkipDays');
 out.write('\t\t<atom:link href="'+opts.FeedURI+'" rel="self" type="application/rss+xml" />\n');
 
 for (i=hdrs.length - 1; i >= 0; i--) {
-	body = base.get_msg_body(hdrs[i].number);
-	if (body == null)
-		continue;
-	body = word_wrap(body, 65535, 79, false).replace(/\r/g, '');
-	m = body.match(/^[\r\n\s]*([\x00-\xff]+?)[\r\n\s]+(https?:\/\/[^\r\n\s]+)[\r\n\s]*$/);
-	if (m==null)
+	item_info=podcast_get_info(base, hdrs[i]);
+	if (item_info == undefined)
 		continue;
 	http = new HTTPRequest();
-	item_headers = http.Head(m[2]);
+	item_headers = http.Head(item_info.enclosure);
 	if (item_headers == undefined)
 		continue;
 	if (item_headers['Content-Type'] == undefined || item_headers['Content-Length'] == undefined) {
@@ -175,16 +154,16 @@ for (i=hdrs.length - 1; i >= 0; i--) {
 	item_length = item_headers['Content-Length'][0] + 0;
 	item_type = item_headers['Content-Type'][0].replace(/^\s*(.*?)\s*/, "$1");
 	item = '\t\t<item>\n';
-	item += '\t\t\t<title>'+encode_xml(hdrs[i].subject)+'</title>\n';
+	item += '\t\t\t<title>'+encode_xml(item_info.title)+'</title>\n';
 	// TODO: HTML integration required here for link
 	item += '\t\t\t<link>'+encode_xml(opts.Link)+'</link>\n';
-	item += '\t\t\t<description>'+encode_xml(m[1])+'</description>\n';
+	item += '\t\t\t<description>'+encode_xml(item_info.description)+'</description>\n';
 	// TODO: author?
 	// TODO: category?
 	// TODO: HTML integration required here for <comments> tag.
-	item += '\t\t\t<enclosure url="'+encode_xml(m[2])+'" length="'+item_length+'" type="'+item_type+'" />\n';
-	item += '\t\t\t<guid isPermaLink="false">'+encode_xml(hdrs[i].id)+'</guid>\n';
-	item += '\t\t\t<pubDate>'+encode_xml((new Date(hdrs[i].when_written_time * 1000)).toUTCString())+'</pubDate>\n';
+	item += '\t\t\t<enclosure url="'+encode_xml(item_info.enclosure)+'" length="'+item_length+'" type="'+item_type+'" />\n';
+	item += '\t\t\t<guid isPermaLink="false">'+encode_xml(item_info.guid)+'</guid>\n';
+	item += '\t\t\t<pubDate>'+encode_xml(item_info.pubDate)+'</pubDate>\n';
 	// TODO: source?
 	item += '\t\t</item>\n';
 	out.write(item);