snoopy 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-24.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ require "rubygems"
7
+ require "snoopy"
8
+
9
+ ##################################################################################
10
+
11
+ opts = Trollop::options do
12
+ version "snoopy 0.0.4 (c) Ari Brown"
13
+ banner <<-EOS
14
+ snoopy is a multithreaded golden retriever for the internet
15
+ --> handles http, ftp (single threaded), and torrent
16
+
17
+ Usage:
18
+ snoopy <URI of file> [options]
19
+ Example:
20
+ snoopy http://www.lighttpd.net -t 5 option
21
+ where [options] are:
22
+ EOS
23
+
24
+ opt :verbose, "Be as loud as possible"
25
+ opt :clean, "Remove the out file after finishing (debugging)"
26
+ opt :out, "Where to save the file", :type => String
27
+ opt :threads, "Set number of threads to use", :default => 5
28
+ opt :dots, "Display a dot every X times a block comes in", :default => 10
29
+ end
30
+ Trollop::die "need a file to download" if ARGV.empty?
31
+
32
+ ##################################################################################
33
+
34
+ opts[:in] = ARGV[0]
35
+
36
+ url = URI.parse(opts[:in])
37
+
38
+ ball = Source.new(url, opts)
39
+
40
+ def start_report(opts)
41
+ puts Time.now
42
+ puts "Downloading #{opts[:in]}"
43
+ puts "with #{opts[:threads]} thread" + (opts[:threads] > 1 ? "s" : '')
44
+ puts
45
+ end
46
+
47
+ def determine_size(value)
48
+ if value >= 1073741824
49
+ end_value = value / 1073741824
50
+ unit = 'GB'
51
+ elsif value >= 1048576 and value < 1073741824
52
+ end_value = value / 1048576
53
+ unit = 'MB'
54
+ elsif value >= 1024 and value < 1048576
55
+ end_value = value / 1024
56
+ unit = 'KB'
57
+ else
58
+ end_value = value
59
+ unit = 'B'
60
+ end
61
+
62
+ [end_value, unit, value]
63
+ end
64
+
65
+ def end_report(source, opts)
66
+ puts
67
+ puts "Download file written to " + opts[:out] + (opts[:clean] ? ", and then deleted" : '')
68
+ puts "Elapsed time: " + $elapsed_time.to_s + " seconds"
69
+
70
+ end_value, unit, actual = determine_size(source.length)
71
+
72
+ if end_value.zero?
73
+ end_value, unit, actual = determine_size(File.size(opts[:out]))
74
+ end
75
+
76
+ puts "Total downloaded: " + end_value.to_s + " " + unit
77
+ res = determine_size(actual/$elapsed_time)
78
+ puts "At %.2f%s/s" % [res[0], res[1]]
79
+
80
+ source.finish
81
+
82
+
83
+ end
84
+
85
+
86
+
87
+ start_report(opts)
88
+ ##################################################################################
89
+ $start_time = Time.now
90
+ ##################################################################################
91
+
92
+ ball.roll
93
+
94
+ ##################################################################################
95
+ $elapsed_time = Time.now - $start_time
96
+ ##################################################################################
97
+ end_report(ball, opts)
@@ -0,0 +1,22 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module FTP
7
+
8
+ def setup(opts, uri)
9
+ @opts = opts
10
+ end
11
+
12
+ def prep
13
+ end
14
+
15
+ def fetch
16
+ Net::FTP.open(@opts[:in]) {|ftp| ftp.getbinaryfile(@opts[:in], @opts[:out]) }
17
+ end
18
+
19
+ def clean
20
+ end
21
+
22
+ end
@@ -0,0 +1,94 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module HTTP
7
+ class << self
8
+ def setup(opts, uri)
9
+ @opts = opts
10
+ @url = uri
11
+ end
12
+
13
+ def prep
14
+ req = Net::HTTP.new(@url.host, @url.port)
15
+ len = req.request_head(@url.page)['content-length']
16
+ @url.length = len.to_i
17
+
18
+ if len.nil? or @url.length == 0
19
+ @opts[:threads] = 1
20
+ puts "--> Uhoh! Server isn't cooperating..."
21
+ puts "--> Changing threadcount to 1"
22
+ alias_method :fetch_multi, :fetch
23
+ alias_method :fetch, :fetch_single
24
+ end
25
+ end
26
+
27
+ def fetch_single
28
+ p single
29
+ open(@opts[:out], "w") do |f|
30
+ http = Net::HTTP.new(@url.host, @url.port)
31
+ http.get(@url.page) do |str|
32
+ f.write str
33
+ end
34
+ end
35
+ end
36
+
37
+ def fetch
38
+ i = 0
39
+ array = []
40
+ start = 0
41
+ jump = @url.length / @opts[:threads]
42
+ finish = -1 # -1 so that start becomes 0
43
+
44
+ @opts[:threads].times do |t|
45
+ start = finish + 1
46
+ finish = (start + jump) - 1
47
+ array << [start, finish, t + 1]
48
+ end
49
+
50
+ array.each_simultaneously { |d, b, w|
51
+ puts "[LOG] Setting up a new thread..." if @opts[:verbose]
52
+ open(@opts[:out], "a+") do |f|
53
+ begin
54
+ f.seek(d)
55
+ Net::HTTP.new(@url.host, @url.port).start do |http|
56
+ http.get(@url.page, {'range' => "bytes=#{d}-#{b}"}) do |str|
57
+ i += 1
58
+ print '.' if i % @opts[:dots] == 0
59
+ STDOUT.flush
60
+ f.write str if str
61
+ end
62
+ end
63
+ rescue Timeout::Error => e
64
+ print "\n[Error] Error in server response in thread #{w}. Retrying!\n"
65
+ puts e
66
+ STDOUT.flush
67
+ sleep 5
68
+ retry
69
+ rescue => e
70
+ print "\n[Error] Error in server response in thread #{w}. Retrying!\n"
71
+ puts e
72
+ STDOUT.flush
73
+ sleep 5
74
+ retry
75
+ end
76
+ end
77
+ puts "[LOG] Thread #{w} finished!" if @opts[:verbose]
78
+ }
79
+ end
80
+
81
+ def clean
82
+ remove_extra_files
83
+ end
84
+
85
+ def remove_extra_files
86
+ puts "[LOG] Writing #{@opts[:out]}" if @opts[:verbose]
87
+
88
+ if @opts[:clean]
89
+ File.delete(@opts[:out])
90
+ puts "[LOG] Removing #{@opts[:out]}" if @opts[:verbose]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,113 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module HTTPS
7
+ class << self
8
+ def setup(opts, uri)
9
+ @opts = opts
10
+ @url = uri
11
+ end
12
+
13
+ def prep
14
+ req = Net::HTTP.new(@url.host, @url.port)
15
+ req.use_ssl = true
16
+ len = req.request_head(@url.page)['content-length']
17
+ @url.length = len.to_i
18
+
19
+ if len.nil?
20
+ @opts[:threads] = 1
21
+ puts "--> Uhoh! Server isn't cooperating..."
22
+ puts "--> Changing threadcount to 1"
23
+ end
24
+ end
25
+
26
+ def fetch
27
+ array = []
28
+ start = 0
29
+ jump = @url.length / @opts[:threads]
30
+ finish = -1 # -1 so that start becomes 0
31
+
32
+ @opts[:threads].times do |t|
33
+ start = finish + 1
34
+ finish = (start + jump) - 1
35
+ array << [start, finish, t + 1]
36
+ end
37
+
38
+ array.each_simultaneously { |d, b, w|
39
+ puts "[LOG] Setting up a new thread..." if @opts[:verbose]
40
+ p @opts[:out]
41
+ open(@opts[:out], "a+") do |f|
42
+ f.seek(d)
43
+ http = Net::HTTP.new(@url.host, @url.port)
44
+ http.use_ssl = true
45
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
46
+ c = http.start do |http|
47
+ a = Net::HTTP::Get.new(@url.page)
48
+ a.range = (d..b)
49
+ http.request(a)
50
+ end
51
+ f.write(c.body)
52
+ end
53
+ }
54
+ end
55
+ =begin
56
+ def fetch_fat
57
+ array = []
58
+ start = 0
59
+ jump = @url.length / @opts[:threads]
60
+ finish = -1 # -1 so that start becomes 0
61
+
62
+ @opts[:threads].times do |t|
63
+ start = finish + 1
64
+ finish = (start + jump) - 1
65
+ array << [start, finish, t + 1]
66
+ end
67
+
68
+ array.each_simultaneously { |d, b, w|
69
+ puts "[LOG] Setting up a new thread..." if @opts[:verbose]
70
+ open("/tmp/part#{w}", "w") do |f|
71
+ http = Net::HTTP.new(@url.host, @url.port)
72
+ c = http.start do |http|
73
+ a = Net::HTTP::Get.new(@url.page)
74
+ a.range = (d..b)
75
+ http.request(a)
76
+ end
77
+ f.write(c.body)
78
+ end
79
+ }
80
+ end
81
+ =end
82
+ def clean
83
+ mash_files
84
+ remove_extra_files
85
+ end
86
+
87
+ def remove_extra_files
88
+ puts "[LOG] Writing #{@opts[:out]}" if @opts[:verbose]
89
+
90
+ Dir["/tmp/part*"].each do |part|
91
+ File.delete(part)
92
+ puts "[LOG] Removing #{part}" if @opts[:verbose]
93
+ end
94
+
95
+ if @opts[:clean]
96
+ File.delete(@opts[:out])
97
+ puts "[LOG] Removing #{@opts[:out]}" if @opts[:verbose]
98
+ end
99
+ end
100
+
101
+ def mash_files
102
+ open(@opts[:out], "a") do |f|
103
+ files = Dir["/tmp/part*"].sort_by { |word|
104
+ word.scan(/\d+/)[0].to_i
105
+ }
106
+ files.each do |part|
107
+ puts "[LOG] Reading #{part}" if @opts[:verbose]
108
+ f.print open(part, "r").read
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,22 @@
1
+ require "rubygems"
2
+ require "trollop"
3
+ require "net/http"
4
+ require "net/https"
5
+ require "net/ftp"
6
+ require "rubytorrent"
7
+ require "tempfile"
8
+ require "open-uri"
9
+
10
+ require File.join(File.dirname(__FILE__), "source")
11
+ require File.join(File.dirname(__FILE__), "http")
12
+ require File.join(File.dirname(__FILE__), "https")
13
+ require File.join(File.dirname(__FILE__), "ftp")
14
+
15
+
16
+ module Enumerable
17
+ def each_simultaneously
18
+ threads = []
19
+ each { |e, f, i| threads << Thread.new { yield e, f, i } }
20
+ return threads.each {|t| t.join }
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ class Source
7
+
8
+ attr_accessor :port, :scheme, :host, :path, :length, :page, :full
9
+
10
+ def initialize(uri_obj, opts)
11
+ @uri = uri_obj
12
+ @host = uri_obj.host
13
+ @port = uri_obj.port
14
+ @scheme = uri_obj.scheme
15
+ @page = (uri_obj.path =~ /^$/ ? "/" : uri_obj.path)
16
+ @full = @scheme + '://' + @host + @page
17
+ @in = opts[:in]
18
+ @out = opts[:out]
19
+ opts[:out] = opts[:out] || (File.basename(@page) =~ /^\/$/ ? 'default' : File.basename(@page))
20
+ opts[:out] = (File.directory?(opts[:out]) ? File.join(opts[:out], File.basename(@page)) : opts[:out])
21
+
22
+ case @scheme
23
+ when "http" then @prefix = HTTP
24
+ when "https" then @prefix = HTTPS
25
+ when "ftp" then @prefix = FTP
26
+ when "torrent" then @prefix = Torrent
27
+ else
28
+ abort "Unknown URL scheme!\nsnoopy --help for help and usage"
29
+ end
30
+
31
+ prep(opts)
32
+
33
+ end
34
+
35
+ def start
36
+ @prefix.prep
37
+ @prefix.fetch
38
+ end
39
+ alias_method :roll, :start
40
+
41
+ def finish
42
+ @prefix.clean
43
+ end
44
+
45
+ private
46
+ def prep(opts)
47
+ @prefix.setup(opts, self)
48
+ end
49
+
50
+ end
@@ -0,0 +1,90 @@
1
+ #!/usr/local/bin/ruby
2
+ #
3
+ # Created by me on 2007-12-28.
4
+ # Copyright (c) 2007. All pwnage reserved.
5
+
6
+ module Torrent
7
+ class << self
8
+
9
+ def setup(opts, uri)
10
+ @opts = opts
11
+ @url = uri
12
+ end
13
+
14
+ def prep
15
+ begin
16
+ mi = RubyTorrent::MetaInfo.from_location(File.join(@url.host, @url.path), ENV["http_proxy"])
17
+ rescue RubyTorrent::MetaInfoFormatError, RubyTorrent::BEncodingError => e
18
+ abort "[Error] Can't parse metainfo file '#{torrent}' -- maybe not a .torrent?"
19
+ rescue RubyTorrent::TypedStructError => e
20
+ raise e
21
+ rescue IOError, SystemCallError => e
22
+ abort "[Error] Can't read file '#{torrent}': #{e.message}"
23
+ end
24
+
25
+
26
+ if FileTest.directory?(@opts[:out]) && mi.info.single?
27
+ dest = File.join(@opts[:out], mi.info.name)
28
+ elsif FileTest.file?(@opts[:out]) && mi.info.multiple?
29
+ abort "[Error] .torrent contains multiple files, but '#{dest}' is a single file (must be a directory)"
30
+ end
31
+ dest ||= @opts[:out]
32
+
33
+ print "[LOG] Checking file status: " if @opts[:verbose]
34
+ package = RubyTorrent::Package.new(mi, dest) do |piece|
35
+ print(piece.complete? && piece.valid? ? "#" : ".") if @opts[:verbose]
36
+ end
37
+ puts " done" if @opts[:verbose]
38
+ end
39
+
40
+ def fetch
41
+ puts "[Log] Starting peer..." if @opts[:verbose]
42
+ bt = RubyTorrent::BitTorrent.new(mi, package, :http_proxy => proxy)
43
+
44
+ if @opts[:verbose]
45
+ bt.on_event(self, :added_peer) { |s, p| puts "[Log] Connected to peer #{p}" }
46
+ bt.on_event(self, :received_block) { |s, b, peer| puts "<-- got block #{b} from peer #{peer}, now #{package.pieces[b.pindex].percent_done.f}% done and #{package.pieces[b.pindex].percent_claimed.f}% claimed" }
47
+ bt.on_event(self, :sent_block) { |s, b, peer| puts "--> Sent block #{b} to peer #{peer}" }
48
+ bt.on_event(self, :discarded_piece) { |s, p| puts "[Log] Checksum error on piece #{p}, discarded" }
49
+ bt.on_event(self, :tracker_connected) { |s, url| puts "--> Connected to tracker #{url}" }
50
+ bt.on_event(self, :tracker_lost) { |s, url| puts "--> Couldn't connect to tracker #{url}" }
51
+ end
52
+
53
+ # Always register this
54
+ bt.on_event(self, :complete) do
55
+ @url.length = bt.dlamt
56
+ puts "--> Download complete!"
57
+ end
58
+
59
+ puts "[Log] Listening on #{bt.ip}:#{bt.port}" if @opts[:verbose]
60
+
61
+
62
+ thread = Thread.new do
63
+ until bt.complete?
64
+ previous = ''
65
+
66
+ if bt.tracker
67
+
68
+ current = '%s: %dk of %dk (%.2f%% complete)' % [Time.now,
69
+ bt.bytes_completed / 1024,
70
+ bt.total_bytes / 1024,
71
+ bt.percent_completed]
72
+ print "\b" * previous.size
73
+ print curent
74
+ previous = ''
75
+
76
+ sleep(5)
77
+ else
78
+ sleep(5)
79
+ end
80
+ end
81
+ end
82
+
83
+ thread.join
84
+ end
85
+
86
+ def clean
87
+ end
88
+
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snoopy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ari Brown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-01-14 00:00:00 -05:00
13
+ default_executable: snoopy
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubytorrent
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0.3"
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: trollop
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 0.0.1
32
+ version:
33
+ description: Segmented downloader for HTTP(S)/FTP/Bit Torrent.
34
+ email: ari@aribrown.com
35
+ executables:
36
+ - snoopy
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/ftp.rb
43
+ - lib/http.rb
44
+ - lib/https.rb
45
+ - lib/snoopy.rb
46
+ - lib/source.rb
47
+ - lib/torrent.rb
48
+ - bin/snoopy
49
+ has_rdoc: false
50
+ homepage: http://snoopy.rubyforge.org/
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.0.0
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: Snoopy is a golden retriever for the internet. It's a great big world and there... And you need a great big dog to do your bidding.
75
+ test_files: []
76
+