A simple Scala 3 API for generating RSS, for general websites as well as for podcasts.
Most common RSS extensions are supported, including Apple Podcast "itunes" elements.
Suppose we have a "blog" defined like this:
//> using scala "3.2.1"
//> using dep "com.mchange::audiofluidity-rss:0.0.2"
import audiofluidity.rss.*
import java.time.*
import scala.collection.*
object MyBlog {
val myName = "Arthur Q. Author"
val myEmail = s"[email protected] (${myName})"
val mainUrl = "https://myblog.dev.null/"
case class Post(title: String, desc: String, text : String, published : Long):
def permalink = s"${mainUrl}/entry/${published}.html"
}
// reverse chronological
given Ordering[MyBlog.Post]
= Ordering.by((p : MyBlog.Post) => (p.published, p.title, p.desc, p.text)).reverse
val posts = immutable.SortedSet (
MyBlog.Post("Hello!", "First post!", "Some words I write.", 1674449923644),
MyBlog.Post("Is this on?", "In which I worry.", "Why was my post not greeted with adulation?", 1674795664978),
MyBlog.Post("Pulitzer!", "In which I gloat.", "Finally 'Hello !' received the recognition it deserves.", 1675054938281),
MyBlog.Post("", "This is an untitled post.", "I've got nothing to say but it's okay.", 1676054938281),
)
def zonedDateTime( epochMilli : Long ) =
Instant.ofEpochMilli(epochMilli).atZone(ZoneId.systemDefault())
You can generate simple XML for it like this:
import audiofluidity.rss.*
import java.time.*
import scala.collection.*
object SimpleExample:
given Itemable[MyBlog.Post] with
extension (post : MyBlog.Post)
def toItem : Element.Item =
val pubDate : Option[ZonedDateTime] = Some(zonedDateTime(post.published))
Element.Item.create (
title = post.title,
linkUrl = post.permalink,
description = post.desc,
author = MyBlog.myEmail,
pubDate = pubDate
)
val channel = Element.Channel.create (
title = "My blog's RSS feed!",
linkUrl = MyBlog.mainUrl,
description = "This blog will blow your mind. Or your chance.",
items = posts
)
val rssFeed = Element.Rss(channel)
@main def simple_go() : Unit =
println(rssFeed.asXmlText)
You can run this from the example directory of this repository:
$ scala-cli . --interactive
And whee!
<?xml version='1.0' encoding='UTF-8'?>
<rss version="2.0">
<channel>
<title>My blog's RSS feed!</title>
<link>https://myblog.dev.null/</link>
<description><![CDATA[This blog will blow your mind. Or your chance.]]></description>
<docs>https://cyber.harvard.edu/rss/rss.html</docs>
<item>
<pubDate>Fri, 10 Feb 2023 13:48:58 -0500</pubDate>
<author>[email protected] (Arthur Q. Author)</author>
<description><![CDATA[This is an untitled post.]]></description>
<link>https://myblog.dev.null//entry/1676054938281.html</link>
<title></title>
</item>
<item>
<pubDate>Mon, 30 Jan 2023 00:02:18 -0500</pubDate>
<author>[email protected] (Arthur Q. Author)</author>
<description><![CDATA[In which I gloat.]]></description>
<link>https://myblog.dev.null//entry/1675054938281.html</link>
<title>Pulitzer!</title>
</item>
<item>
<pubDate>Fri, 27 Jan 2023 00:01:04 -0500</pubDate>
<author>[email protected] (Arthur Q. Author)</author>
<description><![CDATA[In which I worry.]]></description>
<link>https://myblog.dev.null//entry/1674795664978.html</link>
<title>Is this on?</title>
</item>
<item>
<pubDate>Sun, 22 Jan 2023 23:58:43 -0500</pubDate>
<author>[email protected] (Arthur Q. Author)</author>
<description><![CDATA[First post!]]></description>
<link>https://myblog.dev.null//entry/1674449923644.html</link>
<title>Hello!</title>
</item>
</channel>
</rss>
audiofluidity-rss defines lots of not-standard-RSS elements that are commonly mixed into RSS feeds.
For example...
- You might wish to mix-in non-RSS-standard tags
- ...to use
<dc:creator>
elements for an author's name, rather than the e-mail address required in the<author>
tag by the RSS standard - ...to include full-text content, rather than just the RSS-standard
<description>
, in your feed items. A common way to do this is with RDF-defined<content:encoded>
tags. - ...to add an
<atom:link>
element to your channel item indicating the home page of the blog tht the feed represents - If you do any or all of these things, your RSS tag should properly bind
the right XML namespaces to those
dc
/content
/atom
prefixes.
- ...to use
- Defying the RSS standard, you might wish to drop required
<author>
tags (because you don't want to emit e-mails, real or fake), or to drop the required<title>
element for untitled posts (rather than including an empty title).
audiofluidity_rss supports
- mixing-in nonstandard elements (and offers definitions of lots of common choices)
- defining RSS-tag namespaces
- inserting post-processing into the XML-generation process to drop or rewrite elements.
For an example with all the above, please see example/FancierExample.scala
.
You can run it in the examples dir with
$ scala-cli . --interactive
Here's what the output looks like:
<?xml version='1.0' encoding='UTF-8'?>
<rss
version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>My blog's RSS feed!</title>
<link>https://myblog.dev.null/</link>
<description><![CDATA[This blog will blow your mind. Or your chance.]]></description>
<docs>https://cyber.harvard.edu/rss/rss.html</docs>
<item>
<pubDate>Fri, 10 Feb 2023 13:48:58 -0500</pubDate>
<description><![CDATA[This is an untitled post.]]></description>
<link>https://myblog.dev.null//entry/1676054938281.html</link>
<dc:creator><![CDATA[Arthur Q. Author]]></dc:creator>
<content:encoded><![CDATA[I've got nothing to say but it's okay.]]></content:encoded>
</item>
<item>
<pubDate>Mon, 30 Jan 2023 00:02:18 -0500</pubDate>
<description><![CDATA[In which I gloat.]]></description>
<link>https://myblog.dev.null//entry/1675054938281.html</link>
<title>Pulitzer!</title>
<dc:creator><![CDATA[Arthur Q. Author]]></dc:creator>
<content:encoded><![CDATA[Finally 'Hello !' received the recognition it deserves.]]></content:encoded>
</item>
<item>
<pubDate>Fri, 27 Jan 2023 00:01:04 -0500</pubDate>
<description><![CDATA[In which I worry.]]></description>
<link>https://myblog.dev.null//entry/1674795664978.html</link>
<title>Is this on?</title>
<dc:creator><![CDATA[Arthur Q. Author]]></dc:creator>
<content:encoded><![CDATA[Why was my post not greeted with adulation?]]></content:encoded>
</item>
<item>
<pubDate>Sun, 22 Jan 2023 23:58:43 -0500</pubDate>
<description><![CDATA[First post!]]></description>
<link>https://myblog.dev.null//entry/1674449923644.html</link>
<title>Hello!</title>
<dc:creator><![CDATA[Arthur Q. Author]]></dc:creator>
<content:encoded><![CDATA[Some words I write.]]></content:encoded>
</item>
<atom:link type="application/rss+xml" rel="self" href="https://myblog.dev.null/"/>
</channel>
</rss>
Although this library defines an informal AST for RSS, for now it only generates XML, it does not consume and parse it back.
Maybe someday if there's interest.
audiofluidity-rss was revised from a library internal to audiofluidity, a podcast-specific static-site generator.
However, this library is now offered independently, under Apache 2.0 terms. Please see LICENSE.
(The main audiofluidity application is a GPLv3 project.)
More docs soon, I hope. But for now, I want to bookmark some useful RSS resources:
- Really Simple Syndication Best Practices Profile
- RSS 2.0.1 Specification
- RSS Mime Type
- RSS Validator
- Excerpt from O'Reilly book by Ben Hammersley
- DublinCore (
dc
) specification - Podcast RSS resources at audiofluidity main
- Atom specification
- RSS Feed Best Practises — Kevin Cox
See also the podcast-centric RSS resource list in the main audiofluidity README.md