Python library for generating web feeds in both ATOM and RSS formats with extensible support for specialized feed formats
—
Extensible plugin architecture supporting specialized feed formats including podcasts (iTunes), Dublin Core metadata, GeoRSS, media RSS, syndication, and BitTorrent feeds.
Methods for loading and registering extensions on feeds and entries.
def load_extension(self, name, atom=True, rss=True):
"""
Load built-in extension by name.
Args:
name (str): Extension name ('podcast', 'dc', 'geo', etc.)
atom (bool): Enable extension for ATOM feeds
rss (bool): Enable extension for RSS feeds
"""
def register_extension(self, namespace, extension_class_feed=None,
extension_class_entry=None, atom=True, rss=True):
"""
Register custom extension classes.
Args:
namespace (str): XML namespace for extension
extension_class_feed (class, optional): Feed extension class
extension_class_entry (class, optional): Entry extension class
atom (bool): Enable extension for ATOM feeds
rss (bool): Enable extension for RSS feeds
"""iTunes podcast support with specialized metadata for podcast feeds and episodes.
# Load podcast extension
fg.load_extension('podcast')
# Podcast feed methods (accessible via fg.podcast.*)
def itunes_author(self, author=None): ...
def itunes_block(self, block=None): ... # 'yes' or 'no'
def itunes_category(self, category=None, subcategory=None, replace=False): ...
def itunes_explicit(self, explicit=None): ... # 'yes', 'no', 'clean'
def itunes_complete(self, complete=None): ... # 'yes' or 'no'
def itunes_image(self, image=None): ...
def itunes_new_feed_url(self, new_feed_url=None): ...
def itunes_owner(self, name=None, email=None): ...
def itunes_subtitle(self, subtitle=None): ...
def itunes_summary(self, summary=None): ...
def itunes_type(self, type=None): ... # 'episodic' or 'serial'
# Podcast entry methods (accessible via fe.podcast.*)
def itunes_author(self, author=None): ...
def itunes_season(self, season=None): ...
def itunes_episode(self, episode=None): ...
def itunes_title(self, title=None): ...
def itunes_episode_type(self, episode_type=None): ... # 'full', 'trailer', 'bonus'
def itunes_duration(self, duration=None): ...
def itunes_explicit(self, explicit=None): ...
def itunes_subtitle(self, subtitle=None): ...Dublin Core metadata elements for enhanced bibliographic information.
# Load Dublin Core extension
fg.load_extension('dc')
# Dublin Core methods (accessible via fg.dc.* and fe.dc.*)
def dc_contributor(self, contributor=None, replace=False, **kwargs): ...
def dc_coverage(self, coverage=None, replace=False, **kwargs): ...
def dc_creator(self, creator=None, replace=False, **kwargs): ...
def dc_date(self, date=None, replace=False, **kwargs): ...
def dc_description(self, description=None, replace=False, **kwargs): ...
def dc_format(self, format=None, replace=False, **kwargs): ...
def dc_identifier(self, identifier=None, replace=False, **kwargs): ...
def dc_language(self, language=None, replace=False, **kwargs): ...
def dc_publisher(self, publisher=None, replace=False, **kwargs): ...
def dc_relation(self, relation=None, replace=False, **kwargs): ...
def dc_rights(self, rights=None, replace=False, **kwargs): ...
def dc_source(self, source=None, replace=False, **kwargs): ...
def dc_subject(self, subject=None, replace=False, **kwargs): ...
def dc_title(self, title=None, replace=False, **kwargs): ...
def dc_type(self, type=None, replace=False, **kwargs): ...RSS syndication module for update scheduling and frequency information.
# Load syndication extension
fg.load_extension('syndication')
# Syndication methods (accessible via fg.syndication.*)
def update_period(self, period=None):
"""
Set update period: 'hourly', 'daily', 'weekly', 'monthly', 'yearly'
"""
def update_frequency(self, frequency=None):
"""
Set update frequency (number of times per period).
"""
def update_base(self, base=None):
"""
Set base timestamp for update calculations.
"""Geographic information support for location-aware feeds and entries.
# Load geo extension
fg.load_extension('geo')
# GeoRSS entry methods (accessible via fe.geo.*)
def point(self, point=None, replace=False): ...
def line(self, line=None, replace=False): ...
def polygon(self, polygon=None, replace=False): ...
def featuretypetag(self, featuretypetag=None, replace=False): ...
def relationshiptag(self, relationshiptag=None, replace=False): ...
def featurename(self, featurename=None, replace=False): ...
def elev(self, elev=None, replace=False): ...
def floor(self, floor=None, replace=False): ...
def radius(self, radius=None, replace=False): ...Yahoo Media RSS support for rich media content metadata.
# Load media extension
fg.load_extension('media')
# Media entry methods (accessible via fe.media.*)
def content(self, content=None, replace=False, group=None): ...
def group(self, group=None, replace=False): ...
def rating(self, rating=None, replace=False, group=None): ...
def title(self, title=None, replace=False, group=None): ...
def description(self, description=None, replace=False, group=None): ...
def thumbnail(self, thumbnail=None, replace=False, group=None): ...
def category(self, category=None, replace=False, group=None): ...
def hash(self, hash=None, replace=False, group=None): ...
def player(self, player=None, replace=False, group=None): ...
def credit(self, credit=None, replace=False, group=None): ...
def copyright(self, copyright=None, replace=False, group=None): ...
def text(self, text=None, replace=False, group=None): ...
def restriction(self, restriction=None, replace=False, group=None): ...
def community(self, community=None, replace=False, group=None): ...
def comments(self, comments=None, replace=False, group=None): ...
def embed(self, embed=None, replace=False, group=None): ...
def responses(self, responses=None, replace=False, group=None): ...
def backLinks(self, backLinks=None, replace=False, group=None): ...
def status(self, status=None, replace=False, group=None): ...
def price(self, price=None, replace=False, group=None): ...
def license(self, license=None, replace=False, group=None): ...
def peerLink(self, peerLink=None, replace=False, group=None): ...
def rights(self, rights=None, replace=False, group=None): ...
def scenes(self, scenes=None, replace=False, group=None): ...BitTorrent RSS support for torrent feed distribution.
# Load torrent extension
fg.load_extension('torrent')
# Torrent entry methods (accessible via fe.torrent.*)
def filename(self, filename=None): ...
def infohash(self, infohash=None): ...
def contentlength(self, contentlength=None): ...
def seeds(self, seeds=None): ...
def peers(self, peers=None): ...
def verified(self, verified=None): ...Base classes for creating custom extensions.
class BaseExtension:
def extend_ns(self):
"""Return namespace dictionary for XML."""
return {}
def extend_rss(self, feed):
"""Extend RSS feed XML element."""
return feed
def extend_atom(self, feed):
"""Extend ATOM feed XML element."""
return feed
class BaseEntryExtension(BaseExtension):
"""Base class for entry-level extensions."""from feedgen.feed import FeedGenerator
fg = FeedGenerator()
# ... basic feed setup ...
# Load podcast extension
fg.load_extension('podcast')
# Set podcast-specific metadata
fg.podcast.itunes_author('Podcast Host Name')
fg.podcast.itunes_category('Technology', 'Podcasting')
fg.podcast.itunes_explicit('no')
fg.podcast.itunes_complete('no')
fg.podcast.itunes_owner('Host Name', 'host@example.com')
fg.podcast.itunes_summary('A great podcast about technology topics')
fg.podcast.itunes_type('episodic')
# Add podcast episode
fe = fg.add_entry()
fe.id('http://example.com/episodes/001')
fe.title('Episode 1: Introduction')
fe.description('Our first episode introducing the podcast')
# Episode-specific podcast metadata
fe.podcast.itunes_author('Episode Author')
fe.podcast.itunes_season(1)
fe.podcast.itunes_episode(1)
fe.podcast.itunes_title('Introduction Episode')
fe.podcast.itunes_episode_type('full')
fe.podcast.itunes_duration('00:45:30')
# Add audio enclosure
fe.enclosure(
url='http://example.com/episodes/001.mp3',
length='43200000',
type='audio/mpeg'
)
# Generate RSS with podcast extension
rss_feed = fg.rss_str(pretty=True)# Load GeoRSS extension
fg.load_extension('geo')
fe = fg.add_entry()
fe.title('Local Event in San Francisco')
fe.description('A cool event happening in downtown SF')
# Add geographic location
fe.geo.point('37.7749 -122.4194') # San Francisco coordinates
fe.geo.featurename('San Francisco, CA')
fe.geo.elev('52') # Elevation in meters
# Generate feed with geographic metadata
atom_feed = fg.atom_str(pretty=True)# Load Dublin Core extension
fg.load_extension('dc')
# Add Dublin Core metadata to feed
fg.dc.dc_creator('Content Creator Name')
fg.dc.dc_publisher('Publishing Organization')
fg.dc.dc_rights('Creative Commons Attribution 4.0')
fg.dc.dc_type('Text')
# Add entry with Dublin Core metadata
fe = fg.add_entry()
fe.title('Research Article')
fe.description('Academic research on web feeds')
fe.dc.dc_subject(['web feeds', 'RSS', 'ATOM', 'syndication'])
fe.dc.dc_creator('Dr. Research Author')
fe.dc.dc_date('2023-12-01')
fe.dc.dc_type('Text.Article.Research')
# Generate with Dublin Core extensions
atom_with_dc = fg.atom_str(pretty=True)# Load media extension
fg.load_extension('media')
fe = fg.add_entry()
fe.title('Video Tutorial: Python Basics')
fe.description('Learn Python programming fundamentals')
# Add media content
fe.media.content({
'url': 'http://example.com/videos/python-basics.mp4',
'type': 'video/mp4',
'fileSize': '104857600',
'duration': '1800',
'width': '1920',
'height': '1080'
})
# Add media thumbnail
fe.media.thumbnail({
'url': 'http://example.com/thumbnails/python-basics.jpg',
'width': '320',
'height': '180'
})
# Media metadata
fe.media.title('Python Basics Tutorial')
fe.media.description('Complete beginner guide to Python programming')
fe.media.category('Education/Technology')
fe.media.credit({'name': 'Tutorial Creator', 'role': 'author'})
# Generate RSS with media extensions
rss_with_media = fg.rss_str(pretty=True)from feedgen.ext.base import BaseExtension, BaseEntryExtension
class CustomExtension(BaseExtension):
def extend_ns(self):
return {'custom': 'http://example.com/custom-namespace'}
def extend_rss(self, feed):
# Add custom elements to RSS feed
return feed
def extend_atom(self, feed):
# Add custom elements to ATOM feed
return feed
class CustomEntryExtension(BaseEntryExtension):
def custom_field(self, value=None):
if value is not None:
self.__custom_field = value
return getattr(self, '_CustomEntryExtension__custom_field', None)
# Register custom extension
fg.register_extension(
'http://example.com/custom-namespace',
extension_class_feed=CustomExtension,
extension_class_entry=CustomEntryExtension
)Install with Tessl CLI
npx tessl i tessl/pypi-feedgen