Downloader
Tool module for downloading files and extracting archive files. Currently this is console-based but in the future will have SOC for any interface.
- add_mirror
- add_mirrors
- compute_checksum
- download
- extract
- fetch
- interface
- interface=
- mirrors
- monitored_download
- new
- prioritize_urls
[RW] | destination | |
[RW] | region |
[ show source ]
# File lib/more/facets/downloader.rb, line 66 def initialize( destination, mirrors=nil, region=nil, &config ) @destination = destination @mirrors = [] add_mirrors( mirrors ) if mirrors @region = (region || 'US').to_s config.call(self) if config end
[ show source ]
# File lib/more/facets/downloader.rb, line 76 def add_mirror( url, region=nil ) @mirrors << Mirror.new( url, region ) end
[ show source ]
# File lib/more/facets/downloader.rb, line 80 def add_mirrors( mirrors ) mirrors.each { |mirror| case mirror when String url, rgn = *mirror.strip.split(' ') add_mirror( url, rgn.unbracket ) when Array add_mirror( *mirror ) when Hash add_mirror( mirror[:url], mirror[:region] ) when Mirror @mirrors << mirror else raise "unrecogized mirror definition #{mirror.inspect}" end } end
[ show source ]
# File lib/more/facets/downloader.rb, line 240 def compute_checksum( local_path ) if File.exists?( local_path ) File.open( local_path ) do |local_file| return Digest::MD5.new( local_file.read ).hexdigest end end end
currently can only download a single compressed file does not handle downloading an uncompressed directory tree (should it? doubt it)
currently this displays progress to STDOUT; either their should be a way to activate/deactivate or preferably use ducktype singletons (more on that later, see google://_whytheluckystiff if interested) of course I prefer chain messaging but matz said no :(
[ show source ]
# File lib/more/facets/downloader.rb, line 179 def download( url, filepath, checksum='', est_size=nil ) checksum = checksum.to_s.strip est_size = nil if est_size == 0 download_complete = nil if interface interface.preparing_to_download( File.basename( filepath ), url, est_size ) end progress_total = est_size ? est_size : 100000000 # pretend 100MB if no size pbar = ProgressBar.new( "Status", progress_total, STDOUT ) pbar.bar_mark = "=" pbar.format = "%-6s %3d%% %s %s" pbar.file_transfer_mode if est_size progress_proc = proc { |posit| pbar.set(posit) } STDOUT.sync = true begin local_file = File.open( filepath, 'wb' ) remote_file = open( url, :progress_proc => progress_proc ) local_file << remote_file.read rescue pbar.halt download_complete = nil raise else pbar.finish download_complete = filepath ensure remote_file.close unless remote_file.nil? local_file.close unless local_file.nil? STDOUT.sync = false end unless checksum.empty? raise ChecksumError if compute_checksum(filepath) != checksum end if interface if checksum.empty? interface.lacks_checksum( compute_checksum(filepath), :md5 ) end unless est_size interface.lacks_size( File.size(filepath) ) end end if download_complete if interface interface.downloaded( filepath ) end end return download_complete end
[ show source ]
# File lib/more/facets/downloader.rb, line 250 def extract( local_path ) success = false local_dir = File.dirname(local_path) local_file = File.basename(local_path) current_dir = Dir.getwd begin Dir.chdir(local_dir) case local_file when /.*gz$/ system "tar -xzf #{local_file}" when /.*bz2$/ system "tar -xjf #{local_file}" when /.zip$/ system "unzip #{local_file}" else success = false end rescue success = false else success = true ensure Dir.chdir(current_dir) end if interface interface.extracted( local_path) end return success end
[ show source ]
# File lib/more/facets/downloader.rb, line 104 def fetch( file, checksum=0, est_size=0, force=false ) urls = mirrors.collect { |m| "#{m.url.chomp('/')}/#{file}" } urls = prioritize_urls( urls ) filepath = "#{destination.chomp('/')}/#{file}" monitored_download( urls, filepath, checksum, est_size, force ) end
delegate to an interface (under development)
[ show source ]
# File lib/more/facets/downloader.rb, line 61 def interface ; @interface; end
[ show source ]
# File lib/more/facets/downloader.rb, line 62 def interface=(iface) @interface = iface end
[ show source ]
# File lib/more/facets/downloader.rb, line 98 def mirrors @mirrors.sort{ |a,b| a.region == region ? 1 : ( b.region == region ? -1 : 0 ) } end
regional_urls - array of arrays of [ url, region, md5, expected_size ] local_region - region of the user‘s system to_dir - where to store downloaded file (full path) force - download even if file already exists locally
[ show source ]
# File lib/more/facets/downloader.rb, line 125 def monitored_download( urls, filepath, checksum, est_size=0, force=false ) checksum = checksum.to_s.strip est_size = nil if est_size == 0 success=nil # source file exists and passes checksum then we need not fetch #file_path = File.join(to_dir,File.basename(url[0])) if File.exists?(filepath) if compute_checksum(filepath) == checksum and ! force interface.report("File has already been fetched and passes checksum.") success = filepath else File.delete(filepath) end end unless success # download urls.each do |url| begin #file_path = File.join(to_dir,File.basename(url[0])) #file_checksum = url[2].to_s.strip #file_size = url[3].to_i success = self.download( url, filepath, checksum, est_size ) break if success rescue next end end end return success end
In the future we may test each connection for fastest download
[ show source ]
# File lib/more/facets/downloader.rb, line 163 def prioritize_urls( urls ) urls # put local region first #prioritized_urls = regional_urls.find_all { |a| a[1] == local_region } #prioritized_urls.concat regional_urls.find_all { |a| a[1] != local_region } #return prioritized_urls end