File Coverage

blib/lib/WebService/Browshot.pm
Criterion Covered Total %
statement 33 200 16.5
branch 1 44 2.2
condition 4 71 5.6
subroutine 9 39 23.0
pod 24 32 75.0
total 71 386 18.3


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