App-ManiacDownloader

 view release on metacpan or  search on metacpan

lib/App/ManiacDownloader.pm  view on Meta::CPAN


    # We do these to make sure the cancellation guard does not get
    # preserved because it's in the context of the closures.
    my $on_body = sub {
        my ( $active_seq, $data, $hdr ) = @_;

        # Stale or wrong connection - probably AnyEvent::FTP::Client after a
        # quit.
        if ( ( !$r->_is_right_active_seq($active_seq) ) or ( !$r->is_active ) )
        {
            return;
        }

        my $ret = $r->_write_data( \$data );

        $self->_downloaded->_add( $ret->{num_written} );

        my $cont = $ret->{should_continue};
        if ( !$cont )
        {
            if ($is_ftp)
            {
                $r->_guard->quit;
            }
            my $largest_r = max_by { $r->_num_remaining } @{ $self->_ranges };
            if ( $largest_r->_num_remaining < $NUM_CONN_BYTES_THRESHOLD )
            {
                $r->_close;
                if (
                    not $self->_remaining_connections(
                        $self->_remaining_connections() - 1
                    )
                    )
                {
                    $self->_finished_condvar->send;
                }
            }
            else
            {
                $largest_r->_split_into($r);
                $self->_start_connection($idx);
            }
        }
        return $cont;
    };

    my $final_cb = sub { return; };

    my $url = $self->_file->_url;
    {
        my $active_seq = $r->_get_next_active_seq;

        my $seq_on_body = sub { return $on_body->( $active_seq, @_ ); };

        if ($is_ftp)
        {
            my $ftp = AnyEvent::FTP::Client->new( passive => 1 );
            $r->_guard($ftp);
            $ftp->connect( $url->host, $url->port )->cb(
                sub {
                    $ftp->login( $url->user, $url->password )->cb(
                        sub {
                            $ftp->type('I')->cb(
                                sub {
                                    $ftp->retr( $self->_file->_url_path,
                                        $seq_on_body, restart => $r->_start, );
                                }
                            );
                        }
                    );
                }
            );
        }
        else
        {
            my $guard = http_get $url,
                headers =>
                { 'Range' => sprintf( "bytes=%d-%d", $r->_start, $r->_end - 1 )
                },
                on_body => $seq_on_body,
                $final_cb;

            $r->_guard($guard);

            $guard = '';
        }
    }

    return;
}

my $MAX_CHECKS = 6;

sub _handle_stats_timer
{
    my ($self) = @_;

    my ( $num_dloaded, $total_downloaded ) =
        $self->_downloaded->_flush_and_report;

    my $_ranges = $self->_ranges;
    for my $idx ( 0 .. $#$_ranges )
    {
        my $r = $_ranges->[$idx];

        $r->_flush_and_report;
        if ( $r->is_active && $r->_increment_check_count($MAX_CHECKS) )
        {
            $r->_guard('');
            $self->_start_connection($idx);
        }
    }

    my $time      = AnyEvent->now;
    my $last_time = $self->_last_timer_time;

    printf "Downloaded %i%% (Currently: %.2fKB/s)\r",
        int( $total_downloaded * 100 / $self->_len ),
        ( $num_dloaded / ( 1024 * ( $time - $last_time ) ) ),
        ;
    STDOUT->flush;

lib/App/ManiacDownloader.pm  view on Meta::CPAN

    print {$json_out_fh} encode_json( $self->_serialize );
    close($json_out_fh);

    exit(2);
}

sub run
{
    my ( $self, $args ) = @_;

    my $num_connections = $DEFAULT_NUM_CONNECTIONS;

    my @argv = @{ $args->{argv} };

    if (
        !GetOptionsFromArray(
            \@argv, 'k|num-connections=i' => \$num_connections,
        )
        )
    {
        die "Cannot parse argv - $!";
    }

    my $url_s = shift(@argv)
        or die "No url given.";

    $self->_file( App::ManiacDownloader::_File->new );
    $self->_file->_set_url($url_s);

    if ( -e $self->_file->_url_basename )
    {
        print STDERR
            "File appears to have already been downloaded. Quitting.\n";
        return;
    }

    $self->_finished_condvar( scalar( AnyEvent->condvar ) );

    if ( -e $self->_file->_resume_info_path )
    {
        my $record = decode_json( _slurp( $self->_file->_resume_info_path ) );
        $self->_len( $record->{_len} );
        $self->_downloaded->_my_init( $record->{_bytes_dled} );
        $self->_remaining_connections( $record->{_remaining_connections} );
        my $ranges_ref = $record->{_ranges};
        $self->_init_from_len(
            {
                ranges          => $ranges_ref,
                num_connections => scalar(@$ranges_ref),
            }
        );
    }
    else
    {
        my $url = $self->_file->_url;

        if ( $self->_file->_is_ftp )
        {
            my $ftp = AnyEvent::FTP::Client->new( passive => 1 );
            $ftp->connect( $url->host, $url->port )->recv;
            $ftp->login( $url->user, $url->password )->recv;
            $ftp->type('I')->recv;
            $ftp->size( $self->_file->_url_path )->cb(
                sub {
                    my $len = shift->recv;

                    $ftp->quit;
                    undef($ftp);

                    return $self->_with_len_and_num_connections( $len,
                        $num_connections );
                }
            );
        }
        else
        {
            http_head $url, sub {
                my ( undef, $headers ) = @_;
                my $len = $headers->{'content-length'};

                return $self->_with_len_and_num_connections( $len,
                    $num_connections );
            };
        }
    }

    my $signal_handler = sub { $self->_abort_signal_handler(); };
    local $SIG{INT}  = $signal_handler;
    local $SIG{TERM} = $signal_handler;

    $self->_finished_condvar->recv;
    $self->_stats_timer( undef() );

    if ( !$self->_remaining_connections() )
    {
        rename( $self->_file->_downloading_path(),
            $self->_file->_url_basename() );
    }

    return;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::ManiacDownloader - a maniac download accelerator.

=head1 VERSION

version 0.0.13

=head1 SYNOPSIS

    # To download with 10 segments



( run in 0.759 second using v1.01-cache-2.11-cpan-ceb78f64989 )