Playing a podcast over the phone
April 10th, 2020 (by Steve)
Coronavirus. An uncomfortable time for all of us, in different ways. But some great things are coming out of the crisis – the incredible sense of community and the way that people are serving others. We’ve tried to do our own little bit and part of that has involved helping to move a lot of our church communications online. Nearly a year ago now, I set up a system in our church to record sermons – pretty simple, a line out from the sound desk into a laptop with a program to record the audio. We then upload it to our church website. Simples! But what about those members of our congregation who don’t have access to a computer?
On 26 March I read a really interesting blog post about how you could use a service called Twilio to allow people to call a phone number and listen to an online audio file. I dropped an email to our vicar to ask whether there was a need for something similar in our church… and the response was positive; over 20 members of our congregation without access to email or the internet.
So, it was worthwhile doing, but I’m lazy and the method cited in the blog post involved changing an audio file every week. I don’t really want to be doing that – plus I don’t want to be a single point of failure in a system. Everything else with regards to our sermons is automated – we upload it once to a custom post type that I’ve created in WordPress, then we use IFTTT to detect when there’s a new sermon and post a link to the sermon to our Facebook and Twitter accounts. Then separately I’ve created an itunes-formatted RSS feed, at which I’ve pointed Apple Podcasts, Stitcher, PlayerFM and Spotify so people can be served the sermons from their favourite podcast platform. So let’s see if we can automate this phone call stuff.
The first thing I do when creating a new thing is to find out whether it actually is a new thing, so I check whether something exists out there already. This was the 27 March… it didn’t, so I got started. Using the blog post as a starting point, I got stuck into familiarising myself with Javascript (the language that the functions needed to be written in), and wrestling with some libraries to help me out. Oh, and there was also the thing of entertaining young children, working from home etc that meant I couldn’t dedicate much time to it. I finally finished on 6 April and it was all working.
And then I found out that someone had already done it and blogged about it on 3 April, with what I think is a neater solution (using the rss-parser library). The Switched On Network method enables you to point to any standalone MP3 file. Nick Holcombe’s method consumes an RSS feed, and gives you a menu of sermons to select from. My method is somewhere in the middle.
So, here’s my code in case anyone wants to see my alternative way (follow the instructions in the other blog posts to get Twilio up and running… the only extra bit you’ll need for mine is to add an npm dependency on the node-fetch library – version 2.6.0).
url = "https://www.stpaulstephenglos.org.uk/join-in/sermon-podcast/"; const fetch = require('node-fetch'); const DOMParser = require('xmldom').DOMParser; exports.handler = function(context, event, callback) { // create the voice response object let twiml = new Twilio.twiml.VoiceResponse(); // load the first item from the RSS feed (https://developers.google.com/web/updates/2015/03/introduction-to-fetch) fetch(url) .then( function(response) { if (response.status !== 200) { console.log('Looks like there was a problem. Status Code: ' + response.status); // end the call and hang up twiml.hangup(); callback(null, twiml); return; } // Examine the text in the response response.text().then(function(data) { // create a parser, give it the RSS feed and tell it to parse as XML parser = new DOMParser(); xmlfeed = parser.parseFromString(data,"text/xml"); // extract the data from within the channel const channel = xmlfeed.getElementsByTagName("channel")[0]; // get the title and description podcastTitle = channel.getElementsByTagName("title")[0].textContent; podcastDescription = channel.getElementsByTagName("description")[0].textContent; console.log(podcastTitle); console.log(podcastDescription); // Say the welcome text twiml.say('Welcome to ' + podcastTitle); twiml.say(podcastDescription); // get the first item const podcastItem1 = channel.getElementsByTagName("item")[0]; itemTitle = podcastItem1.getElementsByTagName("title")[0].textContent; itemDescription = podcastItem1.getElementsByTagName("itunes:subtitle")[0].textContent; itemDurationTotal = podcastItem1.getElementsByTagName("itunes:duration")[0].textContent; // use the double not bitwise operator to round to the minutes itemDurationMinutes = ~~(itemDurationTotal / 60); // use the modulus to get the seconds itemDurationSeconds = itemDurationTotal % 60; itemUrl = podcastItem1.getElementsByTagName("enclosure")[0].getAttribute("url"); console.log(itemTitle); console.log(itemDescription); console.log(itemDurationMinutes); console.log(itemDurationSeconds); // Introduce the item twiml.say('You are about to hear ' +itemTitle); twiml.say(itemDescription); twiml.say('This is ' +itemDurationMinutes+ ' minutes and ' +itemDurationSeconds+ ' seconds'); twiml.say('Please wait while we load the sound file'); // Play the item twiml.play(itemUrl); // end the call and hang up twiml.hangup(); callback(null, twiml); return; }); } ) .catch(function(err) { console.log('Fetch Error', err); // end the call and hang up twiml.hangup(); callback(null, twiml); }); };
Before I close, it’s probably worth a brief note on SOP and CORS (no, not SOP and The Corrs… although check them out!). This is all about permissions for scripts to access resources that are stored on other sites. The best description of it that I could find when trying to work out why my code wasn’t working was at javascript.info.
In essence, I needed to make a minor addition to the .htaccess file on the church website; we’re happy to allow GET requests from anywhere, so this is the code I added:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET"
Header set Access-Control-Allow-Headers "Content-Type"
But hang on, there are other services consuming the RSS feed (Apple Podcasts, Stitcher, Player.fm, Spotify)… how come these permissions are not needed for the other feeds? My educated guess (although please do correct me) is because this code uses javascript (which is a client-server interaction) whereas the other podcast feed code will be server-server. SOP and CORS only apply for client-server conversations.
If you fancy using my code, it should work for you by just changing the URL to be the URL of your podcast RSS feed. If your podcast is available on ApplePodcasts or Soundcloud and you don’t know your RSS feed URL, then you can use the handy getrssfeed.com tool to find it.
Am I little irked that I could have saved myself some time an effort if I’d kept on searching to see if anyone had done this? Yes. Am I pleased that I managed to get something working on my own? Yes. Is the code meeting a need? Yes. And actually, that’s the main thing.
Stay safe.
Posted in Life, Web Design | 3 Comments »