
It seems that a few of you are using or trying to use the XML::RSS::Podcast
module I published on this blog. As was mentioned there,
modern version of XML::RSS do not support the encode function required for
generating well-formed XML documents.
That’s all changed thanks to Rohan Carly. [Add your thanks to Rohan in the comments.]
I present the complete version of XML::RSS::Podcast below. If you find this module
useful, I’ll attempt to create a CPAN module for it.
package XML::RSS::Podcast; use XML::RSS; use HTML::Entities qw[encode_entities_numeric encode_entities]; @XML::RSS::Podcast::ISA = qw[XML::RSS]; our $VERSION = q[1.1]; # encode by Rohan Carly # Stolen from XML::RSS::Private::Output::Base; sub encode { my ($self,$text) = @_; return unless defined($text); my $encoded_text = ''; while ($text =~ s,(.*?)(<!\[CDATA\[.*?\]\]>),,s) { # we use &named; entities here because it's HTML $encoded_text .= encode_entities($1) . $2; } # we use numeric entities here because it's XML $encoded_text .= encode_entities_numeric($text); return $encoded_text; }; sub as_string { my $self = shift; return $self->as_podcast_rss; } sub as_podcast_rss { my $self = shift; my $enc = $self->{encoding}; my $output = qq[<?xml version="1.0" encoding="$enc"?> <rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">]; $output .= $self->podcast_start_channel; for my $i (@{$self->{items}}) { $output .= $self->podcast_item($i); } $output .= $self->podcast_end_channel; return $output .= "\n</rss>\n"; } sub podcast_start_channel { my $self = shift; my @fields = qw[ttl title description link language pubDate lastBuildDate creator webMaster copyright ]; my @image_fields = qw[title url description link width height]; my @itunes_fields = qw[subtitle author summary image explicit]; # Thanks Rohan my $output = "<channel>\n"; for my $f (@fields) { if (defined($self->{channel}->{$f})) { my $s = $self->encode($self->{channel}->{$f}); $output .= "\t<$f>$s</$f>\n"; } } my $seen_image = 0; for my $f (@image_fields) { if (defined($self->{image}->{$f})) { unless ($seen_image) { $output .= "\t<image>\n"; $seen_image = 1; } my $s = $self->encode($self->{image}->{$f}); $output .= "\t\t<$f>$s</$f>\n"; } } if ($seen_image) { $output .= "\t</image>\n"; } # Owner name/email not handled for my $f (@itunes_fields) { if (defined($self->{channel}->{itunes}->{$f})) { my $s=$self->encode($self->{channel}->{itunes}->{$f}); $output .= "\t<itunes:$f>$s</itunes:$f>\n"; } } # Rohan's sub-cat handling code # Expects an array: [category, sub-category] if (ref $self->{channel}->{itunes}->{category} eq 'ARRAY') { my $major = $self->encode(${$self->{channel}->{itunes}->{category}}[0]); my $minor = $self->encode(${$self->{channel}->{itunes}->{category}}[1]); $output .= qq[\t<itunes:category text="$major">\n]; $output .= qq[\t\t<itunes:category text="$minor">\n]; $output .= qq[\t\t</itunes:category>\n]; $output .= qq[\t</itunes:category>\n]; } return $output . "\n"; } sub podcast_end_channel { return "</channel>\n"; } sub podcast_item { my $self = shift; my $item = shift; my @fields = qw[title guid pubDate description link]; my @itunes_fields = qw[author subtitle summary duration keywords explicit]; my $output = "\t<item>\n"; for my $f (@fields) { if (defined($item->{$f})) { $s = $self->encode($item->{$f}); my $perma = ""; if ($f eq "guid") { $perma = qq[isPermaLink="false"]; } $output .= "\t\t<$f$perma>$s</$f>\n"; } } if (ref $item->{enclosure}) { $output .= "<enclosure"; for my $f (qw[url length type]) { if (defined $item->{enclosure}->{$f}) { $output .= sprintf("$f=%s", $self->encode($item->{enclosure}->{$f})); } } $output .= "/>"; } for my $f (@itunes_fields) { if (defined $item->{itunes}->{$f}) { $s = $self->encode($item->{itunes}->{$f}); $output .= "\t\t<itunes:$f>$s</itunes:$f>\n"; } } return $output .= "\t</item>\n"; } 1;
For an example of usage, please see the previous post cited above.
Report bugs in the comments or through email. Thanks.