2 Library Reference
2.1 Building feeds
Use feed-item and feed to create feeds for web content like blog posts, comments, or even notifications: any content with a timestamp and its own URL.
You have a choice of using RSS or Atom formats, or both. Twenty years ago, holy wars were fought over which format was superior and it was necessary to supply both in order to assure compatibility with the most clients. These days almost every client supports both, so you probably only need to supply one.
You should run all your feeds through the W3C Feed Validator. Please file an issue should you encounter any validation errors in feeds created with Splitflap.
procedure
(feed-item id url title author published updated content [ media]) → feed-item? id : tag-uri? url : valid-url-string? title : string? author : person? published : moment? updated : moment? content : xexpr? media : (or/c enclosure? #f) = #f
The id argument must be a tag URI (obtain from mint-tag-uri or append-specific).
The title should not contain HTML markup; if it does, it will be escaped, and the raw markup may by shown in applications that use your feed.
The author argument must be a person struct.
The value of updated must be identical to or after published, taking time zone information into account, or an exception is raised. The values for these arguments can be most conveniently supplied by infer-moment, but any moment-producing method will work, such as constructing moments directly, parsing strings with parse-moment, etc.
If content is a tagged X-expression, it will be included as XML-appropriate escaped HTML; if it is a plain string, it will be included as CDATA.
You can optionally use the media argument to supply an enclosure, but if you are generating a feed for a podcast you should consider using episode and podcast instead.
procedure
id : tag-uri? site-url : valid-url-string? name : string? entries : (listof feed-item?)
If any of the tag URIs of the entries are tag=? with each other or with the feed id, an exception is raised identifying the first duplicate encountered.
2.2 Producing feed XML
procedure
(express-xml data dialect [ feed-url #:as result-type]) → (or/c string? txexpr? document? element?) data : food? dialect : (or/c 'rss 'atom) feed-url : (or/c valid-url-string? #f) = #f result-type : (or/c 'xml-string 'xexpr 'xml) = 'xml-string
The dialect argument is ignored when data is an episode or a podcast, since Apple’s Podcast feed requirements stipulate the use of RSS 2.0 for podcast feeds.
The feed-url argument must be supplied as a valid URL string when data is a feed or a podcast; this should be the URL where the feed itself will be located. It is not a required argument when data is any other type, and in those cases it will be ignored if supplied.
For complete feeds (e.g., when data is a feed or podcast), the entries/episodes will be sorted in reverse chronological order by their “updated” timestamps, and the most recent such timestamp is used as the value for feed-level “last updated” and/or “published” elements. If the feed contains no entries or episodes, these feed-level timestamps will use now/moment.
The output of complete feeds can be further affected by other parameters (view their documentation for more information):
If include-generator? is #t, a generator element will be included.
If feed-language is not #f, its value will be used as the language code for the feed; otherwise the result of (force system-language) is used.
If feed-xslt-stylesheet is not #f, its value will be used as the path to a stylesheet for the feed.
> (define item (feed-item (mint-tag-uri "rclib.example.com" "2012-06" "blog:example-post") "http://rclib.example.com" "Example" (person "Marion Paroo" "marion@rclib.example.com") (infer-moment "2013-04-13 08:45") (infer-moment "2013-04-14") '(article (p "Etc…")))) > (display (express-xml item 'atom #f))
<entry>
<title type="text">Example</title>
<link rel="alternate" href="http://rclib.example.com" />
<updated>2013-04-14T00:00:00-05:00</updated>
<published>2013-04-13T08:45:00-05:00</published>
<author>
<name>Marion Paroo</name>
<email>marion@rclib.example.com</email>
</author>
<id>tag:rclib.example.com,2012-06:blog:example-post</id>
<content type="html"><article><p>Etc…</p></article></content>
</entry>
> (express-xml item 'atom #:as 'xexpr)
'(entry
(title ((type "text")) "Example")
(link ((rel "alternate") (href "http://rclib.example.com")))
(updated "2013-04-14T00:00:00-05:00")
(published "2013-04-13T08:45:00-05:00")
(author (name "Marion Paroo") (email "marion@rclib.example.com"))
(id "tag:rclib.example.com,2012-06:blog:example-post")
(content ((type "html")) "<article><p>Etc…</p></article>"))
parameter
(include-generator? incl?) → void? incl? : boolean?
= #t
parameter
(feed-language) → (or/c iso-639-language-code? #f)
(feed-language lang) → void? lang : (or/c iso-639-language-code? #f)
= #f
parameter
(feed-xslt-stylesheet) → (or/c url? non-empty-string? #f)
(feed-xslt-stylesheet path) → void? path : (or/c url? non-empty-string? #f)
= #f
Generally, the XSLT file must be served from the same domain as the feed itself, otherwise the styling will not be applied.
Stylesheets can solve the problem of feeds looking and behaving in unfriendly ways when accessed directly in a web browser. See this Github issue for examples of people who have used XSLT stylesheets to add informative links and a welcoming layout to their feeds.
2.3 Podcasts
Splitflap provides some special data types for podcast feeds: episode and podcast. These are patterned after Apple’s Podcast feed requirements since those serve as a kind of de facto standard for this application.
procedure
(episode id url title author published updated content media [ #:duration duration #:image-url image-url #:explicit? explicit #:episode-num ep-num #:season-num s-num #:type type #:block? block]) → episode? id : tag-uri? url : valid-url-string? title : string? author : person? published : moment? updated : moment? content : xexpr? media : enclosure? duration : (or/c exact-nonnegative-integer? #f) = #f image-url : (or/c valid-url-string? #f) = #f explicit : any/c = null ep-num : (or/c exact-nonnegative-integer? #f) = #f s-num : (or/c exact-nonnegative-integer? #f) = #f type : (or/c 'trailer 'full 'bonus #f) = #f block : any/c = #f
The value of updated must be identical to or after published, taking time zone information into account, or an exception is raised. The values for these arguments can be most conveniently supplied by infer-moment, but any moment-producing method will work, such as constructing moments directly, parsing strings with parse-moment, etc.
If content is a tagged X-expression, it will be included as escaped HTML; if it is a plain string, it will be included as CDATA.
Below are further notes about particular elements supplied to episode. The colored passages indicate things which are required by Apple for inclusion in the Apple Podcasts directory but which are not validated by Splitflap. (See Apple’s Podcast feed requirements.)
The title should contain no markup, and should not contain episode or season number information (use #:episode-num and #:season-num for this instead).
The #:image-url is for episode-specific artwork. It must point to an image with a minimum size of 1400 ⨉ 1400 pixels and a maximum size of 3000 ⨉ 3000 pixels (the preferred size), in JPEG or PNG format, 72 dpi, with appropriate file extensions (.jpg, .png), and in the RGB colorspace. See Apple artwork requirements for other requirements.
The #:duration gives the episode’s duration in seconds, and is optional but recommended.
If explicit is an optional override of the mandatory feed-level parental advisory flag in podcast. If it is null (the default), the episode will not contain any parental advisory information. If it is #f, Apple Podcasts will mark the episode as “Clean”. If it is any other value, Apple Podcasts will mark the episode as “Explicit”.
The #:episode-num is optional, but Apple will require it if the podcast has a type of 'episodic.
You can optionally set #:type to 'full if this is a normal full-length episode; to 'trailer for short promos and previews; or to 'bonus for extra or cross-promotional content.
The #:block flag can be set to #t to prevent a particular episode from appearing in Apple podcasts. For example you might want to block a specific episode if you know that its content would otherwise cause the entire podcast to be removed from Apple Podcasts.
procedure
(podcast id site-url name episodes category image-url owner #:explicit? explicit [ #:type type #:block? block #:complete? complete #:new-feed-url new-url]) → podcast? id : tag-uri? site-url : valid-url-string? name : string? episodes : (listof episode?) category : (or/c string? (list/c string? string?)) image-url : valid-url-string? owner : person? explicit : any/c type : (or/c 'serial 'episodic #f) = #f block : any/c = #f complete : any/c = #f new-url : (or/c valid-url-string? #f) = #f
If any of the tag URIs of the episodes are tag=? with each other or with the podcast feed id, an exception is raised identifying the first duplicate encountered.
Below are some notes about particular elements supplied to podcast. The colored passages indicate things which are required by Apple for inclusion in the Apple Podcasts directory but which are not validated by Splitflap. (See Apple’s Podcast feed requirements.)
The category can be either a simple category or a list containing a category and sub-category. The category names must be drawn from Apple’s podcast category list.
The image-url links to the show’s artwork. It must point to an image with a minimum size of 1400 ⨉ 1400 pixels and a maximum size of 3000 ⨉ 3000 pixels (the preferred size), in JPEG or PNG format, 72 dpi, with appropriate file extensions (.jpg, .png), and in the RGB colorspace. See Apple artwork requirements for other requirements.
The owner is for administrative communication about the podcast and is not displayed in Apple’s podcast listings.
The #:type can be one of two values.
Use 'episodic when episodes are not intended to be consumed in any particular order: in this case, Apple Podcasts will present newest episodes first. (If organized into seasons, the newest season will be presented first; otherwise, episodes will be grouped by year published, newest first.)
Use 'serial when episodes are intended to be consumed in sequential order: in this case, Apple Podcasts will present the oldest episodes first. (If organized into seasons, the newest season will be shown first.)
Setting #:block to #t will prevent the entire podcast from appearing in Apple Podcasts.
Setting #:complete? to #t indicates that a podcast is complete and you will not post any more episodes in the future.
If you change the URL where your feed is located, then (in the feed located at the original URL) set #:new-feed-url to the new feed’s URL. Apple Podcasts (and possibly other clients) will automatically update subscriptions to use the new feed. See Apple’s guidelines for moving your RSS feed.
2.4 Feed type predicates
procedure
(feed-item? v) → boolean?
v : any/c