https://github.com/mpgirro/stalla
A Kotlin and Java library for RSS podcast feeds
https://github.com/mpgirro/stalla
java kotlin podcast rss-feed
Last synced: about 1 year ago
JSON representation
A Kotlin and Java library for RSS podcast feeds
- Host: GitHub
- URL: https://github.com/mpgirro/stalla
- Owner: mpgirro
- License: bsd-3-clause
- Created: 2019-03-20T18:46:43.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2024-04-29T13:10:06.000Z (about 2 years ago)
- Last Synced: 2024-05-01T20:29:02.034Z (about 2 years ago)
- Topics: java, kotlin, podcast, rss-feed
- Language: Kotlin
- Homepage: https://stalla.dev
- Size: 5.17 MB
- Stars: 25
- Watchers: 3
- Forks: 4
- Open Issues: 38
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
Stalla
Podcast RSS Feed metadata Parser and Writer
[](https://package-search.jetbrains.com/package?id=dev.stalla%3Astalla)

[](https://coveralls.io/github/mpgirro/stalla?branch=master)
[](https://www.codacy.com/app/mpgirro/stalla?utm_source=github.com&utm_medium=referral&utm_content=mpgirro/stalla&utm_campaign=Badge_Grade)
An RSS 2.0 feed parser and writer library for Podcast metadata on the JVM. This library is written in Kotlin and has a Java-friendly API.
## Supported standards
- [RSS 2.0](http://www.rssboard.org/rss-2-0)
- [Atom](https://tools.ietf.org/html/rfc4287) (RFC 4287) – selected elements, no feed support
- [iTunes](https://help.apple.com/itc/podcasts_connect/#/itcb54353390) (Apple Podcast Connect)
- [Content](http://purl.org/rss/1.0/modules/content/) (RDF Site Summary 1.0 Module)
- [Podlove Simple Chapters](https://podlove.org/simple-chapters/)
- [Podcastindex.org v1](https://github.com/Podcastindex-org/podcast-namespace) ([example feed](https://github.com/Podcastindex-org/podcast-namespace/blob/main/example.xml))
- [Google Play](https://developers.google.com/search/reference/podcast/rss-feed)
- [Feedpress](https://feed.press/xmlns)
- Bitlove
- Fyyd
Scheduled:
- [Podcastindex.org v2](https://github.com/Podcastindex-org/podcast-namespace)
in #49 ([example feed](https://github.com/Podcastindex-org/podcast-namespace/blob/main/example.xml))
- [Spotify](https://drive.google.com/file/d/1KDY1zbRc6J2tkNvhniagor_qcH-pp2T0/view) in #29
- [Dublin Core](http://purl.org/dc/elements/1.1/) ([RFC 5013](https://tools.ietf.org/html/rfc5013)) in #28 – properties in the `/elements/1.1/`
namespace
- [Media RSS](http://www.rssboard.org/media-rss) in #30 ([example feed](https://gist.github.com/misener/7dd9b587b468aea1ae5a))
Feel free to open an issue if Stalla is missing support for a relevant namespace.
## Usage
### Distribution
Stalla is distributed via [Maven Central](https://search.maven.org/search?q=g:dev.stalla%20AND%20a:stalla).
Maven:
```
dev.stalla
stalla
1.0.0
```
Gradle Kotlin DSL:
```
implementation("dev.stalla:stalla:1.0.0")
```
### Parsing an RSS feed
To parse an RSS feed, you need to use the [`PodcastRssParser`](src/main/kotlin/dev/stalla/PodcastRssParser.kt) object. Parsing an RSS feed is as easy
as picking the overload of `parse()` that fits your needs:
```kotlin
val rssFeedFile = File("/my/path/rss.xml")
val podcast = PodcastRssParser.parse(rssFeedFile)
```
The `parse()` function will return a parsed `Podcast?`, which may be `null` if parsing was unsuccessful. It will also throw an exception if parsing
fails catastrophically (e.g., the source is a `File` that doesn't exist).
In Java, all overloads of `parse()` are available as static methods.
### Writing an RSS feed
To write an RSS feed, you need to use the [`PodcastRssWriter`](src/main/kotlin/dev/stalla/PodcastRssWriter.kt) object. Writing an RSS feed is as easy
as picking the overload of `write()` that fits your needs:
```kotlin
val rssFeedFile = File("/my/path/rss.xml")
val podcast: Podcast = // ...
PodcastRssWriter.writeRssFeed(podcast, rssFeedFile)
```
The `write()` function will throw an exception if writing fails for whatever reason.
In Java, all overloads of `write()` are available as static methods.
### Data model
The two central entities in Stalla's entire data model are [`Podcast`](src/main/kotlin/dev/stalla/model/Podcast.kt) (abstracting the RSS ``)
and [`Episode`](src/main/kotlin/dev/stalla/model/Episode.kt) (abstracting an RSS ``).
#### Model construction in Kotlin
All model types are Kotlin [data classes](https://kotlinlang.org/docs/data-classes.html). Their properties are always un-reassignable (`val`), and the
constructors have default values for optional fields (`null`) and lists (`emptyList()`).
```kotlin
val someEpisode: Episode = // ...
val podcast = Podcast(
title = "The Stalla Show",
description = "A show about Stalla development",
link = "https://stalla.dev/podcast",
language = Locale.ENGLISH,
episodes = listOf(firstEpisode)
)
```
#### Model construction in Java
⚠️ Direct instantiation of model class objects is discouraged in Java.
The Java language does not provide named arguments. Some of Stalla's model classes take huge parameter lists. To mitigate this limitation, every model
class provides a static `builder()` method that returns a builder instance enabling expressive construction in Java.
```java
Episode someEpisode = // ...
Podcast podcast = Podcast.builder()
.title("The Stalla Show")
.description("A show about Stalla development")
.link("https://stalla.dev/podcast")
.language(Locale.ENGLISH)
.addEpisodeBuilder(firstEpisode)
.build();
```
Note that builders validate lazily upon calling `build()`, i.e., the constructed model instance may be `null` if:
* Not all required constructor properties of the backing data class have been initialized in the builder (technical requirement by the type system).
* Not all mandatory information bas been provided with respect to the backing specification (RSS or other XML namespace). For example, a
[`Podcast`](src/main/kotlin/dev/stalla/model/Podcast.kt) needs at least one [`Episode`](src/main/kotlin/dev/stalla/model/Episode.kt) (i.e. one
`` within the ``), so while technically an empty list satisfies the technical requirement (required to be not `null`), it however
violates the logical requirement (at least one element).
#### Enumerated types
The supported XML namespaces declare several elements that only accept a finite set of predefined values. In Stalla, these values are usually modeled
via enum classes (e.g., [`GoogleplayCategory`](src/main/kotlin/dev/stalla/model/googleplay/GoogleplayCategory.kt) or
[`TranscriptType`](src/main/kotlin/dev/stalla/model/podcastindex/TranscriptType.kt)). However, some elements, like the Categories of the iTunes
namespace, have a more complex structure (i.e., a hierarchy) that cannot be modeled via enums. These types still try to provide an interface that is
close to that of enums for consistent usage. For example, [`ItunesCategory`](src/main/kotlin/dev/stalla/model/itunes/ItunesCategory.kt) exposes all
possible values as members of the companion object (Kotlin) or as static class members (Java).
Note that the instances exposed by [`MediaType`](src/main/kotlin/dev/stalla/model/MediaType.kt) are not the finite set of possible values, but only
predefined instances for convenience.
### Convenience features
#### Builder factory: builder() method
All model classes expose a `builder()` method as a member of the companion object (Kotlin) or as a static class method (Java). These methods return an
implementation of the respective model classes builder interface. The [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) enables
expressive object construction using named attribute initializers, and compensate for the lack of named constructor arguments in the Java language.
#### Builder mutation: applyFrom() method
All builders expose an `applyFrom(model)` method that takes an instance of the builder's model class as argument, and returns the builder instance. The
method applies all values of the provided model to the builder's state. This is therefore a mutating operation. The returned builder instance is the
mutated state of the builder on which the `applyFrom` method is called, and not a new instance. The operation does not result in any structural
sharing, e.g., references of lists, etc.
The generic use case of this method is the convenient construction of a new model object based on the properties of an existing model instance:
```java
Episode oldEpisode = // ...
Episode newEpisode = Episode.builder()
.applyFrom(oldEpisode)
.title("The latest episode")
.build();
```
In Kotlin, you can also use the `copy()` method exposed by all data classes to obtain a new model instance with some of its properties mutated.
#### Type factory: of() method
All enumerated types expose an `of(String)` method as a member of the companion object (Kotlin) or as a static class method (Java). This method is a
factory and produces an instance of the type if possible, otherwise `null`. In contrast to the enum classes' `valueOf()` standard method, `of()` is
case-insensitive and also available for non-enum class enumerated types (e.g.,
[`ItunesCategory`](src/main/kotlin/dev/stalla/model/itunes/ItunesCategory.kt)).
Additionally, Stalla has some types that validate a string for certain patterns, e.g. [`MediaType`](src/main/kotlin/dev/stalla/model/MediaType.kt)
models media types as defined by [RFC 2046](https://tools.ietf.org/html/rfc2046), and
[`StyledDuration`](src/main/kotlin/dev/stalla/model/StyledDuration.kt) describes values in several supported time formats (`14:02:56` or `5:23` or
`33.833`). These types also provide the `of()` method as the only means to obtain an instance of the class.
### Generate the documentation
The project uses [Dokka](https://github.com/Kotlin/dokka) to generate its documentation from the KDoc comments in the code. If you want to generate a
fresh version of the documentation, use one of the Dokka Gradle tasks:
```bash
$ ./gradlew dokkaHtml
$ ./gradlew dokkaJavadoc
$ ./gradlew dokkaGfm
$ ./gradlew dokkaJekyll
```
## License
Stalla is released under the [BSD 3-clause license](LICENSE).