Overcoming a podcast app-bug using Cloudflare Workers
27 Jul 2022
I listen to a lot of podcasts, mostly when doing chores or exercising, to overcome the boredom that these actions can bring. I mostly listen to podcasts on RadioPublic, which I chose a few years back. Due to sunk cost, I have not been able to bring myself to look for an alternative, despite RadioPublic seeming to not be in active development anymore, having been updated last around a year ago.
RadioPublic allows the user to listen to any podcasts listed in their application, but also to listen to any podcast which provides an RSS-feed. This functionality is, somewhat unintuitively, provided by simply pasting an RSS-feed URL in the search bar, at which point the podcast provided by the RSS-feed will show up as a search result. However - there’s a bug in this functionality.
The bug
As mentioned in the previous section, the “Add podcast by RSS-URL”-feature is somewhat unintuitive. It also has a bug - it only seems to actually respond to URLs that have a file extension, with this file extension being either .xml
or .rss
. Note that I haven’t actually decompiled the application to verify that this is what it does - I merely deduced this by observation. Should the URL not have a file extension ending with .xml
or .rss
, the URL will simply be interpreted as a search query in the search bar, which sometimes coincidentally shows the right result, but not necessarily.
This became an issue for me when I wanted to listen to a podcast hosted by Libsyn - a podcast host which does not provide their RSS-URLs with a file extension - they merely provide a link ending in /rss
,
There could be some discussion on which program is incorrect here, but I am going to argue that it’s definitely RadioPublic not doing the right thing here - file extensions are not my preferred method of conveying file contents. I think that a more correct approach would be to inspect the Content-Type
- header of the HTTP response of the URL, and use that as the basis for whether the URL is a podcast-feed or not. Some quick CURLing to the podcast URLs from Libsyn shows that it does indeed have the correct Content-Type
-set on their HTTP responses, when requesting this URL.
Potential ways to cope with the issue
I was a bit frustrated, as I had found a very interesting podcast episode to listen to, that was hosted on Libsyn. The way I saw it, I had a few ways to go about it:
I could listen to the podcast episode in their web-based player
I did not like this method, as I potentially wanted to be able to download the episode and track my listening progress, and potentially listen to more episodes from this podcast.
I could download the MP3-file containing the podcast episode, and listen to it in another media player
I didn’t find this method appealing either, as I would probably not be able to track my listening progress in a good manner. I could maybe have used an audiobook-app to listen to the podcast-episode, but this did feel a bit like accepting defeat.
I could switch podcast-apps altogether
I must admit that I found this option a bit tempting. However, having tracked all my listening progress in RadioPublic, and being very fluent with the UI of RadioPublic, I had a bit of cost sunk into continuing to use RadioPublic. I might switch some day, though, given the apparent inactivity of RadioPublics development team.
I could make a server that simply served the links from Libsyn, but appended .xml
to the end of the URL
This seemed like a fun way to tackle the problem, and a good opportunity to practice my Rust skills. I initially went with this approach, but after a while I kind of got bored with the prospect of having to actually host this server, and acquire a TLS certificate for it. It just simply seemed to be too much effort for such a small workaround.
I could do the exact same thing as previously mentioned, but use a serverless solution instead
Finally, a solution that seemed just reasonable enough to work. I had read about Cloudflare Workers before - that they have a generous free tier, and that they support Rust out of the box. Combine this with me already using Cloudflare for this blog, and it seemed like a really good match.
Getting up to speed with Cloudflare Worker development
Getting all of the tooling in place for developing Cloudflare Workers was surprisingly easy.
First, I installed the CLI-tool wrangler
, to facilitate development:
$ cargo install wrangler
Then, I created a project from a template:
$ wrangler generate libsyn-mirror-cloudflare-worker --type="rust" \
--template="https://github.com/cloudflare/rustwasm-worker-template"
Wrangler lets you run your worker locally for development in a very simple manner:
$ cd libsyn-mirror-cloudflare-worker
$ wrangler dev
Which then lets you shoot requests to the worker as if it were hosted, but on your local machine, on http://localhost:8787
.
The code itself
Cloudflare workers in Rust use a small router, on which you can register URL patterns that get responded to by calls to a provided function.
My idea was as follows:
- Gather the Libsyn podcast ID from the URL as a path parameter
- Using the ID, construct the URL to the Libsyn-RSS feed (this can be derived using only the ID)
- Respond to the request with a 301 redirect to the Libsyn-RSS feed
The implementation was extremely simple:
// ...
router.
get("/:podcast_id/rss.xml", |_req, ctx| {
// Get the podcast ID from the URL parameter
let podcast_id = ctx.param("podcast_id");
// Return Bad Request if it was not provided
if podcast_id.is_none() {
return Respons::error("Missing parameter `podcast_id`", 400);
}
// Safe to do, as we checked that it's not none before
let podcast_id = podcast_id.unwrap();
// Construct the URL
let url = Url::parse(&format!("https://sites.libsyn.com/{}/rss", podcast_id));
// Return Internal Server Error if URL parsing fails
if url.is_err() {
return Response::error("Could not parse redirect URL", 500);
}
// Safe to do, as we checked that it's not err before
let url = url.unwrap();
// Implicit return - we return a redirect with status 301: Moved Permanently
Response::redirect_with_status(url, 301)
})
// ...
The comments should provide a good level of understanding of what the code does, even for readers who are not very-well versed in Rust.
Deploying and testing it out
Deploying Cloudflare workers is also very simple, using wrangler
:
$ wrangler publish
I was now provided with a URL to the worker, and could test it out in RadioPublic. And lo and behold, it worked - I could now load the podcast I wanted to listen to. I could finally declare victory, and rest easy, having circumvented the bug.
Conclusions
Cloudflare workers seem like a really good option for certain tasks. They are highly performant, and very simple to get up to speed with. They will probably be high up on the list of tools I choose for future personal projects. Rust continues to be a very pleasant language I wish I would be able to use more professionally.