deluge 1.0.0
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.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +25 -0
- data/app.gemspec +15 -0
- data/lib/deluge.rb +265 -0
- data/lib/deluge/protocol.rb +3 -0
- data/lib/deluge/protocol/v3.rb +166 -0
- data/test/test_helper.rb +10 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 621e509656351a32651a96cca7575b275f5c42ed
|
4
|
+
data.tar.gz: 10b04f90dcfd5b5b9b3457138a868289a284be8c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 603a6c8ac060f091897379b867b11edc52fc71477ffb2df708bad52693a3d8b9abd4abdb545a1e22101585f8b5f399e0e0c5f33dd501ab6c0e282f28388217b1
|
7
|
+
data.tar.gz: 1c989baf119615cf58072f27ec645f23ad22f9a57ad341ebf6742099710e95169fb5512f235d921b8aabe82d308e1dcd9e3fcda43cfd55cbd9ff59240136b998
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Mikael Wikman
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## Deluge Ruby Client
|
2
|
+
|
3
|
+
This is a client library written in Ruby for communicating with Deluge daemon process. This implementation currently only supports V3 of the protocol (any version with major number 3, e.g. Deluge 3.3).
|
4
|
+
|
5
|
+
It has all core and daemon RPC functions defined, and you can access any plugin exported functions through the Deluge#call method.
|
6
|
+
|
7
|
+
### Usage
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem install deluge
|
11
|
+
|
12
|
+
# defaults are 'localhost', '58846'
|
13
|
+
d = Deluge.new '192.168.1.11', '58800'
|
14
|
+
|
15
|
+
d.login 'user', 'pass'
|
16
|
+
|
17
|
+
d.add_torrent_url 'http://some-evil-tracker.com/juicy.torrent'
|
18
|
+
|
19
|
+
# example of the dynamic Deluge#call method
|
20
|
+
d.call 'webui.get_config', {}
|
21
|
+
```
|
22
|
+
|
23
|
+
### Methods
|
24
|
+
|
25
|
+
See _lib/deluge_ for all defined methods.
|
data/app.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = ['Mikael Wikman']
|
5
|
+
gem.email = ['mikael@wikman.me']
|
6
|
+
gem.description = %q{}
|
7
|
+
gem.summary = %q{Ruby implementation of the Deluge RPC API}
|
8
|
+
gem.files = `git ls-files`.split($\)
|
9
|
+
gem.homepage = 'https://github.com/mikaelwikman/deluge-ruby'
|
10
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
gem.test_files = gem.files.grep(%r{^(test|features)/})
|
12
|
+
gem.name = "deluge"
|
13
|
+
gem.require_paths = ["lib"]
|
14
|
+
gem.version = '1.0.0'
|
15
|
+
end
|
data/lib/deluge.rb
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'deluge/protocol'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
class Deluge
|
5
|
+
|
6
|
+
def initialize host='localhost', port='58846', protocol=3
|
7
|
+
@protocol = protocol
|
8
|
+
|
9
|
+
if protocol == 3
|
10
|
+
@con = Protocol::V3.new(host, port)
|
11
|
+
else
|
12
|
+
raise "Unsupported protocol #{protocol}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def call *args
|
17
|
+
@con.call *args
|
18
|
+
end
|
19
|
+
|
20
|
+
def login username, password
|
21
|
+
@con.call 'daemon.login', username, password
|
22
|
+
end
|
23
|
+
|
24
|
+
def shutdown args, kwargs={}
|
25
|
+
args = [args] unless args.is_a?(Array)
|
26
|
+
@con.call 'daemon.shutdown', args, kwargs
|
27
|
+
end
|
28
|
+
|
29
|
+
def info
|
30
|
+
@con.call 'daemon.info'
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_method_list
|
34
|
+
@con.call 'daemon.get_method_list'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Available options:
|
38
|
+
# max_connections max_upload_slots max_upload_speed max_download_speed prioritize_first_last_pieces file_priorities compact_allocation download_location auto_managed stop_at_ratio stop_ratio remove_at_ratio move_completed move_completed_path add_paused
|
39
|
+
def add_torrent_file filename, filedump, options={}
|
40
|
+
filedump_enc = Base64.encode64(filedump)
|
41
|
+
@con.call 'core.add_torrent_file', filename, filedump_enc, options, {}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Available options:
|
45
|
+
# max_connections max_upload_slots max_upload_speed max_download_speed prioritize_first_last_pieces file_priorities compact_allocation download_location auto_managed stop_at_ratio stop_ratio remove_at_ratio move_completed move_completed_path add_paused
|
46
|
+
def add_torrent_url url, options={}
|
47
|
+
@con.call 'core.add_torrent_url', url, options, {}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Available options:
|
51
|
+
# max_connections max_upload_slots max_upload_speed max_download_speed prioritize_first_last_pieces file_priorities compact_allocation download_location auto_managed stop_at_ratio stop_ratio remove_at_ratio move_completed move_completed_path add_paused
|
52
|
+
def add_torrent_magnet uri, options={}
|
53
|
+
@con.call 'core.add_torrent_magnet', uri, options, {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove_torrent torrent_id, remove_data: false
|
57
|
+
@con.call 'core.remove_torrent', torrent_id, remove_data
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Gets the session status values for 'keys', these keys are taking
|
62
|
+
# from libtorrent's session status.
|
63
|
+
|
64
|
+
# See: http://www.rasterbar.com/products/libtorrent/manual.html#status
|
65
|
+
def get_session_status keys
|
66
|
+
@con.call 'core.get_session_status', keys, {}
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_cache_status
|
70
|
+
@con.call 'core.get_cache_status', {}
|
71
|
+
end
|
72
|
+
|
73
|
+
def force_reannounce *torrent_ids
|
74
|
+
@con.call 'core.force_reannounce', torrent_ids, {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def pause_torrent *torrent_ids
|
78
|
+
@con.call 'core.pause_torrent', torrent_ids, {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def connect_peer torrent_id, ip, port
|
82
|
+
@con.call 'core.connect_peer', torrent_id, ip, port, {}
|
83
|
+
end
|
84
|
+
|
85
|
+
def move_storage *torrent_ids, dest
|
86
|
+
@con.call 'core.move_storage', torrent_ids, dest, {}
|
87
|
+
end
|
88
|
+
|
89
|
+
def pause_all_torrents
|
90
|
+
@con.call 'core.pause_all_torrents', {}
|
91
|
+
end
|
92
|
+
|
93
|
+
def resume_all_torrents
|
94
|
+
@con.call 'core.resume_all_torrents', {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def resume_torrent *torrent_ids
|
98
|
+
@con.call 'core.resume_torrent', torrent_ids, {}
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_torrent_status torrent_id, keys, diff=false
|
102
|
+
@con.call 'core.get_torrent_status', torrent_id, keys, diff, {}
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_filter_tree show_zero_hits=true, hide_cat=nil
|
106
|
+
@con.call 'core.get_filter_tree', show_zero_hits, hide_cat, {}
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_session_state
|
110
|
+
@con.call 'core.get_session_state', {}
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_config
|
114
|
+
@con.call 'core.get_config', {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_config_values *keys
|
118
|
+
@con.call 'core.get_config_values', keys, {}
|
119
|
+
end
|
120
|
+
|
121
|
+
def set_config config
|
122
|
+
@con.call 'core.set_config', config, {}
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_listen_port
|
126
|
+
@con.call 'core.get_listen_port', {}
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_num_connections
|
130
|
+
@con.call 'core.get_num_connections', {}
|
131
|
+
end
|
132
|
+
|
133
|
+
def get_available_plugins
|
134
|
+
@con.call 'core.get_available_plugins', {}
|
135
|
+
end
|
136
|
+
|
137
|
+
def get_enabled_plugins
|
138
|
+
@con.call 'core.get_enabled_plugins', {}
|
139
|
+
end
|
140
|
+
|
141
|
+
def enable_plugin plugin
|
142
|
+
@con.call 'core.enable_plugin', plugin, {}
|
143
|
+
end
|
144
|
+
|
145
|
+
def disable_plugin plugin
|
146
|
+
@con.call 'core.disable_plugin', plugin, {}
|
147
|
+
end
|
148
|
+
|
149
|
+
def force_recheck *torrent_ids
|
150
|
+
@con.call 'core.force_recheck', torrent_ids, {}
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_torrent_options torrent_ids, options={}
|
154
|
+
torrent_ids = [torrent_ids] unless torrent_ids.kind_of?(Array)
|
155
|
+
@con.call 'core.set_torrent_options',torrent_ids, options, {}
|
156
|
+
end
|
157
|
+
|
158
|
+
def set_torrent_trackers torrent_id, *trackers
|
159
|
+
@con.call 'core.set_torrent_trackers', torrent_id, trackers, {}
|
160
|
+
end
|
161
|
+
|
162
|
+
def set_torrent_max_connections torrent_id, value
|
163
|
+
@con.call 'core.set_torrent_max_connections', torrent_id, value, {}
|
164
|
+
end
|
165
|
+
|
166
|
+
def set_torrent_max_upload_slots torrent_id, value
|
167
|
+
@con.call 'core.set_torrent_max_upload_stats', torrent_id, value, {}
|
168
|
+
end
|
169
|
+
|
170
|
+
def set_torrent_max_upload_speed torrent_id, value
|
171
|
+
@con.call 'core.set_torrent_max_upload_speed', torrent_id, value, {}
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_torrent_max_download_speed torrent_id, value
|
175
|
+
@con.call 'core.set_torrent_max_download_speed', torrent_id, value, {}
|
176
|
+
end
|
177
|
+
|
178
|
+
def set_torrent_file_priorities torrent_id, priorities
|
179
|
+
@con.call 'core.set_torrent_file_priorities', torrent_id, priorities, {}
|
180
|
+
end
|
181
|
+
|
182
|
+
def set_torrent_prioritize_first_last torrent_id, value
|
183
|
+
@con.call 'core.set_torrent_prioritize_first_last', torrent_id, value, {}
|
184
|
+
end
|
185
|
+
|
186
|
+
def set_torrent_auto_managed torrent_id, value
|
187
|
+
@con.call 'core.set_torrent_auto_managed', torrent_id, value, {}
|
188
|
+
end
|
189
|
+
|
190
|
+
def set_torrent_stop_at_ratio torrent_id, value
|
191
|
+
@con.call 'core.set_torrent_stop_at_ratio', torrent_id, value, {}
|
192
|
+
end
|
193
|
+
|
194
|
+
def set_torrent_stop_ratio torrent_id, value
|
195
|
+
@con.call 'core.set_torrent_stop_ratio', torrent_id, value, {}
|
196
|
+
end
|
197
|
+
|
198
|
+
def set_torrent_remove_at_ratio torrent_id, value
|
199
|
+
@con.call 'core.set_torrent_remove_at_ratio', torrent_id, value, {}
|
200
|
+
end
|
201
|
+
|
202
|
+
def set_torrent_move_completed torrent_id, value
|
203
|
+
@con.call 'core.set_torrent_move_completed', torrent_id, value, {}
|
204
|
+
end
|
205
|
+
|
206
|
+
def set_torrent_move_completed_path torrent_id, value
|
207
|
+
@con.call 'core.set_torrent_move_completed_path', torrent_id, value, {}
|
208
|
+
end
|
209
|
+
|
210
|
+
def get_path_size path
|
211
|
+
@con.call 'core.get_path_size', path, {}
|
212
|
+
end
|
213
|
+
|
214
|
+
def create_torrent path, tracker, piece_length, comment, target, webseeds, privat, created_by, trackers, add_to_session
|
215
|
+
@con.call 'core.create_torrent', path, tracker, piece_length, comment, target, webseeds, privat, created_by, trackers, add_to_session, {}
|
216
|
+
end
|
217
|
+
|
218
|
+
def upload_plugin filename, filedump
|
219
|
+
@con.call 'core.upload_plugin', filename, filedump, {}
|
220
|
+
end
|
221
|
+
|
222
|
+
def rescan_plugins
|
223
|
+
@con.call 'core.rescan_plugins', {}
|
224
|
+
end
|
225
|
+
|
226
|
+
def rename_files torrent_id, filenames
|
227
|
+
@con.call 'core.rename_files', torrent_id, filenames, {}
|
228
|
+
end
|
229
|
+
|
230
|
+
def rename_folder torrent_id, folder, new_folder
|
231
|
+
@con.call 'core.rename_folder', torrent_id, folder, new_folder, {}
|
232
|
+
end
|
233
|
+
|
234
|
+
def queue_top *torrent_ids
|
235
|
+
@con.call 'core.queue_top', torrent_ids, {}
|
236
|
+
end
|
237
|
+
|
238
|
+
def queue_up *torrent_ids
|
239
|
+
@con.call 'core.queue_up', torrent_ids, {}
|
240
|
+
end
|
241
|
+
|
242
|
+
def queue_down *torrent_ids
|
243
|
+
@con.call 'core.queue_down', torrent_ids, {}
|
244
|
+
end
|
245
|
+
|
246
|
+
def queue_bottom *torrent_ids
|
247
|
+
@con.call 'core.queue_bottom', torrent_ids, {}
|
248
|
+
end
|
249
|
+
|
250
|
+
def glob path
|
251
|
+
@con.call 'core.glob', path, {}
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_listen_port
|
255
|
+
@con.call 'core.test_listen_port', {}
|
256
|
+
end
|
257
|
+
|
258
|
+
def get_free_space path=nil
|
259
|
+
@con.call 'core.get_free_space', path, {}
|
260
|
+
end
|
261
|
+
|
262
|
+
def get_libtorrent_version
|
263
|
+
@con.call 'core.get_libtorrent_version'
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'rencode'
|
2
|
+
require 'zlib'
|
3
|
+
require 'socket'
|
4
|
+
require 'thread'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
class Deluge
|
8
|
+
class Protocol::V3
|
9
|
+
# Data messages are transfered using very a simple protocol.
|
10
|
+
# Data messages are transfered with a header containing
|
11
|
+
# the length of the data to be transfered (payload).
|
12
|
+
|
13
|
+
def initialize host, port
|
14
|
+
@buffer = []
|
15
|
+
@message_length = 0
|
16
|
+
@messages = []
|
17
|
+
@mutex = Mutex.new
|
18
|
+
@host = host
|
19
|
+
@port = port
|
20
|
+
@counter = 0
|
21
|
+
|
22
|
+
connection = TCPSocket.new(host, port)
|
23
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
24
|
+
ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
25
|
+
ctx.ssl_version = :SSLv3
|
26
|
+
|
27
|
+
@con = OpenSSL::SSL::SSLSocket.new(connection, ctx)
|
28
|
+
@con.connect
|
29
|
+
|
30
|
+
Thread.new do
|
31
|
+
loop do
|
32
|
+
begin
|
33
|
+
data = @con.readpartial(1024)
|
34
|
+
|
35
|
+
if data.length == 0
|
36
|
+
puts "We lost the connection!"
|
37
|
+
break
|
38
|
+
end
|
39
|
+
|
40
|
+
handle_new_data(data.bytes)
|
41
|
+
|
42
|
+
rescue Exception => e
|
43
|
+
puts e.message
|
44
|
+
p e.backtrace
|
45
|
+
sleep 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def call method, *args
|
52
|
+
kwargs = if args.last.is_a?(Hash)
|
53
|
+
args.delete_at(-1)
|
54
|
+
else
|
55
|
+
{}
|
56
|
+
end
|
57
|
+
|
58
|
+
request_id = @counter+=1
|
59
|
+
|
60
|
+
send([request_id, method, args, kwargs])
|
61
|
+
|
62
|
+
response = nil
|
63
|
+
|
64
|
+
sleep 0.01 until response = @messages.find{|m| m[1] == request_id}
|
65
|
+
@messages.delete(response)
|
66
|
+
|
67
|
+
if response[0] == 1
|
68
|
+
return response[2]
|
69
|
+
else
|
70
|
+
raise response.inspect
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Transfer the data.
|
75
|
+
|
76
|
+
# The data will be serialized and compressed before being sent.
|
77
|
+
# First a header is sent - containing the length of the compressed payload
|
78
|
+
# to come as a signed integer. After the header, the payload is transfered.
|
79
|
+
# :param msg: data to be transfered in a data structure serializable by rencode.
|
80
|
+
def send msg
|
81
|
+
rencoded = REncode.dump([msg]).pack('C*')
|
82
|
+
#rencoded = [193, 196, 1, 139, 100, 97, 101, 109, 111, 110, 46, 105, 110, 102, 111, 192, 102].pack('C*')
|
83
|
+
compressed = Zlib::Deflate.deflate(rencoded)
|
84
|
+
raw = compressed.bytes
|
85
|
+
|
86
|
+
# all commented out stuff is for version 4, which we do not yet support
|
87
|
+
# Store length as a signed integer (using 4 bytes), network byte order
|
88
|
+
# header = [raw.length].pack('N').bytes
|
89
|
+
|
90
|
+
#every message begins with an ASCII 'D'
|
91
|
+
# header.insert(0, 'D'.ord)
|
92
|
+
|
93
|
+
# header_str = header.pack('C*')
|
94
|
+
message_str = raw.pack('C*')
|
95
|
+
|
96
|
+
# puts "Writing header:"
|
97
|
+
# p header_str.bytes
|
98
|
+
# puts
|
99
|
+
# puts "Writing message:"
|
100
|
+
# p rencoded.bytes
|
101
|
+
# puts
|
102
|
+
|
103
|
+
|
104
|
+
# @con.write(header_str)
|
105
|
+
@con.write(message_str)
|
106
|
+
@con.flush
|
107
|
+
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
def read
|
112
|
+
@mutex.synchronize do
|
113
|
+
@messages.shift
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def count
|
118
|
+
@mutex.synchronize do
|
119
|
+
@messages.count
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def handle_new_data data, &block
|
126
|
+
# commented code is for version 4, not yet supported
|
127
|
+
# @buffer.concat(data)
|
128
|
+
#
|
129
|
+
# while @buffer.length >= 5 # 'D' + 4 byte integer
|
130
|
+
# if @message_length == 0
|
131
|
+
# handle_new_message
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# if @buffer.length >= @message_length
|
135
|
+
# message = @buffer.shift(@message_length)
|
136
|
+
# @message_length = 0
|
137
|
+
#
|
138
|
+
message = data
|
139
|
+
handle_complete_message(message)
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
end
|
143
|
+
|
144
|
+
# def handle_new_message
|
145
|
+
# header = @buffer.shift(5)
|
146
|
+
# verify_d = header.shift(1)
|
147
|
+
#
|
148
|
+
# unless verify_d == 'D'.ord
|
149
|
+
# raise "Invalid header format, First byte is #{verify_d}"
|
150
|
+
# else
|
151
|
+
# @message_length = header.pack('C*').unpack('N')[0]
|
152
|
+
# raise "Message length is negative: #{@message_length}" if @message_length < 0
|
153
|
+
# end
|
154
|
+
# end
|
155
|
+
|
156
|
+
def handle_complete_message data
|
157
|
+
decompressed = Zlib::Inflate.inflate(data.pack('C*'))
|
158
|
+
derencoded = REncode.parse(decompressed)
|
159
|
+
|
160
|
+
@mutex.synchronize do
|
161
|
+
@messages << derencoded
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deluge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikael Wikman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: ''
|
14
|
+
email:
|
15
|
+
- mikael@wikman.me
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- LICENSE.txt
|
22
|
+
- README.md
|
23
|
+
- app.gemspec
|
24
|
+
- lib/deluge.rb
|
25
|
+
- lib/deluge/protocol.rb
|
26
|
+
- lib/deluge/protocol/v3.rb
|
27
|
+
- test/test_helper.rb
|
28
|
+
homepage: https://github.com/mikaelwikman/deluge-ruby
|
29
|
+
licenses: []
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.2.2
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: Ruby implementation of the Deluge RPC API
|
51
|
+
test_files:
|
52
|
+
- test/test_helper.rb
|