File Coverage

lib/MP3/PodcastFetch/Feed.pm
Criterion Covered Total %
statement 105 111 94.5
branch 38 52 73.0
condition 3 6 50.0
subroutine 21 22 95.4
pod 15 16 93.7
total 182 207 87.9


line stmt bran cond sub pod time code
1             package MP3::PodcastFetch::Feed;
2              
3 1     1   5 use strict;
  1         1  
  1         28  
4 1     1   4 use base 'MP3::PodcastFetch::XML::SimpleParser';
  1         2  
  1         608  
5 1     1   588 use MP3::PodcastFetch::Feed::Channel;
  1         3  
  1         40  
6 1     1   613 use MP3::PodcastFetch::Feed::Item;
  1         4  
  1         38  
7              
8 1     1   1214 use LWP::UserAgent;
  1         45936  
  1         1031  
9              
10             =head1 NAME
11              
12             MP3::PodcastFetch:Feed -- Fetch and parse an RSS file
13              
14             =head1 SYNOPSIS
15              
16             use MP3::PodcastFetch::Feed;
17             my $feed = MP3::PodcastFetch::Feed->new('http://www.npr.org/rss/podcast.php?id=500001');
18              
19             $feed->timeout(100);
20             my @channels = $feed->read_feed;
21             for my $c (@channels) {
22             print "Title = ",$c->title,"\n";
23             }
24              
25             =head1 DESCRIPTION
26              
27             This package provides convenient downloading and parsing of the
28             subscription information in an RSS feed URL. It was written to support
29             the podcast_fetch.pl script.
30              
31             To use it, create an MP3::PodcastFetch:Feed object with the desired
32             RSS URL. Set additional parameters such as timeout values. Then call
33             the read_feed() method to get a list of
34             MP3::PodcastFetch::Feed::Channel objects that contain various bits of
35             information about the podcast subscription.
36              
37             Internally, it is a subclass of MP3::PodcastFetch::XML::SimpleParser,
38             a very straightforward sax-based XML parser.
39              
40             =head2 METHODS
41              
42             This module implements the following methods:
43              
44             =over 4
45              
46             =cut
47              
48             =item $feed = MP3::PodcastFetch::Feed->new($url)
49              
50             Create a new MP3::PodcastFetch::Feed object pointing to the indicated
51             URL. The default fetch timeout is set to 10s.
52              
53             =cut
54              
55             sub new {
56 4     4 1 7 my $class = shift;
57 4         7 my $url = shift;
58 4         30 my $self = $class->SUPER::new();
59 4         15 $self->url($url);
60 4         16 $self->timeout(10);
61 4         17 $self;
62             }
63              
64             =item $url = $feed->url([$new_url])
65              
66             Get or set the RSS URL.
67              
68             =cut
69              
70             sub url {
71 8     8 1 10 my $self = shift;
72 8         14 my $d = $self->{url};
73 8 100       31 $self->{url} = shift if @_;
74 8         20 $d;
75             }
76              
77             =item $error = $feed->errstr([$new_error])
78              
79             Get or set an error message. Call errstr() after an unsuccessful fetch
80             to find out what went wrong.
81              
82             =cut
83              
84             sub errstr {
85 0     0 1 0 my $self = shift;
86 0         0 my $d = $self->{error};
87 0 0       0 $self->{error} = shift if @_;
88 0         0 $d;
89             }
90              
91             =item $timeout = $feed->timeout([$new_timeout])
92              
93             Get or set the timeout for the RSS XML file fetch operation. The
94             default timeout is 10s, meaning that the module will wait a maximum of
95             10 seconds to get a response from the remote server.
96              
97             =cut
98              
99             sub timeout {
100 12     12 1 16 my $self = shift;
101 12         18 my $d = $self->{timeout};
102 12 100       34 $self->{timeout} = shift if @_;
103 12         28 $d;
104             }
105              
106             =item $env_proxy = $feed->env_proxy([$env_proxy])
107              
108             Get or set the proxy usage for the RSS XML file fetch operation. The
109             default is without proxy,
110              
111             =cut
112              
113             sub env_proxy {
114 8     8 1 20 my $self = shift;
115 8         16 my $d = $self->{env_proxy};
116 8 100       32 $self->{env_proxy} = shift if @_;
117 8         19 $d;
118             }
119              
120             =item @channels = $feed->read_feed()
121              
122             This is the main workhorse method of the module. It tries to read and
123             parse the RSS file at the previously-indicated URL. If successful, it
124             returns a list of MP3::PodcastFetch::Feed::Channel objects containing
125             information about each channel and the podcast episodes contained
126             within them. If unsuccessful, it returns an empty list. You can use
127             the errstr() method to find out what went wrong.
128              
129             =cut
130              
131             sub read_feed {
132 4     4 1 6 my $self = shift;
133 4 50       14 my $url = $self->url or return;
134 4         40 my $ua = LWP::UserAgent->new;
135 4 50       4588 $ua->env_proxy if $self->env_proxy;
136 4         14 $ua->timeout($self->timeout);
137 4     4   77 my $response = $ua->get($url,':content_cb' => sub { $self->parse($_[0]) } );
  4         40191  
138 4         1030 $self->eof;
139 4 50       16 unless ($response->is_success) {
140 0         0 $self->errstr($response->status_line);
141 0         0 return;
142             }
143 4         47 return $self->results;
144             }
145              
146             =back
147              
148             =head2 Internal methods
149              
150             The following methods are used during the parse of the downloaded RSS
151             file. See MP3::PodcastFetch::XML::SimpleParser for a description of
152             how they work.
153              
154             =over 4
155              
156             =item t_channel
157              
158             =cut
159              
160             sub t_channel {
161 8     8 1 13 my $self = shift;
162 8         8 my $attrs = shift;
163 8 100       15 if ($attrs) { # tag is starting
164 4         6 push @{$self->{current}},MP3::PodcastFetch::Feed::Channel->new;
  4         138  
165 4         224 return;
166             } else {
167 4         6 $self->add_object(pop @{$self->{current}});
  4         25  
168             }
169             }
170              
171             =item t_item
172              
173             =cut
174              
175             sub t_item {
176 24     24 1 29 my $self = shift;
177 24         26 my $attrs = shift;
178 24 100       35 if ($attrs) { # tag is starting
179 12         12 push @{$self->{current}},MP3::PodcastFetch::Feed::Item->new;
  12         285  
180 12         603 return;
181             } else {
182 12         14 my $item =pop @{$self->{current}};
  12         23  
183 12 50       35 my $channel = $self->{current}[-1] or return;
184 12         50 $channel->add_item($item);
185             }
186             }
187              
188             =item t_title
189              
190             =cut
191              
192             sub t_title {
193 32     32 1 35 my $self = shift;
194 32         34 my $attrs = shift;
195 32 100       119 unless ($attrs) { # tag is ending
196 16 50       45 my $item = $self->{current}[-1] or return;
197 16         40 $item->title($self->char_data);
198             }
199             }
200              
201             =item t_description
202              
203             =cut
204              
205             sub t_description {
206 32     32 1 38 my $self = shift;
207 32         37 my $attrs = shift;
208 32 100       125 unless ($attrs) { # tag is ending
209 16 50       44 my $item = $self->{current}[-1] or return;
210 16         45 $item->description($self->char_data);
211             }
212             }
213              
214             =item t_guid
215              
216             =cut
217              
218             sub t_guid {
219 24     24 1 26 my $self = shift;
220 24         26 my $attrs = shift;
221 24 100       94 unless ($attrs) { # tag is ending
222 12 50       31 my $item = $self->{current}[-1] or return;
223 12         29 $item->guid($self->char_data);
224             }
225             }
226              
227             =item t_pubDate
228              
229             =cut
230              
231             sub t_pubDate {
232 24     24 1 28 my $self = shift;
233 24         29 my $attrs = shift;
234 24 100       96 unless ($attrs) {
235 12 50       29 my $item = $self->{current}[-1] or return;
236 12         32 $item->pubDate($self->char_data);
237             }
238             }
239              
240             =item t_link
241              
242             =cut
243              
244             sub t_link {
245 32     32 1 40 my $self = shift;
246 32         31 my $attrs = shift;
247 32 100       124 unless ($attrs) {
248 16 50       39 my $item = $self->{current}[-1] or return;
249 16         38 $item->link($self->char_data);
250             }
251             }
252              
253             =item t_author
254              
255             =cut
256              
257             sub t_author {
258 8     8 1 10 my $self = shift;
259 8         10 my $attrs = shift;
260 8 100       36 unless ($attrs) {
261 4 50       14 my $item = $self->{current}[-1] or return;
262 4         10 $item->author($self->char_data);
263             }
264             }
265              
266             =item t_itunes_author
267              
268             =cut
269              
270             *t_itunes_author = \&t_author;
271              
272             sub t_itunes_duration {
273 24     24 0 26 my $self = shift;
274 24         35 my $attrs = shift;
275 24 100       93 unless ($attrs) {
276 12 50       30 my $item = $self->{current}[-1] or return;
277 12         30 my @time = split ':',$self->char_data;
278 12   50     37 my $secs = pop @time || 0;
279 12   50     26 my $mins = pop @time || 0;
280 12   50     41 my $hrs = pop @time || 0;
281 12         274 $item->duration($hrs*60*60+$mins*60+$secs);
282             }
283             }
284              
285             =item t_enclosure
286              
287             =cut
288              
289             sub t_enclosure {
290 24     24 1 26 my $self = shift;
291 24         24 my $attrs = shift;
292 24 100       94 if ($attrs) {
293 12 50       37 my $item = $self->{current}[-1] or return;
294 12         249 $item->url($attrs->{url});
295             }
296             }
297              
298             =back
299              
300             =cut
301              
302             1;
303              
304             __END__