File Coverage

lib/Mojo/Downloader.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Mojo::Downloader;
2              
3             # ABSTRACT: a simple download tool
4 1     1   3795 use Momo;
  1         58489  
  1         7  
5              
6 1     1   560 use File::Basename qw(dirname);
  1         3  
  1         69  
7 1     1   1131 use YAML qw(Dump);
  1         11545  
  1         87  
8 1     1   2231 use Mojo::UserAgent;
  1         630498  
  1         21  
9 1     1   594 use Coro;
  0            
  0            
10             use Coro::Semaphore;
11             use AnyEvent;
12             use Storable;
13              
14             extends 'Mojo::EventEmitter';
15              
16             our $VERSION = 0.2;
17              
18             has ua => sub { Mojo::UserAgent->new };
19             has interval => 1;
20             has cv => sub {
21             my $condvar = AnyEvent->condvar;
22             $condvar->cb( sub { shift->recv } );
23             $condvar;
24             };
25             has max_currency => 10;
26             has sem => sub { Coro::Semaphore->new( shift->max_currency ) };
27             has cookie_file => sub { $ENV{MOJO_COOKIE_FILE} };
28              
29             sub new {
30             my ( $class, @args ) = @_;
31             my $self = $class->SUPER::new(@args);
32             if ( $self->cookie_file and -s $self->cookie_file > 0 ) {
33             eval { $self->ua->cookie_jar( retrieve $self->cookie_file ) };
34             die "cookie_file must save as Storable mode" if $@;
35             }
36             $self->on(
37             download => sub {
38             my ( $ua, $tx, $file, $r ) = @_;
39             my $url = $tx->req->url;
40             my $content_lenth = $tx->res->headers->content_length;
41             if ( $tx->success ) {
42             if ( defined $file and -e dirname($file) ) {
43             $tx->res->content->asset->move_to($file);
44             if ( -s $file == $content_lenth ) {
45             print "downloaded $url => $file success!\n";
46             $r->{$file} = 1 if ref $r eq ref {};
47             }
48             else {
49             warn "file => $file not fully downloaded \n";
50             }
51             }
52             }
53             else {
54             warn "download url => " . $tx->req->url . " ".$tx->res->code."failed";
55             }
56             }
57             );
58              
59             return $self;
60             }
61              
62             sub set_max_currency {
63             my ( $self, $limit ) = @_;
64             $self->sem( Coro::Semaphore->new($limit) );
65             $self->max_currency($limit);
66             }
67              
68             sub run{
69             shift->cv->recv;
70             }
71              
72             sub _async_request {
73             my ( $self, $url, $options ) = @_;
74              
75             $options ||= {};
76             my $on_header = delete $options->{on_header};
77             my $on_body = delete $options->{on_body};
78             my $cookies = delete $options->{cookies};
79             my $form = delete $options->{form};
80             my $method = delete $options->{method};
81             my $headers = delete $options->{headers};
82             my $file = delete $options->{file};
83              
84             my $results = $options->{results};
85             $method ||= 'get';
86             $headers ||= {};
87             $form ||= {};
88              
89             if ($url) {
90             $self->ua->cookie_jar($cookies) if ref $cookies;
91             async_pool {
92             $self->sem->down;
93             $self->cv->begin;
94             $self->ua->$method(
95             $url => $headers => form => $form => sub {
96             my ( $ua, $tx ) = @_;
97             Coro::AnyEvent::sleep($self->interval) if $self->interval;
98             $self->emit(
99             download => $ua,
100             $file, $tx, $options->{results}
101             );
102             $self->sem->up;
103             $self->cv->end;
104             }
105             );
106             };
107             }
108             return sub { $self->cv->recv };
109             }
110              
111             sub download {
112             my ( $self, $url, $file ) = @_;
113             return $self->_async_request( $url, { file => $file } );
114             }
115              
116             1;
117              
118             __END__