File Coverage

blib/lib/WebService/TVDB/Series.pm
Criterion Covered Total %
statement 34 36 94.4
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 46 48 95.8


line stmt bran cond sub pod time code
1 1     1   5 use strict;
  1         2  
  1         33  
2 1     1   6 use warnings;
  1         9  
  1         45  
3              
4             package WebService::TVDB::Series;
5             {
6             $WebService::TVDB::Series::VERSION = '1.133200';
7             }
8              
9             # ABSTRACT: Represents a TV Series
10              
11 1     1   479 use WebService::TVDB::Actor;
  1         2  
  1         8  
12 1     1   589 use WebService::TVDB::Banner;
  1         3  
  1         9  
13 1     1   3563 use WebService::TVDB::Episode;
  1         4  
  1         10  
14 1     1   561 use WebService::TVDB::Util qw(pipes_to_array);
  1         3  
  1         88  
15              
16 1     1   5 use Carp qw(carp);
  1         3  
  1         44  
17 1     1   5 use File::Basename qw(dirname);
  1         3  
  1         76  
18 1     1   6 use File::Path qw(mkpath);
  1         1  
  1         68  
19 1     1   1100 use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
  1         88016  
  1         162  
20 1     1   6799 use LWP::Simple ();
  1         77039  
  1         41  
21 1     1   2035 use XML::Simple qw(:strict);
  0            
  0            
