Using the RSS plugin
To start customizing our project, we need to install and use the RSS plugin. The information we’ll need from this plugin is the same as that we’d need to have a traditional RSS feed for a blog as well. To get started, we need to install and configure the plugin.
Installing the plugin
The RSS plugin is an official 11ty plugin and full documentation can be found on the 11ty documentation site. To install the plugin, we need to run an npm install
command for the npm package:
npm install @11ty/eleventy-plugin-rss
This will save the plugin to our project and update our package.json
file. Next, we need to add this to our 11ty configuration file. Just as in the previous projects, the configuration is in the .eleventy.js
file at the root of the project.
To configure the plugin, we set a new variable at the top of the configuration file and then use the addPlugin
method inside the exported function of the file:
const pluginRss = require("@11ty/eleventy-plugin-rss"); module.exports = function(eleventyConfig) { eleventyConfig.addPlugin(pluginRss); // Copy `assets/` to `_site/assets/` eleventyConfig.addPassthroughCopy("assets"); // Set the source for 11ty to the /src directory // Otherwise, this defaults to the project root // This provides a cleaner project structure return { dir: { input: "src", output: "_site", // This is the default, but it's included here for clarity. includes: "_templates" } } }
Now that the plugin is installed, we can start using the filters to begin to solve the needs of our podcast feed.
Setting up a feed page
In our src
directory, we need to create a new page dedicated to the RSS feed. The RSS plugin is created for use in the Nunjucks template engine. Even though the rest of our site is being rendered by Liquid, this specific template can be rendered with Nunjucks by giving it the proper file extension: feed.njk
.
Let’s start by adding the very basics of an RSS feed to this file. This code starts by defining that this is an XML document and is using XML version 1.0 and RSS version 2.0 – the correct versions for acceptance in iTunes:
<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <title>{{ site.name }}</title> <link href="{{ site.url }}{{ permalink }}" rel="self" /> <link href="{{ site.url }}/" /> </rss>
After saving this, a new directory will appear in our _site
directory and a new route will be available in the localhost. The problem with this is that instead of creating an XML file and route, 11ty has built a file whose content is XML but whose file extension is HTML. This will create issues with most podcast apps, and even Google Chrome displays it as a blank HTML page instead of as data from XML.
This is because 11ty treats everything by default as HTML. We can override this by changing the permalink setting by changing the property in the file’s frontmatter. This will change the file placement and extension to an XML document instead of HTML. After saving this file, you can open up localhost:8080/feed.xml
and Chrome will now recognize this as a feed:
--- permalink: '/feed.xml' --- <?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <title>{{ site.name }}</title> <link href="{{ site.url }}{{ permalink }}" rel="self" /> <link href="{{ site.url }}/" /> </rss>
Once this new page is saved, we can navigate to http://localhost:8080/feed.xml
and see the output directly:
Figure 7.1 – The very simple RSS output displayed in Chrome
Now that we have a properly formatted page, we can start adding all the information needed for the general information for the feed.
To start, we’ll add the general information found in the global site data with specific tags for both RSS and iTunes. We’ll add tags for the ID, language, author information, description, categories, and image. The main thing to note here is that the description needs to be inside a CDATA field to be valid XML:
--- permalink: '/feed.xml' --- <?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <title>{{ site.name }}</title> <link href="{{ site.url }}{{ permalink }}" rel="self" /> <link href="{{ site.url }}/" /> <id>{{ site.url }}</id> <language>en-US</language> <itunes:author>{{ site.authorName }}</itunes:author> <description><![CDATA[{{ site.description }}]]> </description> <itunes:owner> <itunes:name>{{ site.authorName }}</itunes:name> <itunes:email>{{ site.authorEmail }}</itunes:email> </itunes:owner> <itunes:explicit>no</itunes:explicit> <itunes:type>episodic</itunes:type> {% for category in site.categories %} <itunes:category text="{{ category }}" /> {% endfor %} {% if site.image %} <itunes:image href="{{ site.image }}" /> <image> <title>{{ site.name }}</title> <link>{{ site.url }}</link> <url>{{ site.image }}</url> </image> {% endif %} </rss>
The last piece of general information to provide the feed is the updated date. This is where two different filters from the RSS plugin are used.
We need to find the most recently added content item in our episodes collection. We can use the getNewestCollectionItemDate
filter for that and then chain that filter with dateToRfc822
to format that date properly. Add the following line to the feed file:
<updated>{{ collections.episodes | getNewestCollectionItemDate | dateToRfc822 }}</updated>
This will display the date in the updated element in the proper format:
<updated>Mon, 31 Dec 2018 19:00:00 +0000</updated>
Next, we need to loop through all the episodes in the collection and make a discrete RSS item for each. To do this, we need to get the absolute URL for each of the episodes from the relative path provided by 11ty. We do that with Nunjucks’ set
tag, the post URL, and the RSS plugin’s absoluteUrl
filter. We also have another instance of an updated date that will require the dateToRfc822
filter again:
{% for post in collections.episodes %} {%- set absolutePostUrl = post.url | absoluteUrl (site.url) %} <item> <title>{{ post.data.title }}</title> <link href="{{ absolutePostUrl }}" /> <updated>{{ post.date | dateToRfc822 }}</updated> <id>{{ absolutePostUrl }}</id> </item> {% endfor %}
We also need to provide the content from the Markdown file to the feed. This will allow for the transcription and description to be available in various podcast apps. To do this, we can use the templateContent
variable on each post. If there are any URLs in that content, however, we also need to make those URLs absolute. The RSS plugin has that ready for us with the htmlToAbsoluteUrls
filter to replace any relative URLs in our Markdown with their absolute versions. Nunjucks also needs to be told to render the Markdown as HTML and not as a string with escaped characters, so we pass that through the safe
filter:
{% for post in collections.episodes %} {%- set absolutePostUrl = post.url | absoluteUrl (site.url) %} <item> <title>{{ post.data.title }}</title> <link href="{{ absolutePostUrl }}" /> <updated>{{ post.date | dateToRfc822 }}</updated> <id>{{ absolutePostUrl }}</id> <content type="html"> <![CDATA[ {{ post.templateContent | htmlToAbsoluteUrls (absolutePostUrl) | safe }} ]]> </content> </item> {% endfor %}
Finally, each item needs an enclosure
element. The enclosure
element provides the actual multimedia content information. This is where we need the Podcast Tools plugin to get the file size information for the feed.