transmission-rpc-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +19 -0
- data/README.md +78 -0
- data/Rakefile +3 -0
- data/lib/transmission.rb +8 -0
- data/lib/transmission/arguments.rb +27 -0
- data/lib/transmission/arguments/session_get.rb +63 -0
- data/lib/transmission/arguments/session_set.rb +58 -0
- data/lib/transmission/arguments/session_stats.rb +17 -0
- data/lib/transmission/arguments/torrent_add.rb +22 -0
- data/lib/transmission/arguments/torrent_get.rb +78 -0
- data/lib/transmission/arguments/torrent_set.rb +34 -0
- data/lib/transmission/config.rb +18 -0
- data/lib/transmission/model.rb +8 -0
- data/lib/transmission/model/session.rb +35 -0
- data/lib/transmission/model/torrent.rb +97 -0
- data/lib/transmission/rpc.rb +108 -0
- data/lib/transmission/rpc/connector.rb +69 -0
- data/spec/helpers/stubs.rb +60 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/transmission/arguments_spec.rb +34 -0
- data/spec/transmission/model/torrent_spec.rb +146 -0
- data/spec/transmission/rpc/connector_spec.rb +81 -0
- data/spec/transmission/rpc_spec.rb +41 -0
- data/transmission-rpc-ruby.gemspec +17 -0
- metadata +128 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Transmission
|
2
|
+
class Arguments
|
3
|
+
class TorrentSet < Transmission::Arguments
|
4
|
+
|
5
|
+
ATTRIBUTES = [
|
6
|
+
'bandwidthPriority',
|
7
|
+
'downloadLimit',
|
8
|
+
'downloadLimited',
|
9
|
+
'downloadLimit',
|
10
|
+
'files-wanted',
|
11
|
+
'files-unwanted',
|
12
|
+
'honorsSessionLimits',
|
13
|
+
'ids',
|
14
|
+
'location',
|
15
|
+
'peer-limit',
|
16
|
+
'priority-high',
|
17
|
+
'priority-low',
|
18
|
+
'priority-normal',
|
19
|
+
'queuePosition',
|
20
|
+
'seedIdleLimit',
|
21
|
+
'seedIdleMode',
|
22
|
+
'seedRatioLimit',
|
23
|
+
'seedRatioMode',
|
24
|
+
'trackerAdd',
|
25
|
+
'trackerRemove',
|
26
|
+
'trackerReplace',
|
27
|
+
'uploadLimit',
|
28
|
+
'uploadLimited',
|
29
|
+
'uploadLimit'
|
30
|
+
]
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Transmission
|
2
|
+
class Config
|
3
|
+
class << self
|
4
|
+
def set(options = {})
|
5
|
+
@@config = options
|
6
|
+
@@connector = Transmission::RPC.new @@config
|
7
|
+
end
|
8
|
+
|
9
|
+
def get
|
10
|
+
@@config
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_connector
|
14
|
+
@@connector
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Transmission
|
2
|
+
module Model
|
3
|
+
class Session
|
4
|
+
class SessionError < StandardError; end
|
5
|
+
|
6
|
+
attr_accessor :attributes
|
7
|
+
|
8
|
+
def initialize(session_object)
|
9
|
+
@attributes = session_object
|
10
|
+
end
|
11
|
+
|
12
|
+
def version
|
13
|
+
@attributes['version'].split(' ').first
|
14
|
+
end
|
15
|
+
|
16
|
+
def rpc_version
|
17
|
+
@attributes['rpc-version']
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def get(options = {})
|
22
|
+
rpc = options[:connector] || connector
|
23
|
+
session_body = rpc.get_session options
|
24
|
+
session_stats_body = rpc.get_session_stats options
|
25
|
+
merged_body = session_body.merge(session_stats_body)
|
26
|
+
Session.new merged_body
|
27
|
+
end
|
28
|
+
|
29
|
+
def connector
|
30
|
+
Transmission::Config.get_connector
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Transmission
|
2
|
+
module Model
|
3
|
+
class Torrent
|
4
|
+
class TorrentError < StandardError; end
|
5
|
+
class TorrentNotFoundError < StandardError; end
|
6
|
+
class MissingAttributesError < StandardError; end
|
7
|
+
class DuplicateTorrentError < StandardError; end
|
8
|
+
|
9
|
+
attr_accessor :attributes, :deleted
|
10
|
+
|
11
|
+
def initialize(torrent_object)
|
12
|
+
@attributes = torrent_object
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete!(remove_local_data = false)
|
16
|
+
Torrent.connector.remove_torrent [self.attributes['id']], remove_local_data
|
17
|
+
@deleted = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def set
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def move_up
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def move_down
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def move_top
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def move_bottom
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def start
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def start_now
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def stop
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def verify
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def re_announce
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def finished?
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_json
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
class << self
|
69
|
+
def all(options = {})
|
70
|
+
rpc = options[:connector] || connector
|
71
|
+
body = rpc.get_torrent nil, options
|
72
|
+
body['torrents'].inject([]) do |torrents, torrent|
|
73
|
+
torrents << Torrent.new(torrent)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def find(id, options = {})
|
78
|
+
rpc = options[:connector] || connector
|
79
|
+
body = rpc.get_torrent [id], options
|
80
|
+
raise TorrentNotFoundError if body['torrents'].size == 0
|
81
|
+
Torrent.new body['torrents'].first
|
82
|
+
end
|
83
|
+
|
84
|
+
def add(options = {})
|
85
|
+
rpc = options[:connector] || connector
|
86
|
+
body = rpc.add_torrent options[:arguments]
|
87
|
+
raise DuplicateTorrentError if body['torrent-duplicate']
|
88
|
+
find body['torrent-added']['id']
|
89
|
+
end
|
90
|
+
|
91
|
+
def connector
|
92
|
+
Transmission::Config.get_connector
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'rpc', 'connector')
|
2
|
+
|
3
|
+
module Transmission
|
4
|
+
class RPC
|
5
|
+
|
6
|
+
attr_accessor :session, :connector
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@connector = Connector.new options
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_session(options = {})
|
13
|
+
fields = Transmission::Arguments::SessionGet.new(options[:fields])
|
14
|
+
arguments = {fields: fields.to_arguments}
|
15
|
+
@connector.post method: 'session-get', arguments: arguments
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_session(arguments)
|
19
|
+
@connector.post method: 'session-set', arguments: arguments
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_session_stats(options = {})
|
23
|
+
fields = Transmission::Arguments::SessionStats.new(options[:fields])
|
24
|
+
arguments = {fields: fields.to_arguments}
|
25
|
+
@connector.post method: 'session-stats', arguments: arguments
|
26
|
+
end
|
27
|
+
|
28
|
+
def close_session
|
29
|
+
@connector.post method: 'session-close'
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_port
|
33
|
+
@connector.post method: 'port-test'
|
34
|
+
end
|
35
|
+
|
36
|
+
def blocklist
|
37
|
+
@connector.post method: 'blocklist-update'
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_torrent(ids = nil, options = {})
|
41
|
+
fields = Transmission::Arguments::TorrentGet.new(options[:fields])
|
42
|
+
arguments = {fields: fields.to_arguments}
|
43
|
+
arguments[:ids] = ids if ids.is_a?(Array)
|
44
|
+
@connector.post method: 'torrent-get', arguments: arguments
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_torrent
|
48
|
+
@connector.post method: 'torrent-set'
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_torrent(arguments = {})
|
52
|
+
@connector.post method: 'torrent-add', arguments: arguments
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_torrent(ids, delete_local_data = false)
|
56
|
+
@connector.post method: 'torrent-remove', arguments: {ids: ids, 'delete-local-data' => delete_local_data}
|
57
|
+
end
|
58
|
+
|
59
|
+
def free_space
|
60
|
+
@connector.post method: 'free-space'
|
61
|
+
end
|
62
|
+
|
63
|
+
def start_torrent
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_torrent_now
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def stop_torrent
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def verify_torrent
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
def re_announce
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_torrent_location
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def rename_torrent_path
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
def move_up_torrent
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def move_down_torrent
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def move_top_torrent
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def move_bottom_torrent
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Transmission
|
5
|
+
class RPC
|
6
|
+
class Connector
|
7
|
+
class AuthError < StandardError; end
|
8
|
+
class ConnectionError < StandardError; end
|
9
|
+
class InvalidSessionError < StandardError; end
|
10
|
+
|
11
|
+
attr_accessor :host, :port, :ssl, :credentials, :path, :session_id, :response
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@host = options[:host] || 'localhost'
|
15
|
+
@port = options[:port] || 9091
|
16
|
+
@ssl = !!options[:ssl]
|
17
|
+
@credentials = options[:credentials] || nil
|
18
|
+
@path = options[:path] || '/transmission/rpc'
|
19
|
+
@session_id = options[:session_id] || ''
|
20
|
+
end
|
21
|
+
|
22
|
+
def post(params = {})
|
23
|
+
response = connection.post do |req|
|
24
|
+
req.url @path
|
25
|
+
req.headers['X-Transmission-Session-Id'] = @session_id
|
26
|
+
req.headers['Content-Type'] = 'application/json'
|
27
|
+
req.body = JSON.generate(params)
|
28
|
+
end
|
29
|
+
handle_response response
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def json_body(response)
|
35
|
+
JSON.parse response.body
|
36
|
+
rescue
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_response(response)
|
41
|
+
@response = response
|
42
|
+
if response.status == 409
|
43
|
+
@session_id = response.headers['x-transmission-session-id']
|
44
|
+
raise InvalidSessionError
|
45
|
+
end
|
46
|
+
body = json_body response
|
47
|
+
raise AuthError if response.status == 401
|
48
|
+
raise ConnectionError, body['result'] unless response.status == 200 && body['result'] == 'success'
|
49
|
+
body['arguments']
|
50
|
+
end
|
51
|
+
|
52
|
+
def connection
|
53
|
+
@connection ||= begin
|
54
|
+
connection = Faraday.new(:url => "#{scheme}://#{@host}:#{@port}", :ssl => {:verify => false}) do |faraday|
|
55
|
+
faraday.request :url_encoded
|
56
|
+
faraday.response :logger
|
57
|
+
faraday.adapter Faraday.default_adapter
|
58
|
+
end
|
59
|
+
connection.basic_auth(@credentials[:username], @credentials[:password]) if @credentials
|
60
|
+
connection
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def scheme
|
65
|
+
@ssl ? 'https' : 'http'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Stubs
|
2
|
+
|
3
|
+
def stub_rpc_request(options = {})
|
4
|
+
host = options[:host] || 'localhost'
|
5
|
+
port = options[:port] || 9091
|
6
|
+
path = options[:path] || '/transmission/rpc'
|
7
|
+
scheme = !!options[:ssl] ? 'https' : 'http'
|
8
|
+
stub_request(:post, "#{scheme}://#{host}:#{port}#{path}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def stub_get_torrent(body, torrents)
|
12
|
+
stub_rpc_request
|
13
|
+
.with(body: torrent_get_body(body))
|
14
|
+
.to_return(successful_response({arguments: {torrents: torrents}}))
|
15
|
+
end
|
16
|
+
|
17
|
+
def torrent_get_body(arguments = {})
|
18
|
+
args = {fields: Transmission::Arguments::TorrentGet.new.to_arguments}.merge(arguments)
|
19
|
+
{method: 'torrent-get', arguments: args}.to_json
|
20
|
+
end
|
21
|
+
|
22
|
+
def torrent_add_body(arguments = {})
|
23
|
+
{method: 'torrent-add', arguments: arguments}.to_json
|
24
|
+
end
|
25
|
+
|
26
|
+
def torrent_remove_body(arguments = {})
|
27
|
+
{method: 'torrent-remove', arguments: arguments}.to_json
|
28
|
+
end
|
29
|
+
|
30
|
+
def session_get_body(arguments = {})
|
31
|
+
{method: 'session-get', arguments: arguments}.to_json
|
32
|
+
end
|
33
|
+
|
34
|
+
def session_set_body(arguments = {})
|
35
|
+
{method: 'session-set', arguments: arguments}.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
def session_stats_body(arguments = {})
|
39
|
+
{method: 'session-stats', arguments: arguments}.to_json
|
40
|
+
end
|
41
|
+
|
42
|
+
def successful_response(options = {})
|
43
|
+
body = {result: 'success', arguments: (options[:arguments] || {})}
|
44
|
+
{status: 200, body: body.to_json, headers: options[:headers] || {}}
|
45
|
+
end
|
46
|
+
|
47
|
+
def unsuccessful_response(options = {})
|
48
|
+
body = {result: (options[:result] || ''), arguments: (options[:arguments] || {})}
|
49
|
+
{status: 200, body: body.to_json, headers: options[:headers] || {}}
|
50
|
+
end
|
51
|
+
|
52
|
+
def unauthorized_response(options = {})
|
53
|
+
{status: 401, body: (options[:body] || {}).to_json, headers: options[:headers] || {}}
|
54
|
+
end
|
55
|
+
|
56
|
+
def conflict_response(options = {})
|
57
|
+
{status: 409, body: (options[:body] || {}).to_json, headers: options[:headers] || {}}
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'transmission')
|
5
|
+
require File.join(File.dirname(__FILE__), 'helpers', 'stubs')
|
6
|
+
|
7
|
+
ENV['TESTING'] = 'true'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
|
11
|
+
config.include Stubs
|
12
|
+
|
13
|
+
end
|