| blib/lib/WWW/TV/Series.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 44 | 131 | 33.5 |
| branch | 13 | 58 | 22.4 |
| condition | 4 | 14 | 28.5 |
| subroutine | 11 | 18 | 61.1 |
| pod | 12 | 12 | 100.0 |
| total | 84 | 233 | 36.0 |
| line | stmt | bran | cond | sub | pod | time | code | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | =head1 NAME | |||||||||||||
| 2 | ||||||||||||||
| 3 | WWW::TV::Series - Parse TV.com for TV Series information. | |||||||||||||
| 4 | ||||||||||||||
| 5 | =head1 SYNOPSIS | |||||||||||||
| 6 | ||||||||||||||
| 7 | use WWW::TV::Series qw(); | |||||||||||||
| 8 | my $series = WWW::TV::Series->new(name => 'Prison Break'); | |||||||||||||
| 9 | ||||||||||||||
| 10 | my @episodes = $series->episodes; | |||||||||||||
| 11 | print $series->summary; | |||||||||||||
| 12 | ||||||||||||||
| 13 | =head1 DESCRIPTION | |||||||||||||
| 14 | ||||||||||||||
| 15 | The L |
|||||||||||||
| 16 | L |
|||||||||||||
| 17 | ||||||||||||||
| 18 | =head1 METHODS | |||||||||||||
| 19 | ||||||||||||||
| 20 | =cut | |||||||||||||
| 21 | ||||||||||||||
| 22 | package WWW::TV::Series; | |||||||||||||
| 23 | 1 | 1 | 827 | use strict; | ||||||||||
| 1 | 2 | |||||||||||||
| 1 | 32 | |||||||||||||
| 24 | 1 | 1 | 5 | use warnings; | ||||||||||
| 1 | 1 | |||||||||||||
| 1 | 46 | |||||||||||||
| 25 | ||||||||||||||
| 26 | our $VERSION = '0.14'; | |||||||||||||
| 27 | ||||||||||||||
| 28 | 1 | 1 | 5 | use Carp qw(croak); | ||||||||||
| 1 | 2 | |||||||||||||
| 1 | 52 | |||||||||||||
| 29 | 1 | 1 | 4 | use LWP::UserAgent qw(); | ||||||||||
| 1 | 2 | |||||||||||||
| 1 | 1992 | |||||||||||||
| 30 | ||||||||||||||
| 31 | =head2 new | |||||||||||||
| 32 | ||||||||||||||
| 33 | The new() method is the constructor. It takes the id of the show if | |||||||||||||
| 34 | you have previously looked that up, or the name of the show which | |||||||||||||
| 35 | will be used to perform a search and the id will be taken from the | |||||||||||||
| 36 | first result. | |||||||||||||
| 37 | ||||||||||||||
| 38 | Optional parameters let you set the season number or LWP user agent. | |||||||||||||
| 39 | ||||||||||||||
| 40 | # default usage | |||||||||||||
| 41 | my $series = WWW::TV::Series->new(name => 'Prison Break'); | |||||||||||||
| 42 | my $series = WWW::TV::Series->new(id => 31635); | |||||||||||||
| 43 | ||||||||||||||
| 44 | # change user-agent from the default of "libwww-perl/#.##" | |||||||||||||
| 45 | my $series = WWW::TV::Series->new(id => 31635, agent => 'WWW::TV'); | |||||||||||||
| 46 | ||||||||||||||
| 47 | It is recommended that you lookup the show first and use the ID, | |||||||||||||
| 48 | otherwise you just don't know what will be returned. | |||||||||||||
| 49 | ||||||||||||||
| 50 | The constructor also takes a single scalar as an argument and does | |||||||||||||
| 51 | it's best to figure out what you want. But due to some shows being | |||||||||||||
| 52 | all digits as a name (e.g. "24"), use of this is not recommended | |||||||||||||
| 53 | (and in future may be deprecated). | |||||||||||||
| 54 | ||||||||||||||
| 55 | =cut | |||||||||||||
| 56 | ||||||||||||||
| 57 | sub new { | |||||||||||||
| 58 | 1 | 50 | 1 | 1 | 778 | my $class = ref $_[0] ? ref(shift) : shift; | ||||||||
| 59 | ||||||||||||||
| 60 | 1 | 3 | my %data; | |||||||||||
| 61 | ||||||||||||||
| 62 | 1 | 50 | 8 | if (@_ == 1) { | ||||||||||
| 50 | ||||||||||||||
| 63 | # If they gave us a plain scalar argument, try our best to figure out | |||||||||||||
| 64 | # what it is. Of course this dies in the arse if you want to search | |||||||||||||
| 65 | # for a program with a name like '24'. | |||||||||||||
| 66 | 0 | 0 | 0 | if ($_[0] =~ /^\d+$/) { | ||||||||||
| 67 | 0 | 0 | $data{id} = shift; | |||||||||||
| 68 | } | |||||||||||||
| 69 | else { | |||||||||||||
| 70 | 0 | 0 | $data{name} = shift; | |||||||||||
| 71 | } | |||||||||||||
| 72 | } | |||||||||||||
| 73 | elsif (scalar(@_) % 2 == 0) { | |||||||||||||
| 74 | 1 | 3 | %data = @_; | |||||||||||
| 75 | } | |||||||||||||
| 76 | ||||||||||||||
| 77 | 1 | 5 | $data{agent} = $class->agent($data{agent}); | |||||||||||
| 78 | 1 | 14 | $data{site} = $class->site ($data{site}); | |||||||||||
| 79 | ||||||||||||||
| 80 | 1 | 50 | 5 | $data{id} = $class->_get_first_search_result($data{name}, $data{agent}, $data{site}) | ||||||||||
| 81 | if exists $data{name}; | |||||||||||||
| 82 | ||||||||||||||
| 83 | 1 | 50 | 3 | croak 'No id or name given to constructor' unless exists $data{id}; | ||||||||||
| 84 | 1 | 50 | 8 | croak "Invalid id: $data{id}" unless $data{id} =~ /^\d+$/; | ||||||||||
| 85 | ||||||||||||||
| 86 | 1 | 50 | 17 | return bless { | ||||||||||
| 87 | id => $data{id}, | |||||||||||||
| 88 | _season => $data{season} || 0, | |||||||||||||
| 89 | _agent => $data{agent}, | |||||||||||||
| 90 | _site => $data{site}, | |||||||||||||
| 91 | filled => { id => 1 }, | |||||||||||||
| 92 | }, $class; | |||||||||||||
| 93 | } | |||||||||||||
| 94 | ||||||||||||||
| 95 | sub _get_first_search_result { | |||||||||||||
| 96 | 0 | 0 | 0 | my ($class, $name, $agent, $site) = @_; | ||||||||||
| 97 | ||||||||||||||
| 98 | 0 | 0 | my $ua = LWP::UserAgent->new( agent => $agent ); | |||||||||||
| 99 | 0 | 0 | my $rc = $ua->get( | |||||||||||
| 100 | sprintf("http://%s.tv.com/search.php?type=Search&stype=ajax_search&search_type=program&qs=%s", | |||||||||||||
| 101 | $site, $name) | |||||||||||||
| 102 | ); | |||||||||||||
| 103 | 0 | 0 | 0 | croak "Unable to get search results for $name" unless $rc->is_success; | ||||||||||
| 104 | ||||||||||||||
| 105 | 0 | 0 | for (split /\n/, $rc->content) { | |||||||||||
| 106 | 0 | 0 | 0 | next unless m{ 107 | 0 | 0 | return $1; | |||||||
| 108 | } | |||||||||||||
| 109 | 0 | 0 | croak 'Unable to find a show in the search results.'; | |||||||||||
| 110 | } | |||||||||||||
| 111 | ||||||||||||||
| 112 | =head2 summary | |||||||||||||
| 113 | ||||||||||||||
| 114 | Returns a string containing basic information about this series. | |||||||||||||
| 115 | ||||||||||||||
| 116 | =cut | |||||||||||||
| 117 | ||||||||||||||
| 118 | sub summary { | |||||||||||||
| 119 | 0 | 0 | 1 | 0 | my $self = shift; | |||||||||
| 120 | ||||||||||||||
| 121 | 0 | 0 | 0 | unless (exists $self->{filled}->{summary}) { | ||||||||||
| 122 | 0 | 0 | ($self->{summary}) = $self->_html =~ m{ | |||||||||||
| 123 | (.*?) | |||||||||||||
| 124 | }smx; | |||||||||||||
| 125 | 0 | 0 | $self->{summary} =~ s/ /\n/g; |
|||||||||||
| 126 | 0 | 0 | $self->{summary} =~ s/.*?<\/a>//g; | |||||||||||
| 127 | 0 | 0 | $self->{summary} =~ s/^\s*//; | |||||||||||
| 128 | 0 | 0 | $self->{summary} =~ s/\s*$//; | |||||||||||
| 129 | 0 | 0 | $self->{filled}->{summary} = 1; | |||||||||||
| 130 | } | |||||||||||||
| 131 | ||||||||||||||
| 132 | 0 | 0 | return $self->{summary}; | |||||||||||
| 133 | } | |||||||||||||
| 134 | ||||||||||||||
| 135 | =head2 genres | |||||||||||||
| 136 | ||||||||||||||
| 137 | Returns a list of all the genres that TV.com have categorised this series as. | |||||||||||||
| 138 | ||||||||||||||
| 139 | # in scalar context, returns a comma-delimited string | |||||||||||||
| 140 | my $genres = $series->genres; | |||||||||||||
| 141 | ||||||||||||||
| 142 | # in array context, returns an array | |||||||||||||
| 143 | my @genres = $series->genres; | |||||||||||||
| 144 | ||||||||||||||
| 145 | =cut | |||||||||||||
| 146 | ||||||||||||||
| 147 | sub genres { | |||||||||||||
| 148 | 0 | 0 | 1 | 0 | my $self = shift; | |||||||||
| 149 | ||||||||||||||
| 150 | 0 | 0 | 0 | unless (exists $self->{filled}->{genres}) { | ||||||||||
| 151 | 0 | 0 | my ($genres_row) = $self->_html =~ m{ | |||||||||||
| 152 | () | |||||||||||||
| 153 | }x; | |||||||||||||
| 154 | ||||||||||||||
| 155 | 0 | 0 | $self->{genres} = | |||||||||||
| 156 | join( | |||||||||||||
| 157 | ', ', | |||||||||||||
| 158 | 0 | 0 | map { s/\s*(.*?)<\/a>\s*/$1/; $_ } | |||||||||||
| 0 | 0 | |||||||||||||
| 159 | split(/,/, $genres_row) | |||||||||||||
| 160 | ); | |||||||||||||
| 161 | ||||||||||||||
| 162 | 0 | 0 | my @genres = split(/, /, $self->{genres}); | |||||||||||
| 163 | 0 | 0 | $self->{genres} = \@genres; | |||||||||||
| 164 | 0 | 0 | $self->{filled}->{genres} = 1; | |||||||||||
| 165 | } | |||||||||||||
| 166 | ||||||||||||||
| 167 | 0 | 0 | 0 | return wantarray ? @{$self->{genres}} : join(', ', @{$self->{genres}}); | ||||||||||
| 0 | 0 | |||||||||||||
| 0 | 0 | |||||||||||||
| 168 | } | |||||||||||||
| 169 | ||||||||||||||
| 170 | =head2 cast | |||||||||||||
| 171 | ||||||||||||||
| 172 | Returns a list of the cast members. The order is the same as they | |||||||||||||
| 173 | appear on TV.com, which is most likely nothing to go by, but | |||||||||||||
| 174 | in most cases is the main cast order. | |||||||||||||
| 175 | ||||||||||||||
| 176 | # in scalar context, returns a comma-delimited string | |||||||||||||
| 177 | my $cast = $series->cast; | |||||||||||||
| 178 | ||||||||||||||
| 179 | # in array context, returns an array | |||||||||||||
| 180 | my @cast = $series->cast; | |||||||||||||
| 181 | ||||||||||||||
| 182 | =cut | |||||||||||||
| 183 | ||||||||||||||
| 184 | sub cast { | |||||||||||||
| 185 | 0 | 0 | 1 | 0 | my $self = shift; | |||||||||
| 186 | ||||||||||||||
| 187 | 0 | 0 | 0 | unless (exists $self->{filled}->{cast}) { | ||||||||||
| 188 | 0 | 0 | my ($cast_line) = $self->_html =~ m{
|
|||||||||||
| 189 | 0 | 0 | my @cast; | |||||||||||
| 190 | 0 | 0 | for my $person (split /<\/li>/, $cast_line) { | |||||||||||
| 191 | 0 | 0 | 0 | next unless $person =~ m{(.*?)}; | ||||||||||
| 192 | 0 | 0 | push @cast, $1; | |||||||||||
| 193 | } | |||||||||||||
| 194 | 0 | 0 | $self->{cast} = \@cast; | |||||||||||
| 195 | 0 | 0 | $self->{filled}->{cast} = 1; | |||||||||||
| 196 | } | |||||||||||||
| 197 | ||||||||||||||
| 198 | 0 | 0 | 0 | return wantarray ? @{$self->{cast}} : join(', ', @{$self->{cast}}); | ||||||||||
| 0 | 0 | |||||||||||||
| 0 | 0 | |||||||||||||
| 199 | } | |||||||||||||
| 200 | ||||||||||||||
| 201 | =head2 name | |||||||||||||
| 202 | ||||||||||||||
| 203 | Returns a string containing the name of the series. | |||||||||||||
| 204 | ||||||||||||||
| 205 | =cut | |||||||||||||
| 206 | ||||||||||||||
| 207 | sub name { | |||||||||||||
| 208 | 1 | 1 | 1 | 3 | my $self = shift; | |||||||||
| 209 | ||||||||||||||
| 210 | 1 | 50 | 9 | unless (exists $self->{filled}->{name}) { | ||||||||||
| 211 | 1 | 3 | ($self->{name}) = $self->_html =~ m{ | |||||||||||
| 212 | \n\n? |
|||||||||||||
| 213 | (.*?):\s*Summary\n |
|||||||||||||
| 214 | }x; | |||||||||||||
| 215 | 0 | 0 | $self->{filled}->{name} = 1; | |||||||||||
| 216 | } | |||||||||||||
| 217 | ||||||||||||||
| 218 | 0 | 0 | return $self->{name}; | |||||||||||
| 219 | } | |||||||||||||
| 220 | ||||||||||||||
| 221 | =head2 image | |||||||||||||
| 222 | ||||||||||||||
| 223 | Returns the url of an image that can be used to identify this series. | |||||||||||||
| 224 | ||||||||||||||
| 225 | =cut | |||||||||||||
| 226 | ||||||||||||||
| 227 | sub image { | |||||||||||||
| 228 | 0 | 0 | 1 | 0 | my $self = shift; | |||||||||
| 229 | ||||||||||||||
| 230 | 0 | 0 | 0 | unless (exists $self->{filled}->{image}) { | ||||||||||
| 231 | 0 | 0 | ($self->{image}) = $self->_html =~ m{ | |||||||||||
| 232 | \s*\n |
|||||||||||||
| 233 | (?:\n)? | |||||||||||||
| 234 | \s* | |||||||||||||
| 235 | }x; | |||||||||||||
| 236 | 0 | 0 | $self->{filled}->{image} = 1; | |||||||||||
| 237 | } | |||||||||||||
| 238 | ||||||||||||||
| 239 | 0 | 0 | return $self->{image}; | |||||||||||
| 240 | } | |||||||||||||
| 241 | ||||||||||||||
| 242 | =head2 episodes | |||||||||||||
| 243 | ||||||||||||||
| 244 | Returns an array of L |
|||||||||||||
| 245 | ||||||||||||||
| 246 | # All episodes | |||||||||||||
| 247 | my @episodes = $series->episodes; | |||||||||||||
| 248 | ||||||||||||||
| 249 | # Episodes for season 2 only | |||||||||||||
| 250 | my @episodes = $series->episodes( season => 2 ); | |||||||||||||
| 251 | ||||||||||||||
| 252 | =cut | |||||||||||||
| 253 | ||||||||||||||
| 254 | sub episodes { | |||||||||||||
| 255 | 0 | 0 | 1 | 0 | my $self = shift; | |||||||||
| 256 | ||||||||||||||
| 257 | 0 | 0 | my %args; | |||||||||||
| 258 | 0 | 0 | 0 | if (scalar(@_) % 2 == 0) { | ||||||||||
| 259 | 0 | 0 | %args = @_; | |||||||||||
| 260 | } | |||||||||||||
| 261 | ||||||||||||||
| 262 | 0 | 0 | 0 | my $season = exists $args{season} ? $args{season} : $self->{_season}; | ||||||||||
| 263 | ||||||||||||||
| 264 | 0 | 0 | 0 | unless ($self->{filled}->{episodes}->{$season}) { | ||||||||||
| 265 | 0 | 0 | my $ua = LWP::UserAgent->new(agent => $self->agent); | |||||||||||
| 266 | 0 | 0 | my $rc = $ua->get($self->episode_url($season)); | |||||||||||
| 267 | 0 | 0 | 0 | croak sprintf('Unable to fetch episodes for series %d, season %d', $self->id, $season) | ||||||||||
| 268 | unless $rc->is_success; | |||||||||||||
| 269 | ||||||||||||||
| 270 | 0 | 0 | require WWW::TV::Episode; | |||||||||||
| 271 | 0 | 0 | my ($episode_line) = $rc->content =~ m{ | (.*)\n}; | ||||||||||
| 272 | 0 | 0 | my @episodes = | |||||||||||
| 273 | 0 | 0 | grep { defined } | |||||||||||
| 274 | map { | |||||||||||||
| 275 | 0 | 0 | my $ep; | |||||||||||
| 276 | 0 | 0 | 0 | if (m#(.*?)#) { | ||||||||||
| 277 | 0 | 0 | $ep = WWW::TV::Episode->new(id => $1, name => $2, agent => $self->agent); | |||||||||||
| 278 | } | |||||||||||||
| 279 | 0 | 0 | $ep; | |||||||||||
| 280 | } split /<\/div>/, $episode_line; | |||||||||||||
| 281 | ||||||||||||||
| 282 | 0 | 0 | $self->{episodes}->{$season} = \@episodes; | |||||||||||
| 283 | 0 | 0 | $self->{filled}->{episodes}->{$season} = 1; | |||||||||||
| 284 | } | |||||||||||||
| 285 | ||||||||||||||
| 286 | 0 | 0 | return @{$self->{episodes}->{$season}}; | |||||||||||
| 0 | 0 | |||||||||||||
| 287 | } | |||||||||||||
| 288 | ||||||||||||||
| 289 | sub _html { | |||||||||||||
| 290 | 1 | 1 | 2 | my $self = shift; | ||||||||||
| 291 | ||||||||||||||
| 292 | 1 | 50 | 5 | unless ($self->{filled}->{html}) { | ||||||||||
| 293 | 1 | 4 | my $ua = LWP::UserAgent->new (agent => $self->agent); | |||||||||||
| 294 | 1 | 3344 | my $rc = $ua->get($self->url); | |||||||||||
| 295 | 1 | 50 | 1271456 | croak sprintf('Unable to fetch page for series %s', $self->id) | ||||||||||
| 296 | unless $rc->is_success; | |||||||||||||
| 297 | ||||||||||||||
| 298 | 0 | 0 | $self->{html} = | |||||||||||
| 299 | join( | |||||||||||||
| 300 | "\n", | |||||||||||||
| 301 | 0 | 0 | map { s/^\s*//; s/\s*$//; $_ } | |||||||||||
| 0 | 0 | |||||||||||||
| 0 | 0 | |||||||||||||
| 302 | split /\n/, $rc->content | |||||||||||||
| 303 | ); | |||||||||||||
| 304 | ||||||||||||||
| 305 | 0 | 0 | $self->{filled}->{html} = 1; | |||||||||||
| 306 | } | |||||||||||||
| 307 | ||||||||||||||
| 308 | 0 | 0 | return $self->{html}; | |||||||||||
| 309 | } | |||||||||||||
| 310 | ||||||||||||||
| 311 | =head2 id | |||||||||||||
| 312 | ||||||||||||||
| 313 | The ID of this series, according to TV.com | |||||||||||||
| 314 | ||||||||||||||
| 315 | =cut | |||||||||||||
| 316 | ||||||||||||||
| 317 | sub id { | |||||||||||||
| 318 | 2 | 2 | 1 | 19 | my $self = shift; | |||||||||
| 319 | ||||||||||||||
| 320 | 2 | 240 | return $self->{id}; | |||||||||||
| 321 | } | |||||||||||||
| 322 | ||||||||||||||
| 323 | =head2 agent ($value) | |||||||||||||
| 324 | ||||||||||||||
| 325 | Returns the current user agent setting, and sets to $value if provided. | |||||||||||||
| 326 | ||||||||||||||
| 327 | =cut | |||||||||||||
| 328 | ||||||||||||||
| 329 | sub agent { | |||||||||||||
| 330 | 2 | 2 | 1 | 4 | my $self = shift; # may be called as $self or $class | |||||||||
| 331 | 2 | 4 | my $value = shift; | |||||||||||
| 332 | ||||||||||||||
| 333 | 2 | 100 | 6 | if (ref $self) { | ||||||||||
| 334 | 1 | 50 | 6 | if (defined $value) { | ||||||||||
| 335 | 0 | 0 | $self->{_agent} = $value; | |||||||||||
| 336 | } | |||||||||||||
| 337 | 1 | 33 | 11 | return ($self->{_agent} || LWP::UserAgent::_agent); | ||||||||||
| 338 | } else { | |||||||||||||
| 339 | 1 | 33 | 6 | return ($value || LWP::UserAgent::_agent); | ||||||||||
| 340 | } | |||||||||||||
| 341 | } | |||||||||||||
| 342 | ||||||||||||||
| 343 | =head2 site ($value) | |||||||||||||
| 344 | ||||||||||||||
| 345 | Returns the current mirror site setting, and sets to $value if provided. | |||||||||||||
| 346 | ||||||||||||||
| 347 | Default site is "www"; other options include: us, uk, au | |||||||||||||
| 348 | ||||||||||||||
| 349 | =cut | |||||||||||||
| 350 | ||||||||||||||
| 351 | sub site { | |||||||||||||
| 352 | 1 | 1 | 1 | 1 | my $self = shift; # may be called as $self or $class | |||||||||
| 353 | 1 | 2 | my $value = shift; | |||||||||||
| 354 | ||||||||||||||
| 355 | 1 | 50 | 4 | if (ref $self) { | ||||||||||
| 356 | 0 | 0 | 0 | if (defined $value) { | ||||||||||
| 357 | 0 | 0 | 0 | if ($value =~ m#^(au|uk|us|www|)$#i) { | ||||||||||
| 358 | 0 | 0 | $self->{_site} = $value; | |||||||||||
| 359 | } else { | |||||||||||||
| 360 | 0 | 0 | warn "Ignoring unknown site value: [$value]\n"; | |||||||||||
| 361 | } | |||||||||||||
| 362 | } | |||||||||||||
| 363 | 0 | 0 | 0 | return ($self->{_site} || 'www'); | ||||||||||
| 364 | } else { | |||||||||||||
| 365 | 1 | 50 | 8 | return ($value || 'www'); | ||||||||||
| 366 | } | |||||||||||||
| 367 | } | |||||||||||||
| 368 | ||||||||||||||
| 369 | =head2 url | |||||||||||||
| 370 | ||||||||||||||
| 371 | Returns the url that was used to create this object. | |||||||||||||
| 372 | ||||||||||||||
| 373 | =cut | |||||||||||||
| 374 | ||||||||||||||
| 375 | sub url { | |||||||||||||
| 376 | 1 | 1 | 1 | 3 | my $self = shift; | |||||||||
| 377 | ||||||||||||||
| 378 | 1 | 4 | return sprintf('http://%s.tv.com/show/%d/summary.html', $self->{_site}, $self->id); | |||||||||||
| 379 | } | |||||||||||||
| 380 | ||||||||||||||
| 381 | =head2 episode_url ($season) | |||||||||||||
| 382 | ||||||||||||||
| 383 | Returns the url that is used to get the episode listings for this | |||||||||||||
| 384 | series. | |||||||||||||
| 385 | ||||||||||||||
| 386 | $season is optional ; defaults to "all" | |||||||||||||
| 387 | ||||||||||||||
| 388 | =cut | |||||||||||||
| 389 | ||||||||||||||
| 390 | sub episode_url { | |||||||||||||
| 391 | 0 | 0 | 1 | my $self = shift; | ||||||||||
| 392 | 0 | 0 | my $season = shift || 'All'; # 0 == ALL seasons | |||||||||||
| 393 | ||||||||||||||
| 394 | 0 | return sprintf( | ||||||||||||
| 395 | 'http://%s.tv.com/show/%d/episode_listings.html?season=%s', | |||||||||||||
| 396 | $self->{_site}, $self->id, $season | |||||||||||||
| 397 | ); | |||||||||||||
| 398 | } | |||||||||||||
| 399 | ||||||||||||||
| 400 | 1; | |||||||||||||
| 401 | ||||||||||||||
| 402 | __END__ |