File Coverage

blib/lib/WebService/Browshot.pm
Criterion Covered Total %
statement 33 196 16.8
branch 1 44 2.2
condition 4 71 5.6
subroutine 9 37 24.3
pod 22 30 73.3
total 69 378 18.2


line stmt bran cond sub pod time code
1             package WebService::Browshot;
2              
3 1     1   25265 use 5.006006;
  1         3  
  1         30  
4 1     1   3 use strict;
  1         2  
  1         26  
5 1     1   3 use warnings;
  1         9  
  1         104  
6              
7 1     1   570 use LWP::UserAgent;
  1         49688  
  1         52  
8 1     1   1033 use JSON;
  1         14759  
  1         7  
9 1     1   942 use URI::Encode qw(uri_encode);
  1         16068  
  1         143  
10              
11             $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
12 1     1   1285 use IO::Socket::SSL;
  1         120337  
  1         16  
13             IO::Socket::SSL::set_ctx_defaults(
14             SSL_verifycn_scheme => 'www',
15             SSL_verify_mode => 0,
16             verify_mode => 0,
17             );
18              
19             our $VERSION = '1.14.1';
20              
21             =head1 NAME
22              
23             WebService::Browshot - Perl extension for Browshot (L), a web service to create screenshots of web pages.
24              
25             =head1 SYNOPSIS
26              
27             use WebService::Browshot;
28            
29             my $browshot = WebService::Browshot->new(key => 'my_key');
30             my $screenshot = $browshot->screenshot_create(url => 'http://www.google.com/');
31             [...]
32             $browshot->screenshot_thumbnail_file(id => $screenshot->{id}, file => 'google.png');
33              
34             =head1 DESCRIPTION
35              
36             Browshot (L) is a web service to easily make screenshots of web pages in any screen size, as any device: iPhone, iPad, Android, Nook, PC, etc. Browshot has full Flash, JavaScript, CSS, & HTML5 support.
37              
38             The latest API version is detailed at L. WebService::Browshot follows the API documentation very closely: the function names are similar to the URLs used (screenshot/create becomes C, instance/list becomes C, etc.), the request arguments are exactly the same, etc.
39              
40             The library version matches closely the API version it handles: WebService::Browshot 1.0.0 is the first release for the API 1.0, WebService::Browshot 1.1.1 is the second release for the API 1.1, etc.
41              
42             WebService::Browshot can handle most the API updates within the same major version, e.g. WebService::Browshot 1.0.0 should be compatible with the API 1.1 or 1.2.
43              
44             The source code is available on github at L.
45              
46              
47             =head1 METHODS
48              
49             =over 4
50              
51             =head2 new()
52              
53             my $browshot = WebService::Browshot->new(key => 'my_key', base => 'http://api.browshot.com/api/v1/', debug => 1]);
54              
55             Create a new WebService::Browshot object. You must pass your API key (go to you Dashboard to find your API key).
56              
57             Arguments:
58              
59             =over 4
60              
61             =item key
62              
63             Required. API key.
64              
65             =item base
66              
67             Optional. Base URL for all API requests. You should use the default base provided by the library. Be careful if you decide to use HTTP instead of HTTPS as your API key could be sniffed and your account could be used without your consent.
68              
69             =item debug
70              
71             Optional. Set to 1 to print debug output to the standard output. 0 (disabled) by default.
72              
73             =item timeout
74              
75             Optional. Set the request timeout - in seconds - against the API. Defaults to 90s.
76              
77             =back
78              
79             C contains the last error message, it is NEVER reset, i.e last_error may not be empty after a successful API call if an earlier call failed.
80              
81             =cut
82              
83             sub new {
84 1     1 1 735 my ($self, %args) = @_;
85              
86 1         11 my $ua = LWP::UserAgent->new();
87 1   50     2549 $ua->timeout($args{'timeout'} || 90);
88 1         21 $ua->env_proxy;
89 1         3721 $ua->max_redirect(32); # for the simple API only
90 1         16 $ua->agent("WebService::Browshot $VERSION");
91 1         74 $ua->ssl_opts( verify_hostnames => 0 );
92              
93 1   50     36 my $browshot = {
      50        
      50        
94             _key => $args{key} || '',
95             _base => $args{base} || 'https://api.browshot.com/api/v1/',
96             _debug => $args{debug} || 0,
97              
98             _retry => 2,
99             last_error => '',
100              
101             _ua => $ua,
102             };
103              
104 1         7 return bless($browshot, $self);
105             }
106              
107              
108             =head2 api_version()
109              
110             Return the API version handled by the library. Note that this library can usually handle new arguments in requests without requiring an update.
111              
112             =cut
113              
114             sub api_version {
115 1     1 1 5 my ($self, %args) = @_;
116              
117 1 50       8 if ($VERSION =~ /^(\d+\.\d+)\.\d/) {
118 1         7 return $1;
119             }
120              
121 0           return $VERSION;
122             }
123              
124              
125              
126             =head2 simple()
127              
128             $browshot->simple(url => 'http://mobilito.net')
129              
130             Retrieve a screenshot in one function. Note: by default, screenshots are cached for 24 hours. You can tune this value with the cache=X parameter.
131              
132             Return an array (status code, PNG). See L for the list of possible status codes.
133              
134             Arguments:
135              
136             See L for the full list of possible arguments.
137              
138             =over 4
139              
140             =item url
141              
142             Required. URL of the website to create a screenshot of.
143              
144             =back
145              
146             =cut
147              
148             sub simple {
149 0     0 1   my ($self, %args) = @_;
150              
151 0           my $url = $self->make_url(action => 'simple', parameters => { %args });
152 0           my $res = $self->{_ua}->get($url);
153              
154             # $self->info($res->message);
155             # $self->info($res->request->as_string);
156             # $self->info($res->as_string);
157            
158 0           return ($res->code, $res->decoded_content);
159             }
160              
161             =head2 simple_file()
162              
163             $browshot->simple_file(url => 'http://mobilito.net', file => '/tmp/mobilito.png')
164              
165             Retrieve a screenshot and save it locally in one function. Note: by default, screenshots are cached for 24 hours. You can tune this value with the cache=X parameter.
166              
167             Return an array (status code, file name). The file name is empty if the screenshot was not retrieved. See L for the list of possible status codes.
168              
169             Arguments:
170              
171             See L for the full list of possible arguments.
172              
173             =over 4
174              
175             =item url
176              
177             Required. URL of the website to create a screenshot of.
178              
179             =item file
180              
181             Required. Local file name to write to.
182              
183             =back
184              
185             =cut
186              
187             sub simple_file {
188 0     0 1   my ($self, %args) = @_;
189 0   0       my $file = $args{file} || $self->error("Missing file in simple_file");
190              
191 0           my $url = $self->make_url(action => 'simple', parameters => { %args });
192 0           my $res = $self->{_ua}->get($url);
193              
194 0           my $content = $res->decoded_content;
195              
196 0 0         if ($content ne '') {
197 0 0         open TARGET, "> $file" or $self->error("Cannot open $file for writing: $!");
198 0           binmode TARGET;
199 0           print TARGET $content;
200 0           close TARGET;
201              
202 0           return ($res->code, $file);
203             }
204             else {
205 0           $self->error("No thumbnail retrieved");
206 0           return ($res->code, '');
207             }
208             }
209              
210             =head2 instance_list()
211              
212             Return the list of instances as a hash reference. See L for the response format.
213              
214             =cut
215              
216             sub instance_list {
217 0     0 1   my ($self, %args) = @_;
218            
219 0           return $self->return_reply(action => 'instance/list');
220             }
221              
222             =head2 instance_info()
223              
224             $browshot->instance_info(id => 2)
225              
226             Return the details of an instance. See L for the response format.
227              
228             Arguments:
229              
230             =over 4
231              
232             =item id
233              
234             Required. Instance ID
235              
236             =back
237              
238             =cut
239              
240             sub instance_info {
241 0     0 1   my ($self, %args) = @_;
242 0   0       my $id = $args{id} || $self->error("Missing id in instance_info");
243              
244 0           return $self->return_reply(action => 'instance/info', parameters => { id => $id });
245             }
246              
247             =head2 browser_list()
248              
249             Return the list of browsers as a hash reference. See L for the response format.
250              
251             =cut
252              
253             sub browser_list {
254 0     0 1   my ($self, %args) = @_;
255            
256 0           return $self->return_reply(action => 'browser/list');
257             }
258              
259             =head2 browser_info()
260              
261             $browshot->browser_info(id => 2)
262              
263             Return the details of a browser. See L for the response format.
264              
265             Arguments:
266              
267             =over 4
268              
269             =item id
270              
271             Required. Browser ID
272              
273             =back
274              
275             =cut
276              
277             sub browser_info {
278 0     0 1   my ($self, %args) = @_;
279 0   0       my $id = $args{id} || $self->error("Missing id in browser_info");
280              
281 0           return $self->return_reply(action => 'browser/info', parameters => { id => $id });
282             }
283              
284              
285             =head2 screenshot_create()
286              
287             $browshot->screenshot_create(url => 'http://wwww.google.com/', instance_id => 3, size => 'page')
288              
289             Request a screenshot. See L for the response format.
290             Note: by default, screenshots are cached for 24 hours. You can tune this value with the cache=X parameter.
291              
292             Arguments:
293              
294             See L for the full list of possible arguments.
295              
296             =over 4
297              
298             =item url
299              
300             Required. URL of the website to create a screenshot of.
301              
302             =item instance_id
303              
304             Optional. Instance ID to use for the screenshot.
305              
306             =item size
307              
308             Optional. Screenshot size.
309              
310             =back
311              
312             =cut
313              
314             sub screenshot_create {
315 0     0 1   my ($self, %args) = @_;
316             # my $url = $args{url} || $self->error("Missing url in screenshot_create");
317             # my $instance_id = $args{instance_id};
318             # my $screen = $args{screen};
319             # my $size = $args{size} || "screen";
320             # my $cache = $args{cache};
321             # my $priority = $args{priority};
322              
323 0 0         $self->error("Missing url in screenshot_create") if (! defined($args{url}));
324             # $args{size} = "screen" if (! defined($args{size}));
325              
326 0           return $self->return_reply(action => 'screenshot/create', parameters => { %args });
327             }
328              
329             =head2 screenshot_info()
330              
331             $browshot->screenshot_info(id => 568978)
332              
333             Get information about a screenshot requested previously. See L for the response format.
334              
335             Arguments:
336              
337             =over 4
338              
339             =item id
340              
341             Required. Screenshot ID.
342              
343             =back
344              
345             =cut
346              
347             sub screenshot_info {
348 0     0 1   my ($self, %args) = @_;
349 0   0       my $id = $args{id} || $self->error("Missing id in screenshot_info");
350              
351              
352 0           return $self->return_reply(action => 'screenshot/info', parameters => { %args });
353             }
354              
355             =head2 screenshot_list()
356              
357             $browshot->screenshot_list(limit => 50)
358              
359             Get details about screenshots requested. See L for the response format.
360              
361             Arguments:
362              
363             =over 4
364              
365             =item limit
366              
367             Optional. Maximum number of screenshots to retrieve.
368              
369             =back
370              
371             =cut
372              
373             sub screenshot_list {
374 0     0 1   my ($self, %args) = @_;
375              
376 0           return $self->return_reply(action => 'screenshot/list', parameters => { %args });
377             }
378              
379             =head2 screenshot_search()
380              
381             $browshot->screenshot_search(url => 'google.com')
382              
383             Get details about screenshots requested. See L for the response format.
384              
385             Arguments:
386              
387             =over 4
388              
389             =item url
390              
391             Required. URL string to look for.
392              
393             =back
394              
395             =cut
396              
397             sub screenshot_search {
398 0     0 1   my ($self, %args) = @_;
399 0   0       my $url = $args{url} || $self->error("Missing id in screenshot_search");
400              
401 0           return $self->return_reply(action => 'screenshot/search', parameters => { %args });
402             }
403              
404             =head2 screenshot_host()
405              
406             $browshot->screenshot_host(id => 12345, hosting => 'browshot')
407              
408             Host a screenshot or thumbnail. See L for the response format.
409              
410             Arguments:
411              
412             =over 4
413              
414             =item id
415              
416             Required. Screenshot ID.
417              
418             =back
419              
420             =cut
421              
422             sub screenshot_host {
423 0     0 1   my ($self, %args) = @_;
424 0   0       my $id = $args{id} || $self->error("Missing id in screenshot_host");
425              
426 0           return $self->return_reply(action => 'screenshot/host', parameters => { %args });
427             }
428              
429              
430             =head2 screenshot_thumbnail()
431              
432             $browshot->screenshot_thumbnail(id => 52942, width => 500)
433              
434             Retrieve the screenshot, or a thumbnail. See L for the response format.
435              
436             Return an empty string if the image could not be retrieved.
437              
438             Arguments:
439              
440             See L for the full list of possible arguments.
441              
442             =over 4
443              
444             =item id
445              
446             Required. Screenshot ID.
447              
448             =item width
449              
450             Optional. Maximum width of the thumbnail.
451              
452             =item height
453              
454             Optional. Maximum height of the thumbnail.
455              
456             =back
457              
458             =cut
459             sub screenshot_thumbnail {
460 0     0 1   my ($self, %args) = @_;
461              
462 0 0 0       if (exists($args{url}) && $args{url} =~ /image\/(\d+)\?/i && ! exists($args{id})) {
    0 0        
463             # get ID from url
464 0           $args{id} = $1;
465              
466 0 0 0       if ($args{url} =~ /&width=(\d+)\?/i && ! exists($args{width})) {
467 0           $args{width} = $1;
468             }
469 0 0 0       if ($args{url} =~ /&height=(\d+)\?/i && ! exists($args{height})) {
470 0           $args{height} = $1;
471             }
472            
473             }
474             elsif(! exists($args{id}) ) {
475 0           $self->error("Missing id and url in screenshot_thumbnail");
476 0           return '';
477             }
478              
479              
480 0           my $url = $self->make_url(action => 'screenshot/thumbnail', parameters => { %args });
481 0           my $res = $self->{_ua}->get($url);
482              
483 0 0         if ($res->is_success) {
484 0           return $res->decoded_content; # raw image file content
485             }
486             else {
487 0           $self->error("Error in thumbnail request: " . $res->as_string);
488 0           return '';
489             }
490             }
491              
492              
493             =head2 screenshot_thumbnail_file()
494              
495             $browshot->screenshot_thumbnail_file(id => 123456, height => 500, file => '/tmp/google.png')
496              
497             Retrieve the screenshot, or a thumbnail, and save it to a file. See L for the response format.
498              
499             Return an empty string if the image could not be retrieved or not saved. Returns the file name if successful.
500              
501             Arguments:
502              
503             See L for the full list of possible arguments.
504              
505             =over 4
506              
507             =item url
508              
509             Required. URL of the screenshot (screenshot_url value retrieved from C or C). You will get the full image if no other argument is specified.
510              
511             =item file
512              
513             Required. Local file name to write to.
514              
515             =item width
516              
517             Optional. Maximum width of the thumbnail.
518              
519             =item height
520              
521             Optional. Maximum height of the thumbnail.
522              
523             =back
524              
525             =cut
526             sub screenshot_thumbnail_file {
527 0     0 1   my ($self, %args) = @_;
528 0   0       my $file = $args{file} || $self->error("Missing file in screenshot_thumbnail_file");
529              
530 0           my $content = $self->screenshot_thumbnail(%args);
531              
532 0 0         if ($content ne '') {
533 0 0         open TARGET, "> $file" or $self->error("Cannot open $file for writing: $!");
534 0           binmode TARGET;
535 0           print TARGET $content;
536 0           close TARGET;
537              
538 0           return $file;
539             }
540             else {
541 0           $self->error("No thumbnail retrieved");
542 0           return '';
543             }
544             }
545              
546             =head2 screenshot_share()
547              
548             $browshot->screenshot_share(id => 12345, note => 'This is my screenshot')
549              
550             Share a screenshot. See L for the response format.
551              
552             Arguments:
553              
554             =over 4
555              
556             =item id
557              
558             Required. Screenshot ID.
559              
560             =item note
561              
562             Optional. Public note to add to the screenshot.
563              
564             =back
565              
566             =cut
567              
568             sub screenshot_share {
569 0     0 1   my ($self, %args) = @_;
570 0   0       my $id = $args{id} || $self->error("Missing id in screenshot_share");
571              
572 0           return $self->return_reply(action => 'screenshot/share', parameters => { %args });
573             }
574              
575              
576             =head2 screenshot_delete()
577              
578             $browshot->screenshot_delete(id => 12345, data => 'url,metadata')
579              
580             Delete details of a screenshot. See L for the response format.
581              
582             Arguments:
583              
584             =over 4
585              
586             =item id
587              
588             Required. Screenshot ID.
589              
590             =item data
591              
592             Optional. Information to delete.
593              
594             =back
595              
596             =cut
597              
598             sub screenshot_delete {
599 0     0 1   my ($self, %args) = @_;
600 0   0       my $id = $args{id} || $self->error("Missing id in screenshot_delete");
601              
602 0           return $self->return_reply(action => 'screenshot/delete', parameters => { %args });
603             }
604              
605             =head2 screenshot_html()
606              
607             $browshot->screenshot_html(id => 12345)
608              
609             Get the HTML code of the rendered page. See L for the response format.
610              
611             Arguments:
612              
613             =over 4
614              
615             =item id
616              
617             Required. Screenshot ID.
618              
619             =back
620              
621             =cut
622              
623             sub screenshot_html {
624 0     0 1   my ($self, %args) = @_;
625 0   0       my $id = $args{id} || $self->error("Missing id in screenshot_html");
626              
627 0           return $self->return_string(action => 'screenshot/html', parameters => { %args });
628             }
629              
630             =head2 screenshot_multiple()
631              
632             $browshot->screenshot_multiple(urls => ['http://mobilito.net/'], instances => [22, 30])
633              
634             Request multiple screenshots. See L for the response format.
635              
636             Arguments:
637              
638             =over 4
639              
640             =item urls
641              
642             Required. One or more URLs.
643              
644             =item instances
645              
646             Required. One or more instance_id.
647              
648             =back
649              
650             =cut
651              
652             sub screenshot_multiple {
653 0     0 1   my ($self, %args) = @_;
654             # my $urls = $args{urls} || $self->error("Missing urls in screenshot_multiple");
655             # my $instances = $args{instances} || $self->error("Missing instances in screenshot_multiple");
656              
657 0           return $self->return_reply(action => 'screenshot/multiple', parameters => { %args });
658             }
659              
660              
661             =head2 batch_create()
662              
663             $browshot->batch_create(file => '/my/file/urls.txt', instance_id => 65)
664              
665             Request multiple screenshots from a text file. See L for the response format.
666              
667             Arguments:
668              
669             =over 4
670              
671             =item file
672              
673             Required. Path to the text file which contains the list of URLs.
674              
675             =item instance_id
676              
677             Required. instance_id to use for all screenshots.
678              
679             =back
680              
681             =cut
682              
683             sub batch_create {
684 0     0 1   my ($self, %args) = @_;
685 0   0       my $file = $args{file} || $self->error("Missing file in batch_create");
686 0   0       my $instance_id = $args{instance_id} || $self->error("Missing instance_id} in batch_create");
687              
688 0           return $self->return_post_reply(action => 'batch/create', parameters => { %args }, file => $file);
689             }
690              
691             =head2 batch_info()
692              
693             $browshot->batch_info(id => 5)
694              
695             Get information about a screenshot batch requested previously. See L for the response format.
696              
697             Arguments:
698              
699             =over 4
700              
701             =item id
702              
703             Required. Batch ID.
704              
705             =back
706              
707             =cut
708              
709             sub batch_info {
710 0     0 1   my ($self, %args) = @_;
711 0   0       my $id = $args{id} || $self->error("Missing id in batch_info");
712              
713 0           return $self->return_reply(action => 'batch/info', parameters => { %args });
714             }
715              
716              
717              
718             =head2 account_info()
719              
720             Return information about the user account. See L for the response format.
721              
722             =cut
723              
724             sub account_info {
725 0     0 1   my ($self, %args) = @_;
726            
727 0           return $self->return_reply(action => 'account/info', parameters => { %args });
728             }
729              
730              
731             # Private methods
732              
733             sub return_string {
734 0     0 0   my ($self, %args) = @_;
735              
736 0           my $url = $self->make_url(%args);
737            
738 0           my $res;
739 0           my $try = 0;
740              
741 0   0       do {
742 0           $self->info("Try $try");
743 0           eval {
744 0           $res = $self->{_ua}->get($url);
745             };
746 0 0         $self->error($@) if ($@);
747 0           $try++;
748             }
749             until($try < $self->{_retry} && defined $@);
750              
751 0 0         if (! $res->is_success) {
752 0           $self->error("Server sent back an error: " . $res->code);
753             }
754            
755 0           return $res->decoded_content;
756             }
757              
758             sub return_post_string {
759 0     0 0   my ($self, %args) = @_;
760 0   0       my $file = $args{'file'} || '';
761              
762 0           delete $args{'file'};
763 0           my $url = $self->make_url(%args);
764            
765 0           my $res;
766 0           my $try = 0;
767              
768 0   0       do {
769 0           $self->info("Try $try");
770 0           eval {
771 0           $res = $self->{_ua}->post(
772             $url,
773             Content_Type => 'form-data',
774             Content => [
775             file => [$file],
776             ]
777             );
778             };
779 0 0         $self->error($@) if ($@);
780 0           $try++;
781             }
782             until($try < $self->{_retry} && defined $@);
783              
784 0 0         if (! $res->is_success) {
785 0           $self->error("Server sent back an error: " . $res->code);
786             }
787            
788 0           return $res->decoded_content;
789             }
790              
791             sub return_reply {
792 0     0 0   my ($self, %args) = @_;
793              
794 0           my $content = $self->return_string(%args);
795              
796 0           my $info;
797 0           eval {
798 0           $info = decode_json($content);
799             };
800 0 0         if ($@) {
801 0           $self->error("Invalid server response: " . $@);
802 0           return $self->generic_error($@);
803             }
804              
805 0           return $info;
806             }
807              
808             sub return_post_reply {
809 0     0 0   my ($self, %args) = @_;
810              
811 0           my $content = $self->return_post_string(%args);
812              
813 0           my $info;
814 0           eval {
815 0           $info = decode_json($content);
816             };
817 0 0         if ($@) {
818 0           $self->error("Invalid server response: " . $@);
819 0           return $self->generic_error($@);
820             }
821              
822 0           return $info;
823             }
824              
825             sub make_url {
826 0     0 0   my ($self, %args) = @_;
827 0   0       my $action = $args{action} || '';
828 0   0       my $parameters = $args{parameters} || { };
829              
830 0           my $url = $self->{_base} . "$action?key=" . uri_encode($self->{_key}, 1);
831              
832              
833 0 0         if (exists $parameters->{urls}) {
834 0           foreach my $uri (@{ $parameters->{urls} }) {
  0            
835 0           $url .= '&url=' . uri_encode($uri, 1);
836             }
837 0           delete $parameters->{urls};
838             }
839              
840 0 0         if (exists $parameters->{instances}) {
841 0           foreach my $instance_id (@{ $parameters->{instances} }) {
  0            
842 0           $url .= '&instance_id=' . uri_encode($instance_id, 1);
843             }
844 0           delete $parameters->{instances};
845             }
846              
847 0           foreach my $key (keys %$parameters) {
848 0 0         $url .= '&' . uri_encode($key) . '=' . uri_encode($parameters->{$key}, 1) if (defined $parameters->{$key});
849             }
850              
851 0           $self->info($url);
852 0           return $url;
853             }
854              
855             sub info {
856 0     0 0   my ($self, $message) = @_;
857              
858 0 0         if ($self->{_debug}) {
859 0           print $message, "\n";
860             }
861              
862 0           return '';
863             }
864              
865             sub error {
866 0     0 0   my ($self, $message) = @_;
867              
868 0           $self->{last_error} = $message;
869              
870 0 0         if ($self->{_debug}) {
871 0           print $message, "\n";
872             }
873              
874 0           return '';
875             }
876              
877             sub generic_error {
878 0     0 0   my ($self, $message) = @_;
879              
880              
881 0           return { error => 1, message => $message };
882             }
883              
884             =head1 CHANGES
885              
886             =over 4
887              
888             =item 1.14.1
889              
890             Remove deprecated API calls.
891              
892             =item 1.14.0
893              
894             Add C and C for API 1.14.
895              
896             =item 1.13.0
897              
898             Add C and C for API 1.13.
899              
900             =item 1.12
901              
902             Add C for API 1.12.
903              
904             =item 1.11.1
905              
906             Return Browshot response in case of error if the reply is valid JSON.
907              
908             =item 1.11
909              
910             Compatible with API 1.11. Optional HTTP timeout.
911              
912             =item 1.10
913              
914             Add C for API 1.10.
915              
916             =item 1.9.4
917              
918             Fix status code in error messages.
919              
920             =item 1.9.3
921              
922             Keep backward compatibility for C.
923              
924             =item 1.9.0
925              
926             Add C for API 1.9.
927              
928             =item 1.8.0
929              
930             Update C to use new API.
931              
932             =item 1.7.0
933              
934             Update C to handle additional parameters
935              
936             =item 1.5.1
937              
938             Use binmode to create valid PNG files on Windows.
939              
940             =item 1.4.1
941              
942             Fix URI encoding.
943              
944             =item 1.4.0
945              
946             Add C and C methods.
947              
948             =item 1.3.1
949              
950             Retry requests (up to 2 times) to browshot.com in case of error
951              
952             =back
953              
954             =head1 SEE ALSO
955              
956             See L for the API documentation.
957              
958             Create a free account at L to get your free API key.
959              
960             Go to L to find your API key after you registered.
961              
962             =head1 AUTHOR
963              
964             Julien Sobrier, Ejulien@sobrier.netE
965              
966             =head1 COPYRIGHT AND LICENSE
967              
968             Copyright (C) 2015 by Julien Sobrier
969              
970             This library is free software; you can redistribute it and/or modify
971             it under the same terms as Perl itself, either Perl version 5.8.8 or,
972             at your option, any later version of Perl 5 you may have available.
973              
974              
975             =cut
976              
977             1;