#!/usr/bin/env perl # aria2.wrapper - Wrapper for "aria2" # License: BSD-style (for this file only) # Revision: 081223 #--------------------------------------------------------------------- # explanation #--------------------------------------------------------------------- # 1. This is a wrapper for "aria2" that adds three features: # # (a) The wrapper tries to set the output-file timestamp correct- # ly. # # (b) Optional: The wrapper sets a default "stream" count for # files larger than a predefined size. # # (c) The wrapper sets the output file's "permissions" to 0644. #--------------------------------------------------------------------- # 2. These features are only supported for HTTP and/or FTP URLs. If no # HTTP or FTP URLs are specified, the wrapper's behavior is more or # less identical to standard "aria2" behavior. #--------------------------------------------------------------------- # 3. The default stream count is controlled by two source-level param- # eters: # # $DEFAULT_STREAM_COUNT - Default value is two # $DEFAULT_STREAM_TRIGGER_SIZE - Default value is one megabyte # # If the user doesn't specify a "stream" count, and if the target-file # size is at least $DEFAULT_STREAM_TRIGGER_SIZE bytes [or unknown], # the wrapper sets the "stream" count equal to $DEFAULT_STREAM_ # COUNT. # To disable this feature, set $DEFAULT_STREAM_COUNT equal to zero or # one. # To enable this feature, set $DEFAULT_STREAM_COUNT equal to an inte- # ger from two to five. #--------------------------------------------------------------------- # module setup #--------------------------------------------------------------------- require 5.6.0; use strict; use Carp; use warnings; use LWP::UserAgent; # Trap warnings $SIG {__WARN__} = sub { die @_; }; #--------------------------------------------------------------------- # basic constants #--------------------------------------------------------------------- use constant ZERO => 0; # Zero use constant ONE => 1; # One use constant TWO => 2; # Two use constant FALSE => 0; # Boolean FALSE use constant TRUE => 1; # Boolean TRUE #--------------------------------------------------------------------- # program parameters #--------------------------------------------------------------------- # If $DEFAULT_STREAM_COUNT is greater than one, it specifies a de- # fault "stream" count. # $DEFAULT_STREAM_COUNT applies to cases where the target-file size is # greater than $DEFAULT_STREAM_TRIGGER_SIZE [or unknown] and the user # didn't specify a "stream" count. # Note: To disable this feature, set $DEFAULT_STREAM_COUNT equal to # zero or one. my $DEFAULT_STREAM_COUNT = 2; my $DEFAULT_STREAM_TRIGGER_SIZE = 1024 * 1024; #--------------------------------------------------------------------- # If this wrapper is used, the "aria2" executable must be renamed. # $PROG_REAL_ARIA2 specifies the name used for the "aria2" executa- # ble on the local system. # If the executable can be accessed through PATH, a filename is suf- # ficient; i.e., a full pathname isn't required. # Note: $PROG_REAL_ARIA2 must *not* be equal to "aria2" (or a path- # name ending with "/aria2"). my $PROG_REAL_ARIA2 = 'aria2.bin'; # Consistency check die "aria2 wrapper: Internal error\n" if $PROG_REAL_ARIA2 =~ m@^(.*/)?aria2\z@; #--------------------------------------------------------------------- # main routine #--------------------------------------------------------------------- sub Main { my @ARGS = @ARGV; # Command-line arguments my $n_streams = ""; # Number of streams my $ofname = ""; # Output-file name my $str; # Scratch my $FTP_URL = ""; # First FTP URL [if any] my $HTTP_URL = ""; # First HTTP URL [if any] my $oldtime = ""; # Old local-file timestamp my $newtime = ""; # New local-file timestamp my $remsize = ""; # Remote-file size my $remtime = ""; # Remote-file timestamp #--------------------------------------------------------------------- # Scan command-line arguments. for my $ii (ZERO..$#ARGS) { my $arg = $ARGS [$ii]; # Current argument # Get first HTTP URL [if any] $HTTP_URL = $arg if !length ($HTTP_URL) && ($arg =~ m@^http://@ ); # Get first FTP URL [if any] $FTP_URL = $arg if !length ($FTP_URL ) && ($arg =~ m@^ftp://@ ); # Check for output-file name switch if (($arg eq '-o') && ($ii < $#ARGS)) { # Specified name using "-o" $ofname = $ARGS [$ii + ONE]; } elsif ($arg =~ m@^--out=(.+)\z@) { # Specified name using "--out" $ofname = $1; } # Check for "stream" count if (($arg eq '-s') && ($ii < $#ARGS)) { # Specified value using "-s" $n_streams = $ARGS [$ii + ONE]; } elsif ($arg =~ m@^--split=(.+)\z@) { # Specified value using "--split" $n_streams = $1; } } #--------------------------------------------------------------------- # Set base URL. # $BASE_URL specifies a reference URL that's used for multiple pur- # poses. # Note: If the user doesn't specify any HTTP or FTP URLs, $BASE_URL # will be empty. my $BASE_URL = length ($HTTP_URL) ? $HTTP_URL : $FTP_URL; #--------------------------------------------------------------------- # Check [or set] output-file name. # This code tries to emulate the approach that "aria2" uses to set # the default output-file name. Note: "aria2" apparently takes what- # ever comes after the last slash in the URL(s) used, even if the # slash occurs inside a CGI parameter. if (!length ($ofname)) # Did user specify a filename? { # No - Need to determine it if (length ($str = $BASE_URL)) { $str =~ s@/\z@/index.html@; $str =~ s@^.*/@@; $str =~ s@^\s+@@; $str =~ s@\s+\z@@; $ofname = $str if length $str; } } #--------------------------------------------------------------------- # Save old timestamp [if any]. if ((-e $ofname) && (-f $ofname)) { $oldtime = (stat $ofname) [9]; $oldtime = "" if !defined $oldtime; $oldtime = "" if $oldtime !~ m@^\d+\z@; } #--------------------------------------------------------------------- # Get remote parameters [if available]. # Technical notes: # # 1. The user-agent setting specified here is consistent with the val- # ue used by the local version of "aria2". # # 2. HTTP::Request supports both HTTP and FTP URLs. if (length ($BASE_URL)) { # Create an LWP "user agent" my $UserAgent = LWP::UserAgent->new; # See notes at start of section $UserAgent->agent ("Mozilla/5.0"); # Request information about file my $request = HTTP::Request->new (HEAD => $BASE_URL); my $result = $UserAgent->request ($request); # Check the result if (!$result->is_success) { # Error die "Error: $BASE_URL: ", $result->status_line, "\n"; } # Pointer to HTTP::Headers structure my $headers = $$result {_headers}; # Check remote-file size value $remsize = $headers->content_length; $remsize = "" if !defined $remsize; $remsize = "" if $remsize !~ m@^\d+\z@; # Check remote-file timestamp value $remtime = $headers->last_modified; $remtime = "" if !defined $remtime; $remtime = "" if $remtime !~ m@^\d+\z@; } #--------------------------------------------------------------------- # Set default stream count [if applicable]. if (!length ($n_streams) && ($DEFAULT_STREAM_COUNT > ONE) && (!length ($remsize) || ($remsize >= $DEFAULT_STREAM_TRIGGER_SIZE))) { unshift (@ARGS, "--split=$DEFAULT_STREAM_COUNT"); } #--------------------------------------------------------------------- # Run the actual [renamed] "aria2" executable. my $WaitCode = system $PROG_REAL_ARIA2, @ARGS; my $StatusCode = int ($WaitCode / 256); #--------------------------------------------------------------------- # Handle a special case. # If no HTTP or FTP URLs were specified, we're done. exit $StatusCode unless length $BASE_URL; #--------------------------------------------------------------------- # Get new output-file timestamp. if (length ($ofname) && (-f $ofname)) { $newtime = (stat $ofname) [9]; $newtime = "" if !defined $newtime; $newtime = "" if $newtime !~ m@^\d+\z@; } #--------------------------------------------------------------------- # Wrap it up. # This code sets the output-file "permissions". If we obtained a rem- # ote timestamp and the output file exists, this code also sets the # local timestamp. # Note: "aria2" (the executable, as opposed to the wrapper) doesn't # preserve timestamps. Therefore, if the output file existed before # we started, and its timestamp hasn't changed, two problems may have # occurred: # # (a) "aria2" wasn't able to download a new copy, or # (b) We've got the wrong output-file name # # In either case, the "permissions" and "timestamp" steps are skip- # ped. my $OldTimeIsKnown = length ($oldtime) ? TRUE : FALSE; my $OldTimeIsUnknown = !$OldTimeIsKnown; my $ModTimeHasChanged = $OldTimeIsKnown && ($oldtime ne $newtime); # Safe to proceed? if (length ($ofname) && (-f $ofname) && length ($newtime) && ($OldTimeIsUnknown || $ModTimeHasChanged)) { # Probably chmod (0644, $ofname); utime ($remtime, $remtime, $ofname) if length ($remtime); } exit $StatusCode; # Exit the program } #--------------------------------------------------------------------- # main program #--------------------------------------------------------------------- &Main(); # Call the main routine exit ONE; # Not reached