File Coverage

blib/lib/IMDB/Persons.pm
Criterion Covered Total %
statement 31 167 18.5
branch 0 62 0.0
condition 0 29 0.0
subroutine 11 27 40.7
pod 8 10 80.0
total 50 295 16.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             IMDB::Persons - Perl extension for retrieving movies persons
4             from IMDB.com
5              
6             =head1 SYNOPSIS
7              
8             use IMDB::Persons;
9              
10             #
11             # Retrieve a person information by IMDB code
12             #
13             my $person = new IMDB::Persons(crit => '0000129');
14              
15             or
16              
17             #
18             # Retrieve a person information by name
19             #
20             my $person = new IMDB::Persons(crit => 'Tom Cruise');
21              
22             or
23              
24             #
25             # Process already stored HTML page from IMDB
26             #
27             my $person = new IMDB::Persons(file => 'imdb.html');
28              
29             if($person->status) {
30             print "Name: ".$person->name."\n";
31             print "Birth Date: ".$person->date_of_birth."\n";
32             } else {
33             print "Something wrong: ".$person->error."!\n";
34             }
35              
36             =head1 DESCRIPTION
37              
38             IMDB::Persons allows to retrieve an information about
39             IMDB persons (actors, actresses, directors etc): full name,
40             photo, date and place of birth, mini bio and filmography.
41              
42             =cut
43              
44             package IMDB::Persons;
45              
46 1     1   2191 use strict;
  1         2  
  1         47  
47 1     1   7 use warnings;
  1         2  
  1         34  
48              
49 1     1   7 use Carp;
  1         2  
  1         76  
50              
51 1     1   6 use Data::Dumper;
  1         2  
  1         52  
52              
53 1     1   6 use base qw(IMDB::BaseClass);
  1         2  
  1         369  
54              
55 1         10 use fields qw( _name
56             _date_of_birth
57             _place_of_birth
58             _photo
59             _mini_bio
60             _filmography_types
61             _filmography
62             _genres
63             _plot_keywords
64 1     1   8 );
  1         2  
65              
66 1     1   129 use vars qw($VERSION %FIELDS);
  1         2  
  1         63  
67              
68 1     1   6 use constant FORCED => 1;
  1         2  
  1         72  
69 1     1   11 use constant CLASS_NAME => 'IMDB::Persons';
  1         2  
  1         49  
70 1     1   6 use constant MAIN_TAG => 'h4';
  1         2  
  1         59  
