{"id":18951829,"url":"https://github.com/mpgirro/stalla","last_synced_at":"2025-04-16T00:32:16.521Z","repository":{"id":50977554,"uuid":"176797185","full_name":"mpgirro/stalla","owner":"mpgirro","description":"A Kotlin and Java library for RSS podcast feeds","archived":false,"fork":false,"pushed_at":"2024-04-29T13:10:06.000Z","size":5423,"stargazers_count":25,"open_issues_count":38,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-05-01T20:29:02.034Z","etag":null,"topics":["java","kotlin","podcast","rss-feed"],"latest_commit_sha":null,"homepage":"https://stalla.dev","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mpgirro.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-20T18:46:43.000Z","updated_at":"2024-03-17T14:44:46.000Z","dependencies_parsed_at":"2024-04-29T14:31:43.660Z","dependency_job_id":"673bbd75-1922-461e-bf45-c164bc98e0b4","html_url":"https://github.com/mpgirro/stalla","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpgirro%2Fstalla","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpgirro%2Fstalla/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpgirro%2Fstalla/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpgirro%2Fstalla/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mpgirro","download_url":"https://codeload.github.com/mpgirro/stalla/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223691322,"owners_count":17186791,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","kotlin","podcast","rss-feed"],"created_at":"2024-11-08T13:29:34.471Z","updated_at":"2024-11-08T13:29:35.139Z","avatar_url":"https://github.com/mpgirro.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  Stalla\n\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003e\n\t\u003cimg src=\"https://github.com/mpgirro/stalla/blob/master/.idea/icon.png?raw=true\" width=\"128px\" alt=\"Stalla logo\" /\u003e\n\t\u003cbr/\u003e\n\tPodcast RSS Feed metadata Parser and Writer\n\u003c/h3\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![](https://img.shields.io/maven-central/v/dev.stalla/stalla)](https://package-search.jetbrains.com/package?id=dev.stalla%3Astalla)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/mpgirro/stalla/Buildbot)\n[![Coverage Status](https://coveralls.io/repos/github/mpgirro/stalla/badge.svg?branch=master)](https://coveralls.io/github/mpgirro/stalla?branch=master)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/66d3c5df2fbf4c9aaabe66e52a847cdd)](https://www.codacy.com/app/mpgirro/stalla?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=mpgirro/stalla\u0026amp;utm_campaign=Badge_Grade)\n\n\u003c/div\u003e\nAn 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.\n\n## Supported standards\n\n- [RSS 2.0](http://www.rssboard.org/rss-2-0)\n- [Atom](https://tools.ietf.org/html/rfc4287) (RFC 4287) – selected elements, no feed support\n- [iTunes](https://help.apple.com/itc/podcasts_connect/#/itcb54353390) (Apple Podcast Connect)\n- [Content](http://purl.org/rss/1.0/modules/content/) (RDF Site Summary 1.0 Module)\n- [Podlove Simple Chapters](https://podlove.org/simple-chapters/)\n- [Podcastindex.org v1](https://github.com/Podcastindex-org/podcast-namespace) ([example feed](https://github.com/Podcastindex-org/podcast-namespace/blob/main/example.xml))\n- [Google Play](https://developers.google.com/search/reference/podcast/rss-feed)\n- [Feedpress](https://feed.press/xmlns)\n- Bitlove\n- Fyyd\n\nScheduled:\n\n- [Podcastindex.org v2](https://github.com/Podcastindex-org/podcast-namespace)\n  in #49 ([example feed](https://github.com/Podcastindex-org/podcast-namespace/blob/main/example.xml))\n- [Spotify](https://drive.google.com/file/d/1KDY1zbRc6J2tkNvhniagor_qcH-pp2T0/view) in #29\n- [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/`\n  namespace\n- [Media RSS](http://www.rssboard.org/media-rss) in #30 ([example feed](https://gist.github.com/misener/7dd9b587b468aea1ae5a))\n\nFeel free to open an issue if Stalla is missing support for a relevant namespace.\n\n## Usage\n\n### Distribution\n\nStalla is distributed via [Maven Central](https://search.maven.org/search?q=g:dev.stalla%20AND%20a:stalla).\n\nMaven:\n\n```\n\u003cdependency\u003e\n  \u003cgroupId\u003edev.stalla\u003c/groupId\u003e\n  \u003cartifactId\u003estalla\u003c/artifactId\u003e\n  \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle Kotlin DSL:\n\n```\nimplementation(\"dev.stalla:stalla:1.0.0\")\n```\n\n### Parsing an RSS feed\n\nTo 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\nas picking the overload of `parse()` that fits your needs:\n\n```kotlin\nval rssFeedFile = File(\"/my/path/rss.xml\")\nval podcast = PodcastRssParser.parse(rssFeedFile)\n```\n\nThe `parse()` function will return a parsed `Podcast?`, which may be `null` if parsing was unsuccessful. It will also throw an exception if parsing\nfails catastrophically (e.g., the source is a `File` that doesn't exist).\n\nIn Java, all overloads of `parse()` are available as static methods.\n\n### Writing an RSS feed\n\nTo 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\nas picking the overload of `write()` that fits your needs:\n\n```kotlin\nval rssFeedFile = File(\"/my/path/rss.xml\")\nval podcast: Podcast = // ...\n  PodcastRssWriter.writeRssFeed(podcast, rssFeedFile)\n```\n\nThe `write()` function will throw an exception if writing fails for whatever reason.\n\nIn Java, all overloads of `write()` are available as static methods.\n\n### Data model\n\nThe two central entities in Stalla's entire data model are [`Podcast`](src/main/kotlin/dev/stalla/model/Podcast.kt) (abstracting the RSS `\u003cchannel\u003e`)\nand [`Episode`](src/main/kotlin/dev/stalla/model/Episode.kt) (abstracting an RSS `\u003citem\u003e`).\n\n#### Model construction in Kotlin\n\nAll model types are Kotlin [data classes](https://kotlinlang.org/docs/data-classes.html). Their properties are always un-reassignable (`val`), and the\nconstructors have default values for optional fields (`null`) and lists (`emptyList()`).\n\n```kotlin\nval someEpisode: Episode = // ...\nval podcast = Podcast(\n  title = \"The Stalla Show\",\n  description = \"A show about Stalla development\",\n  link = \"https://stalla.dev/podcast\",\n  language = Locale.ENGLISH,\n  episodes = listOf(firstEpisode)\n)\n```\n\n#### Model construction in Java\n\n⚠️ Direct instantiation of model class objects is discouraged in Java.\n\nThe Java language does not provide named arguments. Some of Stalla's model classes take huge parameter lists. To mitigate this limitation, every model\nclass provides a static `builder()` method that returns a builder instance enabling expressive construction in Java.\n\n```java\nEpisode someEpisode = // ...\nPodcast podcast = Podcast.builder()\n  .title(\"The Stalla Show\")\n  .description(\"A show about Stalla development\")\n  .link(\"https://stalla.dev/podcast\")\n  .language(Locale.ENGLISH)\n  .addEpisodeBuilder(firstEpisode)\n  .build();\n```\n\nNote that builders validate lazily upon calling `build()`, i.e., the constructed model instance may be `null` if:\n\n* Not all required constructor properties of the backing data class have been initialized in the builder (technical requirement by the type system).\n* Not all mandatory information bas been provided with respect to the backing specification (RSS or other XML namespace). For example, a\n  [`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\n  `\u003citem\u003e` within the `\u003cchannel\u003e`), so while technically an empty list satisfies the technical requirement (required to be not `null`), it however\n  violates the logical requirement (at least one element).\n\n#### Enumerated types\n\nThe supported XML namespaces declare several elements that only accept a finite set of predefined values. In Stalla, these values are usually modeled\nvia enum classes (e.g., [`GoogleplayCategory`](src/main/kotlin/dev/stalla/model/googleplay/GoogleplayCategory.kt) or\n[`TranscriptType`](src/main/kotlin/dev/stalla/model/podcastindex/TranscriptType.kt)). However, some elements, like the Categories of the iTunes\nnamespace, 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\nclose to that of enums for consistent usage. For example, [`ItunesCategory`](src/main/kotlin/dev/stalla/model/itunes/ItunesCategory.kt) exposes all\npossible values as members of the companion object (Kotlin) or as static class members (Java).\n\nNote that the instances exposed by [`MediaType`](src/main/kotlin/dev/stalla/model/MediaType.kt) are not the finite set of possible values, but only\npredefined instances for convenience.\n\n### Convenience features\n\n#### Builder factory: builder() method\n\nAll 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\nimplementation of the respective model classes builder interface. The [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) enables\nexpressive object construction using named attribute initializers, and compensate for the lack of named constructor arguments in the Java language.\n\n#### Builder mutation: applyFrom() method\n\nAll builders expose an `applyFrom(model)` method that takes an instance of the builder's model class as argument, and returns the builder instance. The\nmethod applies all values of the provided model to the builder's state. This is therefore a mutating operation. The returned builder instance is the\nmutated state of the builder on which the `applyFrom` method is called, and not a new instance. The operation does not result in any structural\nsharing, e.g., references of lists, etc.\n\nThe generic use case of this method is the convenient construction of a new model object based on the properties of an existing model instance:\n\n```java\nEpisode oldEpisode = // ...\nEpisode newEpisode = Episode.builder()\n  .applyFrom(oldEpisode)\n  .title(\"The latest episode\")\n  .build();\n```\n\nIn 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.\n\n#### Type factory: of() method\n\nAll 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\nfactory and produces an instance of the type if possible, otherwise `null`. In contrast to the enum classes' `valueOf()` standard method, `of()` is\ncase-insensitive and also available for non-enum class enumerated types (e.g.,\n[`ItunesCategory`](src/main/kotlin/dev/stalla/model/itunes/ItunesCategory.kt)).\n\nAdditionally, Stalla has some types that validate a string for certain patterns, e.g. [`MediaType`](src/main/kotlin/dev/stalla/model/MediaType.kt)\nmodels media types as defined by [RFC 2046](https://tools.ietf.org/html/rfc2046), and\n[`StyledDuration`](src/main/kotlin/dev/stalla/model/StyledDuration.kt) describes values in several supported time formats (`14:02:56` or `5:23` or\n`33.833`). These types also provide the `of()` method as the only means to obtain an instance of the class.\n\n### Generate the documentation\n\nThe 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\nfresh version of the documentation, use one of the Dokka Gradle tasks:\n\n```bash\n$ ./gradlew dokkaHtml\n$ ./gradlew dokkaJavadoc\n$ ./gradlew dokkaGfm\n$ ./gradlew dokkaJekyll\n```\n\n## License\n\nStalla is released under the [BSD 3-clause license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpgirro%2Fstalla","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmpgirro%2Fstalla","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpgirro%2Fstalla/lists"}