22              
23             # Assessors
24             # alphabetically, case insensitive
25             # First section from http://thetvdb.com/api/GetSeries.php?seriesname=...
26             # Second section from
27             # Third section are WebService::TVDB:: objects
28             # Forth section are API values
29             use Object::Tiny qw(
30             banner
31             FirstAired
32             id
33             IMDB_ID
34             language
35             Overview
36             seriesid
37             SeriesName
38             zap2it_id
39              
40             added
41             addedBy
42             Actors
43             Airs_DayOfWeek
44             Airs_Time
45             ContentRating
46             fanart
47             Genre
48             Language
49             lastupdated
50             Network
51             NetworkID
52             poster
53             Rating
54             RatingCount
55             Runtime
56             SeriesID
57             Status
58              
59             actors
60             banners
61             episodes
62              
63             _api_key
64             _api_language
65             _max_retries
66             );
67              
68             # the url for full series data
69             use constant URL => 'http://thetvdb.com/api/%s/series/%s/all/%s.zip';
70              
71             # the local path for full series data
72             use constant CACHE_PATH => '%s/.tvdbcache/series/%s/all/%s.zip';
73              
74             # xml files in the zip
75             use constant ACTORS_XML_FILE => 'actors.xml';
76             use constant BANNERS_XML_FILE => 'banners.xml';
77              
78             sub fetch {
79             my ($self) = @_;
80              
81             my $url = $self->_url;
82             my $cache_path = $self->_cache_path;
83             $cache_path =~ /\A(.*)\z/s or die;
84             $cache_path = $1; # ensure its untainted
85             my $dir = dirname($cache_path);
86             -e $dir or mkpath($dir) or die 'could not create ' . $dir;
87              
88             my $agent = $LWP::Simple::ua->agent;
89             $LWP::Simple::ua->agent("WebService::TVDB/$WebService::TVDB::VERSION");
90              
91             # get the zip
92             my $res = LWP::Simple::mirror( $url, $cache_path );
93             my $retries = 0;
94             until ( $res == LWP::Simple::RC_NOT_MODIFIED
95             || LWP::Simple::is_success($res)
96             || $retries == $self->_max_retries )
97             {
98             carp "failed to get URL $url: $res - retrying";
99              
100             # TODO configurable wait time
101             sleep 1;
102             $res = LWP::Simple::mirror( $url, $cache_path );
103              
104             $retries++;
105             }
106             $LWP::Simple::ua->agent($agent);
107             if ( $retries == $self->_max_retries ) {
108             die "failed to get URL $url after $retries retries. Aborting.";
109             }
110             my $zip = Archive::Zip->new();
111             unless ( $zip->read($cache_path) == AZ_OK ) {
112             die 'could not read zip at ' . $cache_path;
113             }
114              
115             # parse the xml files
116             my $status;
117             my $xml;
118             my $parsed_xml;
119              
120             my $series_xml_file = $self->language . '.xml';
121             ( $xml, $status ) = $zip->contents($series_xml_file);
122             unless ( $status == AZ_OK ) {
123             die 'could not read ' . $series_xml_file;
124             }
125             $parsed_xml = XML::Simple::XMLin(
126             $xml,
127             ForceArray => [ 'Data', 'Episode' ],
128             KeyAttr => 'Data',
129             SuppressEmpty => 1
130             );
131             $self->_parse_series_data($parsed_xml);
132              
133             ( $xml, $status ) = $zip->contents(ACTORS_XML_FILE);
134             unless ( $status == AZ_OK ) {
135             die 'could not read ' . ACTORS_XML_FILE;
136             }
137             $parsed_xml = XML::Simple::XMLin(
138             $xml,
139             ForceArray => ['Actor'],
140             KeyAttr => 'Actor',
141             SuppressEmpty => 1
142             );
143             $self->_parse_actors($parsed_xml);
144              
145             ( $xml, $status ) = $zip->contents(BANNERS_XML_FILE);
146             unless ( $status == AZ_OK ) {
147             die 'could not read ' . BANNERS_XML_FILE;
148             }
149             $parsed_xml = XML::Simple::XMLin(
150             $xml,
151             ForceArray => ['Banner'],
152             KeyAttr => 'Banner',
153             SuppressEmpty => 1
154             );
155             $self->_parse_banners($parsed_xml);
156             }
157              
158             sub get_episode {
159             my ( $self, $season_number, $episode_number ) = @_;
160              
161             for my $episode ( @{ $self->episodes } ) {
162             if ( $episode->SeasonNumber eq $season_number ) {
163             if ( $episode->EpisodeNumber eq $episode_number ) {
164             return $episode;
165             }
166             }
167              
168             }
169             }
170              
171             # generates the url for full series data
172             sub _url {
173             my ($self) = @_;
174             return sprintf( URL,
175             $self->_api_key, $self->seriesid,
176             $self->_api_language->{abbreviation} );
177             }
178              
179             # generates the local path for full series data
180             # TODO configurable path
181             sub _cache_path {
182             my ($self) = @_;
183             require File::HomeDir;
184             return sprintf( CACHE_PATH,
185             File::HomeDir->my_home, $self->seriesid,
186             $self->_api_language->{abbreviation} );
187             }
188              
189             # parse .xml
190             sub _parse_series_data {
191             my ( $self, $xml ) = @_;
192              
193             # populate extra Series data
194             while ( my ( $key, $value ) = each( %{ $xml->{Series} } ) ) {
195             if ( $key eq 'Genre' || $key eq 'Actors' ) {
196             $self->{$key} = pipes_to_array($value);
197             }
198             else {
199             $self->{$key} = $value;
200             }
201             }
202              
203             # populate Episodes, if they exist
204             my @episodes;
205             if ( $xml->{Episode} ) {
206             for ( @{ $xml->{Episode} } ) {
207             push @episodes, WebService::TVDB::Episode->new( %{$_} );
208              
209             }
210             }
211             $self->{episodes} = \@episodes;
212             return $self->{episodes};
213             }
214              
215             # parse actors.xml
216             sub _parse_actors {
217             my ( $self, $xml ) = @_;
218              
219             my @actors;
220             for ( @{ $xml->{Actor} } ) {
221             push @actors, WebService::TVDB::Actor->new( %{$_} );
222              
223             }
224             $self->{actors} = \@actors;
225             return $self->{actors};
226             }
227              
228             # parse banners.xml
229             sub _parse_banners {
230             my ( $self, $xml ) = @_;
231              
232             my @banners;
233             for ( @{ $xml->{Banner} } ) {
234             push @banners, WebService::TVDB::Banner->new( %{$_} );
235              
236             }
237             $self->{banners} = \@banners;
238             return $self->{banners};
239             }
240              
241             1;
242              
243             __END__