File Coverage

blib/lib/WebService/Browshot.pm
Criterion Covered Total %
statement 38 211 18.0
branch 1 50 2.0
condition 4 71 5.6
subroutine 11 39 28.2
pod 22 30 73.3
total 76 401 18.9


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