transmission-rpc-ruby 0.1.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/.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
|