PodcastRSS.Channel (Podcast RSS v0.3.0)

View Source

Low-level API for creating podcast RSS channels.

This module provides direct control over all channel properties without any defaults. For most users, PodcastRSS module provides a more convenient API with useful defaults.

The Channel struct contains the core RSS 2.0 required fields for a valid podcast feed.

Summary

Functions

Add an episode to the channel.

Set the podcast category.

Set the podcast category with subcategory.

Add a custom field to the channel.

Set the channel description.

Set the podcast cover image URL.

Set the podcast language.

Set the channel link (website URL).

Create a new empty channel.

Create a new episode that inherits this channel's namespaces.

Register a namespace for use in custom fields.

Set the canonical RSS feed URL.

Set the channel title.

Set the type of show.

Types

custom_field()

@type custom_field() :: %{content: String.t(), attributes: map(), cdata: boolean()}

t()

@type t() :: %PodcastRSS.Channel{
  category: String.t() | {String.t(), String.t()} | nil,
  custom_fields: %{required(String.t()) => custom_field()},
  description: String.t() | nil,
  episodes: [PodcastRSS.Episode.t()],
  feed_url: String.t() | nil,
  image_url: String.t() | nil,
  language: String.t() | nil,
  link: String.t() | nil,
  namespaces: %{required(String.t()) => String.t()},
  title: String.t() | nil,
  type: :episodic | :serial | nil
}

Functions

add_episode(channel, episode)

@spec add_episode(t(), PodcastRSS.Episode.t()) :: t()

Add an episode to the channel.

Episodes are prepended to the list, so the most recently added episode will appear first in the RSS feed.

category(channel, category)

@spec category(t(), String.t()) :: t()

Set the podcast category.

The category must be a valid Apple Podcasts category. This function validates the category against Apple's official category list.

Examples

iex> Channel.new() |> Channel.category("Technology")
%Channel{category: "Technology", ...}

category(channel, category, subcategory)

@spec category(t(), String.t(), String.t()) :: t()

Set the podcast category with subcategory.

Both the category and subcategory must be valid Apple Podcasts categories. This function validates both against Apple's official category list.

Examples

iex> Channel.new() |> Channel.category("Health & Fitness", "Nutrition")
%Channel{category: {"Health & Fitness", "Nutrition"}, ...}

custom_field(channel, name, content, opts \\ [])

@spec custom_field(t(), String.t(), String.t(), keyword()) :: t()

Add a custom field to the channel.

Namespace Handling

If the field name contains a colon (e.g., "itunes:block"), the namespace prefix must be registered first using register_namespace/3, or you can use the :namespace_uri option for one-shot registration.

Options

  • :attributes - A map of XML attributes for the field (default: %{})
  • :cdata - Whether to wrap content in CDATA (default: false)
  • :namespace_uri - URI for one-shot namespace registration (extracts prefix from field name)

Examples

# Method 1: Explicit registration (recommended for multiple fields)
channel = Channel.new()
  |> Channel.register_namespace("itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd")
  |> Channel.custom_field("itunes:block", "no")
  |> Channel.custom_field("itunes:explicit", "no")

# Method 2: One-shot registration (convenient for single usage)
channel = Channel.new()
  |> Channel.custom_field("itunes:block", "no",
       namespace_uri: "http://www.itunes.com/dtds/podcast-1.0.dtd")
  |> Channel.custom_field("podcast:locked", "no",
       namespace_uri: "https://podcastindex.org/namespace/1.0")
  |> Channel.custom_field("itunes:explicit", "no")  # Uses registered namespace

# Non-namespaced fields
channel
  |> Channel.custom_field("language", "en-us")
  |> Channel.custom_field("copyright", "© 2024 My Podcast", attributes: %{"type" => "legal"})

description(channel, description)

@spec description(t(), String.t()) :: t()

Set the channel description.

image(channel, image_url)

@spec image(t(), String.t()) :: t()

Set the podcast cover image URL.

language(channel, language_code)

@spec language(t(), String.t()) :: t()

Set the podcast language.

The language should be specified using ISO 639 language codes (e.g., "en-us", "de", "fr").

link(channel, link)

@spec link(t(), String.t()) :: t()

Set the channel link (website URL).

new()

@spec new() :: t()

Create a new empty channel.

new_episode(channel)

@spec new_episode(t()) :: PodcastRSS.Episode.t()

Create a new episode that inherits this channel's namespaces.

The returned episode will have all of the channel's registered namespaces available, allowing immediate use of namespace-prefixed custom fields without requiring explicit namespace registration.

Examples

episode = channel
  |> Channel.new_episode()
  |> Episode.title("Episode Title")
  |> Episode.custom_field("itunes:duration", "00:15:30")  # Works immediately

register_namespace(channel, namespace, uri)

@spec register_namespace(t(), String.t(), String.t()) :: t()

Register a namespace for use in custom fields.

This allows you to use namespaced field names (e.g., "itunes:block") without having to specify the namespace URI every time.

Examples

channel = Channel.new()
  |> Channel.register_namespace("itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd")
  |> Channel.custom_field("itunes:block", "no")     # Uses registered namespace
  |> Channel.custom_field("itunes:explicit", "no")  # Uses registered namespace

self_link(channel, feed_url)

@spec self_link(t(), String.t()) :: t()

Set the canonical RSS feed URL.

title(channel, title)

@spec title(t(), String.t()) :: t()

Set the channel title.

type(channel, show_type)

@spec type(t(), :episodic | :serial) :: t()

Set the type of show.

The type of show determines how Apple Podcasts presents your episodes to listeners. According to Apple's iTunes specifications:

  • Episodic (default): Episodes are intended to be consumed without any specific order. Apple Podcasts will present newest episodes first and display the publish date of each episode. If organized into seasons, the newest season will be presented first - otherwise, episodes will be grouped by year published, newest first. For new subscribers, Apple Podcasts adds the newest, most recent episode in their Library.

  • Serial: Episodes are intended to be consumed in sequential order. Apple Podcasts will present the oldest episodes first and display the episode numbers of each episode. If organized into seasons, the newest season will be presented first and episode numbers must be given for each episode. For new subscribers, Apple Podcasts adds the first episode to their Library, or the entire current season if using seasons.

Examples

iex> Channel.new() |> Channel.type(:episodic)
%Channel{type: :episodic, ...}

iex> Channel.new() |> Channel.type(:serial)
%Channel{type: :serial, ...}