quartz_torrent 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.
- data/bin/quartztorrent_download +127 -0
- data/bin/quartztorrent_download_curses +841 -0
- data/bin/quartztorrent_magnet_from_torrent +32 -0
- data/bin/quartztorrent_show_info +62 -0
- data/lib/quartz_torrent.rb +2 -0
- data/lib/quartz_torrent/bitfield.rb +314 -0
- data/lib/quartz_torrent/blockstate.rb +354 -0
- data/lib/quartz_torrent/classifiedpeers.rb +95 -0
- data/lib/quartz_torrent/extension.rb +37 -0
- data/lib/quartz_torrent/filemanager.rb +543 -0
- data/lib/quartz_torrent/formatter.rb +92 -0
- data/lib/quartz_torrent/httptrackerclient.rb +121 -0
- data/lib/quartz_torrent/interruptiblesleep.rb +27 -0
- data/lib/quartz_torrent/log.rb +132 -0
- data/lib/quartz_torrent/magnet.rb +92 -0
- data/lib/quartz_torrent/memprofiler.rb +27 -0
- data/lib/quartz_torrent/metainfo.rb +221 -0
- data/lib/quartz_torrent/metainfopiecestate.rb +265 -0
- data/lib/quartz_torrent/peer.rb +145 -0
- data/lib/quartz_torrent/peerclient.rb +1627 -0
- data/lib/quartz_torrent/peerholder.rb +123 -0
- data/lib/quartz_torrent/peermanager.rb +170 -0
- data/lib/quartz_torrent/peermsg.rb +502 -0
- data/lib/quartz_torrent/peermsgserialization.rb +102 -0
- data/lib/quartz_torrent/piecemanagerrequestmetadata.rb +12 -0
- data/lib/quartz_torrent/rate.rb +58 -0
- data/lib/quartz_torrent/ratelimit.rb +48 -0
- data/lib/quartz_torrent/reactor.rb +949 -0
- data/lib/quartz_torrent/regionmap.rb +124 -0
- data/lib/quartz_torrent/semaphore.rb +43 -0
- data/lib/quartz_torrent/trackerclient.rb +271 -0
- data/lib/quartz_torrent/udptrackerclient.rb +70 -0
- data/lib/quartz_torrent/udptrackermsg.rb +250 -0
- data/lib/quartz_torrent/util.rb +100 -0
- metadata +195 -0
@@ -0,0 +1,250 @@
|
|
1
|
+
module QuartzTorrent
|
2
|
+
class UdpTrackerMessage
|
3
|
+
ActionConnect = 0
|
4
|
+
ActionAnnounce = 1
|
5
|
+
ActionScrape = 2
|
6
|
+
ActionError = 3
|
7
|
+
|
8
|
+
EventNone = 0
|
9
|
+
EventCompleted = 1
|
10
|
+
EventStarted = 2
|
11
|
+
EventStopped = 3
|
12
|
+
|
13
|
+
# Pack the number 'num' as a network byte order signed integer, 'len' bytes long.
|
14
|
+
# Negative numbers are written in two's-compliment notation.
|
15
|
+
def self.packAsNetworkOrder(num, len)
|
16
|
+
result = ""
|
17
|
+
len.times do
|
18
|
+
result << (num & 0xff)
|
19
|
+
num >>= 8
|
20
|
+
end
|
21
|
+
result.reverse
|
22
|
+
end
|
23
|
+
|
24
|
+
# Unpack the number stored in 'str' assuming it is a network byte order signed integer.
|
25
|
+
# Negative numbers are assumed to be in two's-compliment notation.
|
26
|
+
def self.unpackNetworkOrder(str, len = nil)
|
27
|
+
result = 0
|
28
|
+
first = true
|
29
|
+
negative = false
|
30
|
+
index = 0
|
31
|
+
str.each_byte do |b|
|
32
|
+
if first
|
33
|
+
negative = (b & 0x80) > 0
|
34
|
+
first = false
|
35
|
+
end
|
36
|
+
result <<= 8
|
37
|
+
result += b
|
38
|
+
index += 1
|
39
|
+
break if len && index == len
|
40
|
+
end
|
41
|
+
if negative
|
42
|
+
# Internally the value is being represented unsigned. To make it signed,
|
43
|
+
# we first take the ones compliment of the value, remove the sign bit, and add one.
|
44
|
+
# This takes the two's compliment of the two's compliment of the number, which results
|
45
|
+
# in the absolute value of the original number. Finally we use the unary - operator to
|
46
|
+
# make the value negative.
|
47
|
+
result = -(((~result) & 0x7fffffffffffffff) + 1)
|
48
|
+
end
|
49
|
+
result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Superclass for UDP tracker requests.
|
54
|
+
class UdpTrackerRequest
|
55
|
+
def initialize
|
56
|
+
@connectionId = 0x41727101980
|
57
|
+
@action = UdpTrackerMessage::ActionConnect
|
58
|
+
# Get a number that is a valid 32-bit signed integer.
|
59
|
+
@transactionId = rand(0x10000000)-8000000
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the connectionId as an integer
|
63
|
+
attr_reader :connectionId
|
64
|
+
# Get the action as an integer. Should be one of the UdpTrackerMessage::Action* constants
|
65
|
+
attr_reader :action
|
66
|
+
# Get the transactionId as an integer.
|
67
|
+
attr_reader :transactionId
|
68
|
+
|
69
|
+
# Set the connectionId. Value must be an integer
|
70
|
+
def connectionId=(v)
|
71
|
+
raise "The 'connectionId' field must be an integer" if ! v.is_a?(Integer)
|
72
|
+
@connectionId = v
|
73
|
+
end
|
74
|
+
# Set the action. Value should be one of the UdpTrackerMessage::Action* constants
|
75
|
+
def action=(v)
|
76
|
+
raise "The 'action' field must be an integer" if ! v.is_a?(Integer)
|
77
|
+
@action = v
|
78
|
+
end
|
79
|
+
# Set the transactionId. Value must be an integer. If not set a random number is used as per the specification.
|
80
|
+
def transactionId=(v)
|
81
|
+
raise "The 'transactionId' field must be an integer" if ! v.is_a?(Integer)
|
82
|
+
@transactionId = v
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Superclass for UDP tracker responses
|
87
|
+
class UdpTrackerResponse
|
88
|
+
def initialize
|
89
|
+
@connectionId = nil
|
90
|
+
@action = nil
|
91
|
+
@transactionId = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
# ConnectionId as an integer
|
95
|
+
attr_accessor :connectionId
|
96
|
+
# Get the action as an integer. Should be one of the UdpTrackerMessage::Action* constants
|
97
|
+
attr_accessor :action
|
98
|
+
# Get the transactionId as an integer
|
99
|
+
attr_accessor :transactionId
|
100
|
+
end
|
101
|
+
|
102
|
+
class UdpTrackerConnectRequest < UdpTrackerRequest
|
103
|
+
def serialize
|
104
|
+
result = UdpTrackerMessage::packAsNetworkOrder(@connectionId, 8)
|
105
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@action, 4)
|
106
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@transactionId, 4)
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class UdpTrackerConnectResponse < UdpTrackerResponse
|
112
|
+
def initialize
|
113
|
+
super
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.unserialize(msg)
|
117
|
+
raise "Invalid connect response: it is #{msg.length} when it must be at least 16" if msg.length < 16
|
118
|
+
result = UdpTrackerConnectResponse.new
|
119
|
+
result.action = UdpTrackerMessage::unpackNetworkOrder(msg,4)
|
120
|
+
result.transactionId = UdpTrackerMessage::unpackNetworkOrder(msg[4,4],4)
|
121
|
+
result.connectionId = UdpTrackerMessage::unpackNetworkOrder(msg[8,8],8)
|
122
|
+
raise "Invalid connect response: action is not connect" if result.action != UdpTrackerMessage::ActionConnect
|
123
|
+
result
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.tohex(str)
|
127
|
+
result = ""
|
128
|
+
str.each_byte do |b|
|
129
|
+
result << b.to_s(16)
|
130
|
+
end
|
131
|
+
result
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class UdpTrackerAnnounceRequest < UdpTrackerRequest
|
136
|
+
def initialize(connectionId)
|
137
|
+
super()
|
138
|
+
@connectionId = connectionId
|
139
|
+
@action = UdpTrackerMessage::ActionAnnounce
|
140
|
+
@infoHash = nil
|
141
|
+
@peerId = nil
|
142
|
+
@downloaded = nil
|
143
|
+
@left = nil
|
144
|
+
@uploaded = nil
|
145
|
+
@event = nil
|
146
|
+
# 0 means allow tracker to assume the sender's IP address is the one it's looking for
|
147
|
+
# http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html
|
148
|
+
@ipAddress = 0
|
149
|
+
@key = rand(0xffffffff)
|
150
|
+
# Number of peers requested: default.
|
151
|
+
@numWant = -1
|
152
|
+
@port = nil
|
153
|
+
end
|
154
|
+
|
155
|
+
attr_accessor :infoHash
|
156
|
+
attr_accessor :peerId
|
157
|
+
attr_reader :downloaded
|
158
|
+
attr_reader :left
|
159
|
+
attr_reader :uploaded
|
160
|
+
attr_reader :event
|
161
|
+
attr_accessor :ipAddress
|
162
|
+
attr_accessor :key
|
163
|
+
attr_accessor :numWant
|
164
|
+
attr_accessor :port
|
165
|
+
|
166
|
+
def downloaded=(v)
|
167
|
+
raise "The 'downloaded' field must be an integer" if ! v.is_a?(Integer)
|
168
|
+
@downloaded = v
|
169
|
+
end
|
170
|
+
|
171
|
+
def left=(v)
|
172
|
+
raise "The 'left' field must be an integer" if ! v.is_a?(Integer)
|
173
|
+
@left = v
|
174
|
+
end
|
175
|
+
|
176
|
+
def uploaded=(v)
|
177
|
+
raise "The 'uploaded' field must be an integer" if ! v.is_a?(Integer)
|
178
|
+
@uploaded = v
|
179
|
+
end
|
180
|
+
|
181
|
+
def event=(v)
|
182
|
+
raise "The 'event' field must be an integer" if ! v.is_a?(Integer)
|
183
|
+
@event = v
|
184
|
+
end
|
185
|
+
|
186
|
+
def numWant=(v)
|
187
|
+
raise "The 'numWant' field must be an integer" if ! v.is_a?(Integer)
|
188
|
+
@numWant = v
|
189
|
+
end
|
190
|
+
|
191
|
+
def port=(v)
|
192
|
+
raise "The 'port' field must be an integer" if ! v.is_a?(Integer)
|
193
|
+
@port = v
|
194
|
+
end
|
195
|
+
|
196
|
+
def serialize
|
197
|
+
result = UdpTrackerMessage::packAsNetworkOrder(@connectionId, 8)
|
198
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@action, 4)
|
199
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@transactionId, 4)
|
200
|
+
result << infoHash
|
201
|
+
result << peerId
|
202
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@downloaded, 8)
|
203
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@left, 8)
|
204
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@uploaded, 8)
|
205
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@event, 4)
|
206
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@ipAddress, 4)
|
207
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@key, 4)
|
208
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@numWant, 4)
|
209
|
+
result << UdpTrackerMessage::packAsNetworkOrder(@port, 2)
|
210
|
+
result
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class UdpTrackerAnnounceResponse < UdpTrackerResponse
|
215
|
+
def initialize
|
216
|
+
super
|
217
|
+
@interval = nil
|
218
|
+
@leechers = nil
|
219
|
+
@seeders = nil
|
220
|
+
@ips = []
|
221
|
+
@ports = []
|
222
|
+
end
|
223
|
+
|
224
|
+
attr_accessor :interval
|
225
|
+
attr_accessor :leechers
|
226
|
+
attr_accessor :seeders
|
227
|
+
attr_accessor :ips
|
228
|
+
attr_accessor :ports
|
229
|
+
|
230
|
+
def self.unserialize(msg)
|
231
|
+
raise "Invalid connect response: it is #{msg.length} when it must be at least 20" if msg.length < 20
|
232
|
+
result = UdpTrackerAnnounceResponse.new
|
233
|
+
result.action = UdpTrackerMessage::unpackNetworkOrder(msg,4)
|
234
|
+
result.transactionId = UdpTrackerMessage::unpackNetworkOrder(msg[4,4],4)
|
235
|
+
result.interval = UdpTrackerMessage::unpackNetworkOrder(msg[8,4],4)
|
236
|
+
result.leechers = UdpTrackerMessage::unpackNetworkOrder(msg[12,4],4)
|
237
|
+
result.seeders = UdpTrackerMessage::unpackNetworkOrder(msg[16,4],4)
|
238
|
+
|
239
|
+
index = 20
|
240
|
+
while index+6 < msg.length
|
241
|
+
result.ips.push msg[index,4]
|
242
|
+
result.ports.push msg[index+4,2]
|
243
|
+
index += 6
|
244
|
+
end
|
245
|
+
result
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
class Hash
|
2
|
+
# Treat the array as a hash of lists. This method will append 'value' to the list
|
3
|
+
# at key 'key' in the hash. If there is no list for 'key', one is created.
|
4
|
+
def pushToList(key, value)
|
5
|
+
list = self[key]
|
6
|
+
if ! list
|
7
|
+
list = []
|
8
|
+
self[key] = list
|
9
|
+
end
|
10
|
+
list.push value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module QuartzTorrent
|
15
|
+
# This is Linux specific: system call number for gettid
|
16
|
+
SYSCALL_GETTID = 224
|
17
|
+
|
18
|
+
# Return a hex string representing the bytes in the passed string.
|
19
|
+
def self.bytesToHex(v, addSpaces = nil)
|
20
|
+
s = ""
|
21
|
+
v.each_byte{ |b|
|
22
|
+
hex = b.to_s(16)
|
23
|
+
hex = "0" + hex if hex.length == 1
|
24
|
+
s << hex
|
25
|
+
s << " " if addSpaces == :add_spaces
|
26
|
+
}
|
27
|
+
s
|
28
|
+
end
|
29
|
+
|
30
|
+
# Given a hex string representing a sequence of bytes, convert it the the original bytes. Inverse of bytesToHex.
|
31
|
+
def self.hexToBytes(v)
|
32
|
+
[v].pack "H*"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Shuffle the subset of elements in the given array between start and start+length-1 inclusive.
|
36
|
+
def self.arrayShuffleRange!(array, start, length)
|
37
|
+
raise "Invalid range" if start + length > array.size
|
38
|
+
|
39
|
+
(start+length).downto(start+1) do |i|
|
40
|
+
r = start + rand(i-start)
|
41
|
+
array[r], array[i-1] = array[i-1], array[r]
|
42
|
+
end
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
# Store Linux Lightweight process ids (LWPID) on each thread.
|
47
|
+
# If this is called a while before logBacktraces the backtraces will
|
48
|
+
# include lwpids.
|
49
|
+
def self.setThreadLwpid(thread = nil)
|
50
|
+
# This function works by calling the GETTID system call in Linux. That
|
51
|
+
# system call must be called in the thread that we want to get the lwpid of,
|
52
|
+
# but the user may not have created those threads and so can't call the system call
|
53
|
+
# in those threads (think Sinatra). To get around this this function runs code in the
|
54
|
+
# thread by adding a trace function to the thread, and on the first traced operation
|
55
|
+
# stores the LWPID on the thread and unregisters itself.
|
56
|
+
|
57
|
+
isLinux = RUBY_PLATFORM.downcase.include?("linux")
|
58
|
+
return if !isLinux
|
59
|
+
|
60
|
+
tracer = Proc.new do
|
61
|
+
Thread.current[:lwpid] = syscall(SYSCALL_GETTID) if ! Thread.current[:lwpid] && isLinux
|
62
|
+
Thread.current.set_trace_func(nil)
|
63
|
+
end
|
64
|
+
|
65
|
+
if thread
|
66
|
+
thread.set_trace_func(tracer)
|
67
|
+
else
|
68
|
+
Thread.list.each { |thread| thread.set_trace_func(tracer) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Log backtraces of all threads currently running. The threads are logged to the
|
73
|
+
# passed io, or if that's nil they are written to the logger named 'util' at error level.
|
74
|
+
def self.logBacktraces(io)
|
75
|
+
logger = nil
|
76
|
+
logger = LogManager.getLogger("util") if ! io
|
77
|
+
isLinux = RUBY_PLATFORM.downcase.include?("linux")
|
78
|
+
|
79
|
+
Thread.list.each do |thread|
|
80
|
+
lwpid = ""
|
81
|
+
|
82
|
+
setThreadLwpid thread if ! thread[:lwpid] && isLinux
|
83
|
+
lwpid = " [lwpid #{thread[:lwpid]}]" if thread[:lwpid]
|
84
|
+
|
85
|
+
msg = "Thread #{thread[:name]} #{thread.object_id}#{lwpid}: #{thread.status}\n " + (thread.backtrace ? thread.backtrace.join("\n ") : "no backtrace")
|
86
|
+
if io
|
87
|
+
io.puts msg
|
88
|
+
else
|
89
|
+
logger.error msg
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Method to set a few thread-local variables useful in debugging. Threads should call this when started.
|
95
|
+
def self.initThread(name)
|
96
|
+
Thread.current[:name] = name
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quartz_torrent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jeff Williams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bencode
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.8'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.8'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pqueue
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: base32
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.2'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: log4r
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.1'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.1'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: minitest
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: yard
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: redcarpet
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: A pure ruby bittorrent library
|
127
|
+
email:
|
128
|
+
executables:
|
129
|
+
- quartztorrent_download
|
130
|
+
- quartztorrent_download_curses
|
131
|
+
- quartztorrent_magnet_from_torrent
|
132
|
+
- quartztorrent_show_info
|
133
|
+
extensions: []
|
134
|
+
extra_rdoc_files: []
|
135
|
+
files:
|
136
|
+
- lib/quartz_torrent.rb
|
137
|
+
- lib/quartz_torrent/blockstate.rb
|
138
|
+
- lib/quartz_torrent/memprofiler.rb
|
139
|
+
- lib/quartz_torrent/regionmap.rb
|
140
|
+
- lib/quartz_torrent/httptrackerclient.rb
|
141
|
+
- lib/quartz_torrent/interruptiblesleep.rb
|
142
|
+
- lib/quartz_torrent/extension.rb
|
143
|
+
- lib/quartz_torrent/util.rb
|
144
|
+
- lib/quartz_torrent/log.rb
|
145
|
+
- lib/quartz_torrent/metainfo.rb
|
146
|
+
- lib/quartz_torrent/trackerclient.rb
|
147
|
+
- lib/quartz_torrent/peermsgserialization.rb
|
148
|
+
- lib/quartz_torrent/semaphore.rb
|
149
|
+
- lib/quartz_torrent/reactor.rb
|
150
|
+
- lib/quartz_torrent/metainfopiecestate.rb
|
151
|
+
- lib/quartz_torrent/peer.rb
|
152
|
+
- lib/quartz_torrent/peerclient.rb
|
153
|
+
- lib/quartz_torrent/formatter.rb
|
154
|
+
- lib/quartz_torrent/piecemanagerrequestmetadata.rb
|
155
|
+
- lib/quartz_torrent/peerholder.rb
|
156
|
+
- lib/quartz_torrent/rate.rb
|
157
|
+
- lib/quartz_torrent/peermanager.rb
|
158
|
+
- lib/quartz_torrent/bitfield.rb
|
159
|
+
- lib/quartz_torrent/udptrackermsg.rb
|
160
|
+
- lib/quartz_torrent/ratelimit.rb
|
161
|
+
- lib/quartz_torrent/classifiedpeers.rb
|
162
|
+
- lib/quartz_torrent/peermsg.rb
|
163
|
+
- lib/quartz_torrent/filemanager.rb
|
164
|
+
- lib/quartz_torrent/magnet.rb
|
165
|
+
- lib/quartz_torrent/udptrackerclient.rb
|
166
|
+
- bin/quartztorrent_download
|
167
|
+
- bin/quartztorrent_download_curses
|
168
|
+
- bin/quartztorrent_magnet_from_torrent
|
169
|
+
- bin/quartztorrent_show_info
|
170
|
+
homepage: https://github.com/jeffwilliams/quartz-torrent
|
171
|
+
licenses: []
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options: []
|
174
|
+
require_paths:
|
175
|
+
- lib
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
none: false
|
184
|
+
requirements:
|
185
|
+
- - ! '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
requirements: []
|
189
|
+
rubyforge_project:
|
190
|
+
rubygems_version: 1.8.23
|
191
|
+
signing_key:
|
192
|
+
specification_version: 3
|
193
|
+
summary: A bittorrent library
|
194
|
+
test_files: []
|
195
|
+
has_rdoc:
|