71              
72             BEGIN {
73 1     1   2466 $VERSION = '0.53';
74             }
75              
76             {
77             my %_defaults = (
78             cache => 0,
79             debug => 0,
80             error => [],
81             matched => [],
82             cache_exp => '1 h',
83             host => 'www.imdb.com',
84             query => 'name/nm',
85             search => 'find?nm=on&mx=20&q=',
86             status => 0,
87             timeout => 10,
88             user_agent => 'Mozilla/5.0',
89             );
90              
91 0     0     sub _get_default_attrs { keys %_defaults }
92             sub _get_default_value {
93 0     0     my($self, $attr) = @_;
94 0           $_defaults{$attr};
95             }
96             }
97              
98             =head1 Object Private Methods
99              
100             =over 4
101              
102             =item _init()
103              
104             Initialize a new object.
105              
106             =cut
107              
108             sub _init {
109 0     0     my CLASS_NAME $self = shift;
110 0           my %args = @_;
111              
112 0 0 0       croak "Person IMDB ID or Name should be defined!" if !$args{crit} && !$args{file};
113              
114 0           $self->SUPER::_init(%args);
115 0           my $name = $self->name();
116            
117 0 0         for my $prop (grep { /^_/ && !/^_name$/ } sort keys %FIELDS) {
  0            
118 0           ($prop) = $prop =~ /^_(.*)/;
119 0           $self->$prop();
120             }
121             }
122              
123             =item _search_person()
124              
125             Implements a logic to search IMDB persons by their names.
126              
127             =cut
128              
129             sub _search_person {
130 0     0     my CLASS_NAME $self = shift;
131              
132 0           return $self->SUPER::_search_results('\/name\/nm(\d+)', '/a');
133             }
134              
135             sub fields {
136 0     0 0   my CLASS_NAME $self = shift;
137 0           return \%FIELDS;
138             }
139              
140              
141             =back
142              
143             =head1 Object Public Methods
144              
145             =over 4
146              
147             =item name()
148              
149             Retrieve a person full name
150              
151             my $person_name = $person->name();
152              
153             =cut
154              
155             sub name {
156 0     0 1   my CLASS_NAME $self = shift;
157 0 0         if(!defined $self->{'_name'}) {
158 0           my $parser = $self->_parser(FORCED);
159              
160 0           $parser->get_tag('title');
161 0           my $title = $parser->get_text();
162 0           $title =~ s#\s*\-\s*IMDB##i;
163              
164 0           $self->_show_message("Title=$title", 'DEBUG');
165            
166             # Check if we have some search results
167 0           my $no_matches = 1;
168 0           while(my $tag = $parser->get_tag('td')) {
169 0 0 0       if($tag->[1]->{class} && $tag->[1]->{class} eq 'media_strip_header') {
170 0           $no_matches = 0;
171 0           last;
172             }
173             }
174              
175 0 0 0       if($title =~ /imdb\s+name\s+search/i && !$no_matches) {
176 0           $self->_show_message("Go to search page ...", 'DEBUG');
177 0           $title = $self->_search_person();
178             }
179            
180 0 0         $title = '' if $title =~ /IMDb Name Search/i;
181 0 0         if($title) {
182 0           $self->status(1);
183 0 0         $self->retrieve_code($parser, 'http://www.imdb.com/name/nm(\d+)') unless $self->code;
184             } else {
185 0           $self->status(0);
186 0           $self->error('Not Found');
187             }
188              
189 0           $title =~ s/^imdb\s+\-\s+//i;
190 0           $self->{'_name'} = $title;
191             }
192              
193 0           return $self->{'_name'};
194             }
195              
196             =item mini_bio()
197              
198             Returns a mini bio for specified IMDB person
199              
200             my $mini_bio = $person->mini_bio();
201              
202             =cut
203              
204             sub mini_bio {
205 0     0 1   my CLASS_NAME $self = shift;
206 0 0         if(!defined $self->{_mini_bio}) {
207 0           my $parser = $self->_parser(FORCED);
208 0           while(my $tag = $parser->get_tag('div') ) {
209 0 0 0       last if $tag->[1]->{class} && $tag->[1]->{class} eq 'infobar';
210             }
211            
212 0           my $tag = $parser->get_tag('p');
213 0           $self->{'_mini_bio'} = $parser->get_trimmed_text('a');
214             }
215 0           return $self->{'_mini_bio'};
216             }
217              
218             =item date_of_birth()
219              
220             Returns a date of birth of IMDB person in format 'day' 'month caption' 'year':
221              
222             my $d_birth = $person->date_of_birth();
223              
224             =cut
225              
226             #TODO: add date convertion in different formats.
227             sub date_of_birth {
228 0     0 1   my CLASS_NAME $self = shift;
229 0 0         if(!defined $self->{'_date_of_birth'}) {
230 0           my $parser = $self->_parser(FORCED);
231 0           while(my $tag = $parser->get_tag(MAIN_TAG)) {
232 0           my $text = $parser->get_text;
233 0 0         last if $text =~ /^Born/i;
234             }
235              
236 0           my $date = '';
237 0           my $year = '';
238 0           my $place = '';
239 0           while(my $tag = $parser->get_tag()) {
240 0 0         last if $tag->[0] eq '/td';
241            
242 0 0         if($tag->[0] eq 'a') {
243 0           my $text = $parser->get_text();
244 0 0         next unless $text;
245              
246 0           SWITCH: for($tag->[1]->{href}) {
247 0 0         /birth_monthday/i && do { $date = $text; $date =~ s#(\w+)\s(\d+)#$2 $1#; last SWITCH; };
  0            
  0            
  0            
248 0 0         /birth_year/i && do { $year = $text; last SWITCH; };
  0            
  0            
249 0 0         /birth_place/i && do { $place = $text; last SWITCH; };
  0            
  0            
250             }
251             }
252             }
253              
254 0           $self->{'_date_of_birth'} = {date => "$date $year", place => $place};
255             }
256              
257 0           return $self->{'_date_of_birth'}{'date'};
258             }
259              
260             =item place_of_birth()
261              
262             Returns a name of place of the birth
263              
264             my $place = $person->place_of_birth();
265              
266             =cut
267              
268             sub place_of_birth {
269 0     0 1   my CLASS_NAME $self = shift;
270 0           return $self->{'_date_of_birth'}{'place'};
271             }
272              
273             =item photo()
274              
275             Return a path to the person's photo
276              
277             my $photo = $person->photo();
278              
279             =cut
280              
281             sub photo {
282 0     0 1   my CLASS_NAME $self = shift;
283 0 0         if(!defined $self->{'_photo'}) {
284 0           my $tag;
285 0           my $parser = $self->_parser(FORCED);
286 0           while($tag = $parser->get_tag('img')) {
287 0 0 0       if($tag->[1]->{alt} && $tag->[1]->{alt} eq $self->name . ' Picture') {
288 0           $self->{'_photo'} = $tag->[1]{src};
289 0           last;
290             }
291             }
292              
293 0 0         $self->{'_photo'} = 'No Photo' unless $self->{'_photo'};
294             }
295              
296 0           return $self->{'_photo'};
297             }
298              
299             =item filmography()
300              
301             Returns a person's filmography as a hash of arrays with following structure:
302              
303             my $fg = $person->filmography();
304              
305             __DATA__
306             $fg = {
307             'Section' => [
308             { title => 'movie title',
309             role => 'person role',
310             year => 'year of movie production',
311             code => 'IMDB code of movie',
312             }
313             ];
314             }
315              
316             The section can be In Development, Actor, Self, Thanks, Archive Footage, Producer etc.
317              
318             =cut
319              
320             sub filmography {
321 0     0 1   my CLASS_NAME $self = shift;
322            
323 0           my $films;
324 0 0         if(!$self->{'_filmography'}) {
325 0           my $parser = $self->_parser(FORCED);
326 0           while(my $tag = $parser->get_tag('h2')) {
327              
328 0           my $text = $parser->get_text;
329 0 0 0       last if $text && $text =~ /filmography/i;
330             }
331            
332 0           my $key = 'Unknown';
333 0           while(my $tag = $parser->get_tag()) {
334            
335 0 0         last if $tag->[0] eq 'script'; # Netx section after filmography
336            
337 0 0         if($tag->[0] eq 'h5') {
338 0           my $caption = $parser->get_trimmed_text('h5', '/a');
339            
340 0 0         $key = $caption if $caption;
341 0           $key =~ s/://;
342              
343 0           $self->_show_message("FILMOGRAPHY: key=$key; caption=$caption; trimmed=".$parser->get_trimmed_text('h5', '/a'), 'DEBUG');
344             }
345            
346 0 0 0       if($tag->[0] eq 'a' && $tag->[1]->{href} && $tag->[1]{href} =~ m!title\/tt(\d+)!) {
      0        
347 0           my $title = $parser->get_text();
348 0           my $text = $parser->get_trimmed_text('br', '/li');
349            
350 0           $self->_show_message("link: $title --> $text", 'DEBUG');
351              
352 0           my $code = $1;
353 0           my($year, $role) = $text =~ m!\((\d+)\)\s.+\.+\s(.+)!;
354 0           push @{$films->{$key}}, { title => $title,
  0            
355             code => $code,
356             year => $year,
357             role => $role,
358             };
359             }
360             }
361              
362 0           $self->{'_filmography'} = $films;
363              
364             } else {
365 0           $self->_show_message("filmography defined!", 'DEBUG');
366             }
367            
368 0           return $self->{'_filmography'};
369             }
370              
371             =item genres()
372              
373             Retrieve a list of movie genres for specified person:
374              
375             my $genres = $persons->genres;
376              
377             =cut
378              
379             sub genres {
380 0     0 1   my CLASS_NAME $self = shift;
381              
382 0 0         unless($self->{_genres}) {
383 0           my @genres = $self->_get_common_array_propery('genres');
384 0           $self->{_genres} = \@genres;
385             }
386              
387 0           $self->{_genres};
388             }
389              
390             =item plot_keywords()
391              
392             Retrieve a list of keywords for movies where specified person plays:
393              
394             my $keywords = $persons->plot_keywords;
395              
396             =cut
397              
398             sub plot_keywords {
399 0     0 1   my CLASS_NAME $self = shift;
400              
401 0 0         unless($self->{_plot_keywords}) {
402 0           my @keywords = $self->_get_common_array_propery('plot keywords');
403 0           $self->{_plot_keywords} = \@keywords;
404             }
405              
406 0           $self->{_plot_keywords};
407             }
408              
409             sub _get_common_array_propery {
410 0     0     my CLASS_NAME $self = shift;
411 0   0       my $target = shift || '';
412              
413 0           my $parser = $self->_parser(FORCED);
414 0           while(my $tag = $parser->get_tag(MAIN_TAG)) {
415 0           my $text = $parser->get_text();
416 0 0         last if $text =~ /$target/i;
417             }
418            
419 0           my @res = ();
420 0           while(my $tag = $parser->get_tag('a')) {
421 0 0 0       last if $tag->[1]->{class} && $tag->[1]->{class} =~ /tn15more/i;
422 0           push @res, $parser->get_text;
423             }
424            
425 0           return @res;
426             }
427              
428             sub filmography_types {
429 0     0 0   my CLASS_NAME $self = shift;
430             }
431              
432             sub DESTROY {
433 0     0     my $self = shift;
434             }
435              
436             1;
437              
438             __END__