line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# $Id: MoviePosterDB.pm 6486 2011-06-13 13:42:02Z chris $ |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
WebService::MoviePosterDB - OO Perl interface to the movie poster database MoviePosterDB. |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use WebService::MoviePosterDB; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $ws = WebService::MoviePosterDB->new(api_key => "key", api_secret => "secret", cache => 1, cache_exp => "12h"); |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
my $movie = $ws->search(type => "Movie", imdbid => "tt0114814", width => 300); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
print $movie->title(), ": \n\n"; |
17
|
|
|
|
|
|
|
print $movie->page(), "\n\n"; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
foreach ( @{$movie->posters()} ) { |
20
|
|
|
|
|
|
|
print $_->image_location(), "\n"; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 DESCRIPTION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
WebService::MusicBrainz is an object-oriented interface to MoviePosterDB. It can |
27
|
|
|
|
|
|
|
be used to retrieve artwork for IMDB titles. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=cut |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
package WebService::MoviePosterDB; |
33
|
|
|
|
|
|
|
|
34
|
1
|
|
|
1
|
|
22373
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
35
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
85
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
our $VERSION = '0.18'; |
38
|
|
|
|
|
|
|
|
39
|
1
|
|
|
1
|
|
823
|
use Cache::FileCache; |
|
1
|
|
|
|
|
129705
|
|
|
1
|
|
|
|
|
46
|
|
40
|
|
|
|
|
|
|
|
41
|
1
|
|
|
1
|
|
8
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
62
|
|
42
|
|
|
|
|
|
|
|
43
|
1
|
|
|
1
|
|
6
|
use Digest::MD5 qw(md5_hex); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
48
|
|
44
|
|
|
|
|
|
|
|
45
|
1
|
|
|
1
|
|
5
|
use File::Spec::Functions qw(tmpdir); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
53
|
|
46
|
|
|
|
|
|
|
|
47
|
1
|
|
|
1
|
|
1128
|
use JSON; |
|
1
|
|
|
|
|
18065
|
|
|
1
|
|
|
|
|
6
|
|
48
|
1
|
|
|
1
|
|
10622
|
use LWP::UserAgent; |
|
1
|
|
|
|
|
88844
|
|
|
1
|
|
|
|
|
39
|
|
49
|
1
|
|
|
1
|
|
11
|
use URI; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
135
|
|
50
|
|
|
|
|
|
|
|
51
|
1
|
|
|
1
|
|
696
|
use WebService::MoviePosterDB::Movie; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head1 METHODS |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 new(%opts) |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Constructor. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
%opts can contain: |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=over 4 |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=item api_key, api_secret |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
A key and secret are required to use the API. Contact movieposterdb.com for details. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=item cache |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Whether to cache responses. Defaults to true |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=item cache_root |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
The root dir for the cache. Defaults to tmpdir(); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item cache_exp |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
How long to cache responses for. Defaults to "1h" |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=back |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=cut |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub new { |
84
|
5
|
|
|
5
|
1
|
2524
|
my $class = shift; |
85
|
5
|
|
|
|
|
14
|
my %args = @_; |
86
|
5
|
|
|
|
|
11
|
my $self = {}; |
87
|
|
|
|
|
|
|
|
88
|
5
|
|
|
|
|
11
|
bless $self, $class; |
89
|
|
|
|
|
|
|
|
90
|
5
|
100
|
100
|
|
|
57
|
if ((!exists $args{'api_version'} || !defined $args{'api_version'} || $args{'api_version'} == 1) && !exists $args{'api_key'}) { |
|
|
|
100
|
|
|
|
|
91
|
2
|
|
|
|
|
365
|
carp "version 1 API is no longer available, using demo credentials"; |
92
|
2
|
|
|
|
|
17
|
$self->{'api_key'} = "demo"; |
93
|
2
|
|
|
|
|
5
|
$self->{'api_secret'} = "demo"; |
94
|
|
|
|
|
|
|
} else { |
95
|
3
|
|
|
|
|
45
|
$self->{'api_key'} = $args{'api_key'}; |
96
|
3
|
|
|
|
|
8
|
$self->{'api_secret'} = $args{'api_secret'}; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
5
|
100
|
66
|
|
|
42
|
if (!defined $self->{'api_key'} || !defined $self->{'api_secret'}) { |
100
|
2
|
|
|
|
|
240
|
croak "api_key and/or api_secret missing"; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
3
|
|
33
|
|
|
21
|
$self->{'_cache_root'} = $args{'cache_root'} || tmpdir(); |
104
|
3
|
|
50
|
|
|
146
|
$self->{'_cache_exp'} = $args{'cache_exp'} || "1h"; |
105
|
3
|
50
|
|
|
|
10
|
$self->{'_cache'} = defined $args{'cache'} ? $args{'cache'} : 1; |
106
|
|
|
|
|
|
|
|
107
|
3
|
50
|
|
|
|
13
|
if ($self->{'_cache'}) { |
108
|
3
|
|
|
|
|
33
|
$self->{'_cacheObj'} = Cache::FileCache->new( {'cache_root' => $self->{'_cache_root'}, 'namespace' => "WebService-MoviePosterDB", 'default_expires_in' => $self->{'_cache_exp'}} ); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
3
|
|
|
|
|
847
|
$self->{'_useragent'} = LWP::UserAgent->new(); |
112
|
3
|
|
|
|
|
30469
|
$self->{'_useragent'}->env_proxy(); |
113
|
3
|
|
|
|
|
152293
|
$self->{'_useragent'}->agent("WebService::MoviePosterDB/$VERSION"); |
114
|
|
|
|
|
|
|
|
115
|
3
|
|
|
|
|
249
|
return $self; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 search(type => "Movie", %args) |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Accesses MoviePosterDB and returns a WebService::MoviePosterDB::Movie object. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
%args can contain: |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=over 4 |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item type |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Controls the type of resource being requested. Currently only supports "Movie". |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item tconst |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
IMDB id for the title, e.g. tt0114814 |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item imdbid |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Alias for tconst |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item title |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Name of the title |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item width |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Image width for returned artwork |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=back |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=cut |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub search { |
152
|
5
|
|
|
5
|
1
|
26966
|
my $self = shift; |
153
|
5
|
|
|
|
|
29
|
my %args = @_; |
154
|
|
|
|
|
|
|
|
155
|
5
|
50
|
|
|
|
28
|
croak "Unknown type" unless ($args{'type'} eq "Movie"); |
156
|
|
|
|
|
|
|
|
157
|
5
|
|
|
|
|
11
|
my %_args; |
158
|
|
|
|
|
|
|
|
159
|
5
|
100
|
100
|
|
|
46
|
if (exists $args{'imdb_code'}) { |
|
|
100
|
|
|
|
|
|
160
|
1
|
|
|
|
|
6
|
$_args{'imdb_code'} = sprintf("%d", $args{'imdb_code'}); # Trim leading zeroes |
161
|
|
|
|
|
|
|
} elsif (exists $args{'tconst'} || exists $args{'imdbid'}) { |
162
|
3
|
100
|
|
|
|
14
|
my $tconst = exists $args{'tconst'} ? $args{'tconst'} : $args{'imdbid'}; |
163
|
3
|
50
|
|
|
|
23
|
my ($id) = $tconst =~ m/^tt(\d{6,7})$/ or croak "Unable to parse tconst '$tconst'"; |
164
|
3
|
|
|
|
|
17
|
$_args{'imdb_code'} = sprintf("%d", $id); # Trim leading zeroes |
165
|
|
|
|
|
|
|
} |
166
|
5
|
100
|
|
|
|
15
|
if (exists $args{'title'}) { $_args{'title'} = $args{'title'}; } |
|
1
|
|
|
|
|
4
|
|
167
|
5
|
50
|
|
|
|
17
|
if (exists $args{'width'}) { $_args{'width'} = $args{'width'}; } |
|
5
|
|
|
|
|
11
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Ugly hack. The demi api service appears to normalise the title key to lower case before returning the secret hash. |
170
|
5
|
50
|
66
|
|
|
35
|
if (exists $_args{'title'} && $self->{'api_key'} eq "demo" && $self->{'api_secret'} eq "demo") { $_args{'title'} = lc $_args{'title'}; } |
|
1
|
|
66
|
|
|
4
|
|
171
|
|
|
|
|
|
|
|
172
|
5
|
|
|
|
|
13
|
$_args{'api_key'} = $self->{'api_key'}; |
173
|
5
|
|
|
|
|
25
|
$_args{'secret'} = $self->_get_secret(%_args); |
174
|
|
|
|
|
|
|
|
175
|
5
|
|
|
|
|
31
|
my $uri = URI->new(); |
176
|
5
|
|
|
|
|
173
|
$uri->scheme("http"); |
177
|
5
|
|
|
|
|
292
|
$uri->host("api.movieposterdb.com"); |
178
|
5
|
|
|
|
|
276
|
$uri->path("json"); |
179
|
5
|
|
|
|
|
136
|
$uri->query_form( map { my ($n, $v) = ($_, $_args{$_}); utf8::encode($n); utf8::encode($v); ($n => $v); } sort keys %_args ); |
|
20
|
|
|
|
|
37
|
|
|
20
|
|
|
|
|
35
|
|
|
20
|
|
|
|
|
30
|
|
|
20
|
|
|
|
|
54
|
|
180
|
|
|
|
|
|
|
|
181
|
5
|
|
|
|
|
484
|
my $json = JSON->new()->decode($self->_get_page($uri->as_string())); |
182
|
|
|
|
|
|
|
|
183
|
5
|
|
|
|
|
81
|
return WebService::MoviePosterDB::Movie->_new($json); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub _get_secret { |
188
|
5
|
|
|
5
|
|
7
|
my $self = shift; |
189
|
5
|
|
|
|
|
18
|
my %args = @_; |
190
|
|
|
|
|
|
|
|
191
|
5
|
50
|
33
|
|
|
40
|
if ($self->{'api_key'} eq "demo" && $self->{'api_secret'} eq "demo") { |
192
|
|
|
|
|
|
|
|
193
|
5
|
|
|
|
|
9
|
my %_args; |
194
|
|
|
|
|
|
|
|
195
|
5
|
100
|
|
|
|
23
|
if (exists $args{'title'}) {$_args{'title'} = $args{'title'}; } |
|
1
|
|
|
|
|
4
|
|
196
|
5
|
100
|
|
|
|
15
|
if (exists $args{'imdb_code'}) {$_args{'imdb_code'} = $args{'imdb_code'}; } |
|
4
|
|
|
|
|
14
|
|
197
|
|
|
|
|
|
|
|
198
|
5
|
|
|
|
|
366
|
$_args{'type'} = "JSON"; |
199
|
5
|
|
|
|
|
46
|
$_args{'api_key'} = $self->{'api_key'}; |
200
|
5
|
|
|
|
|
12
|
$_args{'api_secret'} = $self->{'api_secret'}; |
201
|
|
|
|
|
|
|
|
202
|
5
|
|
|
|
|
44
|
my $uri = URI->new(); |
203
|
5
|
|
|
|
|
5230
|
$uri->scheme("http"); |
204
|
5
|
|
|
|
|
12915
|
$uri->host("api.movieposterdb.com"); |
205
|
5
|
|
|
|
|
673
|
$uri->path("console"); |
206
|
5
|
|
|
|
|
195
|
$uri->query_form( map { my ($n, $v) = ($_, $_args{$_}); utf8::encode($n); utf8::encode($v); ($n => $v); } sort keys %_args ); |
|
20
|
|
|
|
|
41
|
|
|
20
|
|
|
|
|
37
|
|
|
20
|
|
|
|
|
32
|
|
|
20
|
|
|
|
|
71
|
|
207
|
|
|
|
|
|
|
|
208
|
5
|
|
|
|
|
518
|
my $page = $self->_get_page($uri->as_string()); |
209
|
5
|
50
|
|
|
|
69
|
my ($s) = $page =~ m/secret=([a-f0-9]{12})/ or die "Failed to extract secret"; |
210
|
|
|
|
|
|
|
|
211
|
5
|
|
|
|
|
34
|
return $s; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
} else { |
214
|
0
|
|
|
|
|
0
|
my $v = $self->{'api_secret'}; |
215
|
0
|
0
|
|
|
|
0
|
if (exists $args{'imdb_code'}) { $v .= sprintf("%d", $args{'imdb_code'}); } |
|
0
|
|
|
|
|
0
|
|
216
|
0
|
0
|
|
|
|
0
|
if (exists $args{'title'}) { $v .= $args{'title'}; } |
|
0
|
|
|
|
|
0
|
|
217
|
|
|
|
|
|
|
|
218
|
0
|
|
|
|
|
0
|
utf8::encode($v); |
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
0
|
return substr(md5_hex($v), 10, 12); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub _get_page { |
226
|
10
|
|
|
10
|
|
59
|
my $self = shift; |
227
|
10
|
|
|
|
|
16
|
my $url = shift; |
228
|
|
|
|
|
|
|
|
229
|
10
|
|
|
|
|
16
|
my $content; |
230
|
|
|
|
|
|
|
|
231
|
10
|
50
|
|
|
|
33
|
if ($self->{'_cache'}) { |
232
|
10
|
|
|
|
|
254
|
$content = $self->{'_cacheObj'}->get($url); |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
10
|
50
|
|
|
|
140837
|
if (! defined $content) { |
236
|
0
|
|
|
|
|
0
|
my $response = $self->{'_useragent'}->get($url); |
237
|
|
|
|
|
|
|
|
238
|
0
|
0
|
|
|
|
0
|
if($response->code() ne "200") { |
239
|
0
|
|
|
|
|
0
|
croak "URL (", $url, ") Request Failed - Code: ", $response->code(), " Error: ", $response->message(), "\n"; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
0
|
|
|
|
|
0
|
$content = $response->decoded_content(); |
243
|
|
|
|
|
|
|
|
244
|
0
|
0
|
|
|
|
0
|
if ($self->{'_cache'}) { |
245
|
0
|
|
|
|
|
0
|
$self->{'_cacheObj'}->set($url, $content); |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
10
|
|
|
|
|
113
|
return $content; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
1; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=head1 NOTES |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
The version 1 API, previously used by default, stopped as of 2011-09-27, and credentials |
258
|
|
|
|
|
|
|
are required to access the version 2 API. It is possible to access the |
259
|
|
|
|
|
|
|
version 2 API using test credentials (key, secret = "demo"), and this will be |
260
|
|
|
|
|
|
|
done for legacy applications that try to use the version 1 API. However, this |
261
|
|
|
|
|
|
|
feature is only intended for test purposes: legacy applications should be adapted, |
262
|
|
|
|
|
|
|
and new applications should not use it. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head1 AUTHOR |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Christopher Key |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENCE |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
Copyright (C) 2010-2011 Christopher Key |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
275
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.8.4 or, |
276
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=cut |