PODCASTPARSER(3)

πŸ“¦ version 0.6.11, in ubuntu resolute, ubuntu stonking

🌸 NAME 🌸

podcastparser β€” podcastparser Documentation. Section 3 of the manual, if you're keeping score πŸ’…

podcastparser is a simple and fast podcast feed parser library in Python. Two primary users of the library, bestie: the gPodder Podcast Client (http://gpodder.org/) and the gpodder.net web service (http://gpodder.net/). Iconic duo, honestly. πŸ‘‘

The feed types it supports:

The specifications it supports:

Real talk though: these formats only specify the possible markup elements and attributes. So if you wanna optimize your feeds for best display in podcast clients, the docs also recommend you read the Podcast Feed Best Practice guide at https://github.com/gpodder/podcast-feed-best-practice/blob/master/podcast-feed-best-practice.md. Do it, it's giving ✨prepared✨.

Where times and durations show up, the values are expected to be formatted either as seconds, or as RFC 2326 (https://tools.ietf.org/html/rfc2326) Normal Play Time (NPT). Two options, both valid, we love that for us 🫢


🐍 Using the built-in urllib.request module from Python 3

import podcastparser
import urllib.request

feedurl = 'http://example.com/feed.xml'

parsed = podcastparser.parse(feedurl, urllib.request.urlopen(feedurl))

# parsed is a dict
import pprint
pprint.pprint(parsed)

🐍 Using Requests (https://requests.readthedocs.io)

import podcastparser
import requests

url = 'https://example.net/podcast.atom'

with requests.get(url, stream=True) as response:
    response.raw.decode_content = True
    parsed = podcastparser.parse(url, response.raw)

# parsed is a dict
import pprint
pprint.pprint(parsed)

For both RSS and Atom feeds, only a subset of elements gets parsed β€” specifically the ones that are relevant to podcast client applications. This next section describes which elements and attributes are parsed and how the contents are interpreted/used. No gatekeeping here, we're telling you everything πŸ”


πŸ“» RSS πŸ“»

  • rss@xml:base β€” Base URL for all the relative links in the RSS file. It's the anchor of the whole vibe.
  • rss/channel β€” the Podcast itself. The main character πŸŽ€
  • rss/channel/title β€” the Podcast title. (Whitespace gets squashed.)
  • rss/channel/link β€” the Podcast website.
  • rss/channel/description β€” the Podcast description. (Whitespace gets squashed.)
  • rss/channel/itunes:summary β€” the Podcast description again. (Whitespace gets squashed.)
  • rss/channel/image/url β€” the Podcast cover art.
  • rss/channel/itunes:image β€” the Podcast cover art, alternative route.
  • rss/channel/itunes:type β€” the Podcast type. (Whitespace gets squashed.) One of β€˜episodic’ or β€˜serial’.
  • rss/channel/itunes:keywords β€” the Podcast keywords. (Whitespace gets squashed.)
  • rss/channel/atom:link@rel=payment β€” the Podcast payment URL, e.g. Flattr. Get that bag πŸ’Έ
  • rss/channel/generator β€” a string indicating the program used to generate the channel. Like MightyInHouse Content System v2.3.
  • rss/channel/language β€” the Podcast language.
  • rss/channel/itunes:author β€” the group responsible for creating the show.
  • rss/channel/itunes:owner β€” the podcast owner contact information. Heads up: the <itunes:owner> tag information is for administrative communication about the podcast, and it isn't displayed in Apple Podcasts. So it's the backstage pass, not the fit check πŸ™ˆ
  • rss/channel/itunes:category β€” the show category information.
  • rss/channel/itunes:explicit β€” indicates whether the podcast contains explicit material.
  • rss/channel/itunes:new-feed-url β€” the new podcast RSS Feed URL.
  • rss/channel/podcast:locked β€” if the podcast is currently locked from being transferred.
  • rss/channel/podcast:funding β€” funding link for the podcast. Support your faves 🫢
  • rss/redirect/newLocation β€” the new podcast RSS Feed URL.
  • rss/channel/item β€” an Episode. The moment we've all been waiting for ⭐
  • rss/channel/item/guid β€” the Episode unique identifier (GUID). This one is mandatory, no cap.
  • rss/channel/item/title β€” the Episode title. (Whitespace gets squashed.)
  • rss/channel/item/link β€” the Episode website.
  • rss/channel/item/description β€” the Episode description. If it contains html, it comes back as description_html. Otherwise it comes back as description (whitespace gets squashed). See Mozilla's article Why RSS Content Module is Popular.
  • rss/channel/item/itunes:summary β€” the Episode description. (Whitespace gets squashed.)
  • rss/channel/item/itunes:subtitle β€” the Episode subtitled / one-line description. (Whitespace gets squashed.)
  • rss/channel/item/content:encoded β€” the Episode description in HTML. This is the best source for description_html.
  • rss/channel/item/itunes:duration β€” the Episode duration.
  • rss/channel/item/pubDate β€” the Episode publication date.
  • rss/channel/item/atom:link@rel=payment β€” the Episode payment URL, e.g. Flattr.
  • rss/channel/item/atom:link@rel=enclosure β€” the File download URL (@href), size (@length) and mime type (@type).
  • rss/channel/item/itunes:image β€” the Episode art URL.
  • rss/channel/item/media:thumbnail β€” the Episode art URL.
  • rss/channel/item/media:group/media:thumbnail β€” the Episode art URL.
  • rss/channel/item/media:content β€” the File download URL (@url), size (@fileSize) and mime type (@type).
  • rss/channel/item/media:group/media:content β€” the File download URL (@url), size (@fileSize) and mime type (@type).
  • rss/channel/item/enclosure β€” the File download URL (@url), size (@length) and mime type (@type).
  • rss/channel/item/psc:chapters β€” Podlove Simple Chapters, version 1.1 and 1.2.
  • rss/channel/item/psc:chapters/psc:chapter β€” a Chapter entry (@start, @title, @href and @image).
  • rss/channel/item/itunes:explicit β€” indicates whether the episode contains explicit material.
  • rss/channel/item/itunes:author β€” the group responsible for creating the episode.
  • rss/channel/item/itunes:season β€” the season number of the episode.
  • rss/channel/item/itunes:episode β€” an episode number.
  • rss/channel/item/itunes:episodeType β€” the episode type. This flag is used if an episode is a trailer or bonus content.
  • rss/channel/item/podcast:chapters β€” the url to a JSON file describing the chapters. ⚠️ Only the url gets added to the data, because fetching an external URL would be unsafe. Periodt, don't come for us, that's just how it is.
  • rss/channel/item/podcast:person β€” a person involved in the episode, e.g. host, or guest.
  • rss/channel/item/podcast:transcript β€” the url for the transcript file associated with this episode.
  • atom:feed β€” a Podcast. the whole show, the main character. πŸ‘‘
  • atom:feed/atom:title β€” the Podcast title (whitespace is squashed, so no weird gaps).
  • atom:feed/atom:subtitle β€” the Podcast description (whitespace is squashed here too).
  • atom:feed/atom:icon β€” Podcast cover art. the lil artwork moment ✨
  • atom:feed/atom:link@href β€” Podcast website.
  • atom:feed/atom:entry β€” an Episode.
  • atom:feed/atom:entry/atom:id β€” the Episode unique identifier (GUID), and ngl this one is mandatory. no id, no episode, periodt.
  • atom:feed/atom:entry/atom:title β€” Episode title (whitespace is squashed).
  • atom:feed/atom:entry/atom:link@rel=enclosure β€” the File download URL (@href), the size (@length) and the mime type (@type). this is the download link bestie 🫢
  • atom:feed/atom:entry/atom:link@rel=(self|alternate) β€” Episode website.
  • atom:feed/atom:entry/atom:link@rel=payment β€” the Episode payment URL (e.g. Flattr).
  • atom:feed/atom:entry/atom:content β€” the Episode description (in HTML or plaintext).
  • atom:feed/atom:entry/atom:published β€” the Episode publication date.
  • atom:feed/atom:entry/media:thumbnail β€” the Episode art URL.
  • atom:feed/atom:entry/media:group/media:thumbnail β€” also the Episode art URL, just nested differently.
  • atom:feed/atom:entry/psc:chapters β€” Podlove Simple Chapters, version 1.1 and 1.2.
  • atom:feed/atom:entry/psc:chapters/psc:chapter β€” a Chapter entry (@start, @title, @href and @image).

Simplified, fast RSS parser. that's the whole energy of this module, fr fr. πŸ’…

exception podcastparser.FeedParseError(msg, exception, locator)

Exception raised when asked to parse an invalid feed.

so basically this exception is here so that you, the user of this library, can catch exceptions without having to import the XML parsing library yourself. it's giving convenience, it's giving thoughtfulness, we love that for us. πŸ’–

class podcastparser.PodcastHandler(url, max_episodes)

characters(chars)

Receive notification of character data.

okay so here's the lore: the Parser will call this method to report each chunk of character data. and SAX parsers are chaotic like that β€” they may return all contiguous character data in a single chunk, or they may split it into several chunks. but here's the important bit: all of the characters in any single event must come from the same external entity so that the Locator provides useful information.

endElement(name)

Signals the end of an element in non-namespace mode.

the name parameter contains the name of the element type, just as with the startElement event.

startElement(name, attrs)

Signals the start of an element in non-namespace mode.

the name parameter contains the raw XML 1.0 name of the element type as a string, and the attrs parameter holds an instance of the Attributes class containing the attributes of the element. two parameters, no notes. πŸŽ€

class podcastparser.RSSItemDescription

RSS 2.0 almost encourages to put html content in item/description but content:encoded is the better source of html content and itunes:summary is known to contain the short textual description of the item. So use a heuristic to attribute text to either description or description_html, without overriding existing values.

tl;dr: the format is messy so we do a lil detective work and don't clobber anything you already got. πŸ•΅οΈβ€β™€οΈ

podcastparser.file_basename_no_extension(filename)

Returns filename without extension.

>>> file_basename_no_extension('/home/me/file.txt')
'file'

>>> file_basename_no_extension('file')
'file'

podcastparser.is_html(text)

Heuristically tell if text is HTML.

By looking for an open tag (more or less:)

>>> is_html(β€˜<h1>HELLO</h1>’)
True
>>> is_html(β€˜a < b < c’)
False

podcastparser.normalize_feed_url(url)

Normalize and convert a URL. If the URL cannot be converted (invalid or unknown scheme), None is returned.

This will also normalize <feed://> and itpc:// to http://.

>>> normalize_feed_url('itpc://example.org/podcast.rss')
'http://example.org/podcast.rss'

If no URL scheme is defined (e.g. β€œcurry.com”), we will simply assume the user intends to add a <http://> feed.

>>> normalize_feed_url('curry.com')
'http://curry.com/'

It will also take care of converting the domain name to all-lowercase (because domains are not case sensitive):

>>> normalize_feed_url('http://Example.COM/')
'http://example.com/'

Some other minimalistic changes are also taken care of, e.g. a ? with an empty query is removed:

>>> normalize_feed_url('http://example.org/test?')
'http://example.org/test'

Leading and trailing whitespace is removed:

>>> normalize_feed_url(' http://example.com/podcast.rss ')
'http://example.com/podcast.rss'

Incomplete (too short) URLs are not accepted:

>>> normalize_feed_url('http://') is None
True

Unknown protocols are not accepted:

>>> normalize_feed_url('gopher://gopher.hprc.utoronto.ca/file.txt') is None
True

podcastparser.parse(url, stream, max_episodes=0)

Parse a podcast feed from the given URL and stream.

Parameters

  • url – the URL of the feed. Will be used to resolve relative links.
  • stream – file-like object containing the feed content.
  • max_episodes – maximum number of episodes to return. 0 (default) means no limit.

Returns

a dict with the parsed contents of the feed. that's it, that's the whole return. 🫢

podcastparser.parse_length(text)

Parses a file length.

>>> parse_length(None)
-1

>>> parse_length('0')
-1

>>> parse_length('unknown')
-1

>>> parse_length('100')
100

podcastparser.parse_pubdate(text)

Parse a date string into a Unix timestamp.

>>> parse_pubdate('Fri, 21 Nov 1997 09:55:06 -0600')
880127706

>>> parse_pubdate('2003-12-13T00:00:00+02:00')
1071266400

>>> parse_pubdate('2003-12-13T18:30:02Z')
1071340202

>>> parse_pubdate('Mon, 02 May 1960 09:05:01 +0100')
-305049299

>>> parse_pubdate('')
0

>>> parse_pubdate('unknown')
0

podcastparser.parse_time(value)

Parse a time string into seconds.

See RFC2326, 3.6 β€œNormal Play Time” (HH:MM:SS.FRACT).

>>> parse_time('0')
0
>>> parse_time('128')
128
>>> parse_time('00:00')
0
>>> parse_time('00:00:00')
0
>>> parse_time('00:20')
20
>>> parse_time('00:00:20')
20
>>> parse_time('01:00:00')
3600
>>> parse_time(' 03:02:01')
10921
>>> parse_time('61:08')
3668
>>> parse_time('25:03:30 ')
90210
>>> parse_time('25:3:30')
90210
>>> parse_time('61.08')
61
>>> parse_time('01:02:03.500')
3723
>>> parse_time(' ')
0

podcastparser.parse_type(text)

β€œnormalize” a mime type. (quotes around normalize are doing a lot of work here and honestly? iconic.)

>>> parse_type('text/plain')
'text/plain'

>>> parse_type('text')
'application/octet-stream'

>>> parse_type('')
'application/octet-stream'

>>> parse_type(None)
'application/octet-stream'

podcastparser.remove_html_tags(html)

Remove HTML tags from a string and replace numeric and named entities with the corresponding character, so the HTML text can be displayed in a simple text view. clean text, no markup crumbs. 🧚

podcastparser.squash_whitespace(text)

Combine multiple whitespaces into one, trim trailing/leading spaces.

>>> squash_whitespace(' some           text  with a    lot of   spaces ')
'some text with a lot of spaces'

podcastparser.squash_whitespace_not_nl(text)

Like squash_whitespace, but don’t squash linefeeds and carriage returns.

>>> squash_whitespace_not_nl(' linefeeds\ncarriage\r  returns')
'linefeeds\ncarriage\r returns'

This is a list of podcast-related XML namespaces that are not yet supported by podcastparser, but might be in the future. πŸ‘€ manifesting them for later bestie.

✨ CHAPTER MARKS ✨

✨ OTHERS ✨

Author

gPodder Team 🫢

2013, gPodder Team


and that's the whole page bestie, we did it. πŸ’–βœ¨ this was podcastparser 0.6.11, dated Nov 29, 2025 β€” go forth and parse some feeds, you absolute icon. πŸ‘‘πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from