transmission-rpc-ruby-ext 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CHANGELOG.md +3 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +62 -0
  8. data/LICENSE +19 -0
  9. data/README.md +173 -0
  10. data/Rakefile +5 -0
  11. data/lib/transmission.rb +11 -0
  12. data/lib/transmission/arguments.rb +50 -0
  13. data/lib/transmission/arguments/location_set.rb +11 -0
  14. data/lib/transmission/arguments/session_set.rb +56 -0
  15. data/lib/transmission/arguments/torrent_add.rb +24 -0
  16. data/lib/transmission/arguments/torrent_set.rb +32 -0
  17. data/lib/transmission/config.rb +18 -0
  18. data/lib/transmission/extensions.rb +4 -0
  19. data/lib/transmission/extensions/torrent_actions.rb +15 -0
  20. data/lib/transmission/extensions/torrent_attributes.rb +103 -0
  21. data/lib/transmission/extensions/torrent_status.rb +42 -0
  22. data/lib/transmission/extensions/transmission.rb +82 -0
  23. data/lib/transmission/fields.rb +41 -0
  24. data/lib/transmission/fields/session_get.rb +61 -0
  25. data/lib/transmission/fields/session_stats.rb +15 -0
  26. data/lib/transmission/fields/torrent_get.rb +76 -0
  27. data/lib/transmission/model.rb +8 -0
  28. data/lib/transmission/model/session.rb +48 -0
  29. data/lib/transmission/model/session_stats.rb +28 -0
  30. data/lib/transmission/model/torrent.rb +152 -0
  31. data/lib/transmission/rpc.rb +116 -0
  32. data/lib/transmission/rpc/connector.rb +67 -0
  33. data/lib/transmission/utils.rb +24 -0
  34. data/spec/helpers/stubs.rb +82 -0
  35. data/spec/spec_helper.rb +13 -0
  36. data/spec/transmission/arguments_spec.rb +78 -0
  37. data/spec/transmission/fields_spec.rb +60 -0
  38. data/spec/transmission/model/session_spec.rb +85 -0
  39. data/spec/transmission/model/torrent_spec.rb +345 -0
  40. data/spec/transmission/rpc/connector_spec.rb +82 -0
  41. data/spec/transmission/rpc_spec.rb +33 -0
  42. data/spec/transmission/utils_spec.rb +69 -0
  43. data/transmission-rpc-ruby-ext.gemspec +17 -0
  44. metadata +141 -0
@@ -0,0 +1,76 @@
1
+ module Transmission
2
+ class Fields
3
+ class TorrentGet < Transmission::Fields
4
+ ATTRIBUTES = [
5
+ { field: 'id' },
6
+ { field: 'activityDate' },
7
+ { field: 'addedDate' },
8
+ { field: 'bandwidthPriority' },
9
+ { field: 'comment' },
10
+ { field: 'corruptEver' },
11
+ { field: 'creator' },
12
+ { field: 'dateCreated' },
13
+ { field: 'desiredAvailable' },
14
+ { field: 'doneDate' },
15
+ { field: 'downloadDir' },
16
+ { field: 'downloadedEver' },
17
+ { field: 'downloadLimit' },
18
+ { field: 'downloadLimited' },
19
+ { field: 'error' },
20
+ { field: 'errorString' },
21
+ { field: 'eta' },
22
+ { field: 'etaIdle' },
23
+ { field: 'files' },
24
+ { field: 'fileStats' },
25
+ { field: 'hashString' },
26
+ { field: 'haveUnchecked' },
27
+ { field: 'haveValid' },
28
+ { field: 'honorsSessionLimits' },
29
+ { field: 'isFinished' },
30
+ { field: 'isPrivate' },
31
+ { field: 'isStalled' },
32
+ { field: 'leftUntilDone' },
33
+ { field: 'magnetLink' },
34
+ { field: 'manualAnnounceTime' },
35
+ { field: 'maxConnectedPeers' },
36
+ { field: 'metadataPercentComplete' },
37
+ { field: 'name' },
38
+ { field: 'peer-limit' },
39
+ { field: 'peers' },
40
+ { field: 'peersConnected' },
41
+ { field: 'peersFrom' },
42
+ { field: 'peersGettingFromUs' },
43
+ { field: 'peersSendingToUs' },
44
+ { field: 'percentDone' },
45
+ { field: 'pieces' },
46
+ { field: 'pieceCount' },
47
+ { field: 'pieceSize' },
48
+ { field: 'priorities' },
49
+ { field: 'queuePosition' },
50
+ { field: 'rateDownload' },
51
+ { field: 'rateUpload' },
52
+ { field: 'recheckProgress' },
53
+ { field: 'secondsDownloading' },
54
+ { field: 'secondsSeeding' },
55
+ { field: 'seedIdleLimit' },
56
+ { field: 'seedIdleMode' },
57
+ { field: 'seedRatioLimit' },
58
+ { field: 'seedRatioMode' },
59
+ { field: 'sizeWhenDone' },
60
+ { field: 'startDate' },
61
+ { field: 'status' },
62
+ { field: 'trackers' },
63
+ { field: 'trackerStats' },
64
+ { field: 'totalSize' },
65
+ { field: 'torrentFile' },
66
+ { field: 'uploadedEver' },
67
+ { field: 'uploadLimit' },
68
+ { field: 'uploadLimited' },
69
+ { field: 'uploadRatio' },
70
+ { field: 'wanted' },
71
+ { field: 'webseeds' },
72
+ { field: 'webseedsSendingToUs' }
73
+ ].freeze
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'model/torrent'
2
+ require_relative 'model/session'
3
+ require_relative 'model/session_stats'
4
+
5
+ module Transmission
6
+ module Model
7
+ end
8
+ end
@@ -0,0 +1,48 @@
1
+ module Transmission
2
+ module Model
3
+ class Session
4
+ class SessionError < StandardError; end
5
+
6
+ attr_accessor :attributes, :connector
7
+
8
+ def initialize(session_object, connector)
9
+ @attributes = session_object
10
+ @connector = connector
11
+ end
12
+
13
+ def save!
14
+ filtered = Transmission::Arguments::SessionSet.filter @attributes
15
+ connector.set_session filtered
16
+ end
17
+
18
+ def to_json
19
+ @attributes
20
+ end
21
+
22
+ def method_missing(symbol, *args)
23
+ string = symbol.to_s
24
+ if string[-1] == '='
25
+ string = string[0..-2]
26
+ key = Transmission::Arguments::SessionSet.real_key string
27
+ return @attributes[key] = args.first unless key.nil?
28
+ else
29
+ key = Transmission::Fields::SessionGet.real_key string
30
+ return @attributes[key] unless key.nil?
31
+ end
32
+ super
33
+ end
34
+
35
+ class << self
36
+ def get(options = {})
37
+ rpc = options[:connector] || connector
38
+ body = rpc.get_session options[:fields]
39
+ Session.new body, rpc
40
+ end
41
+
42
+ def connector
43
+ Transmission::Config.get_connector
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ module Transmission
2
+ module Model
3
+ class SessionStats
4
+ attr_accessor :attributes, :connector
5
+
6
+ def initialize(session_object, connector)
7
+ @attributes = session_object
8
+ @connector = connector
9
+ end
10
+
11
+ def to_json
12
+ @attributes
13
+ end
14
+
15
+ class << self
16
+ def get(options = {})
17
+ rpc = options[:connector] || connector
18
+ body = rpc.get_session_stats options[:fields]
19
+ SessionStats.new body, rpc
20
+ end
21
+
22
+ def connector
23
+ Transmission::Config.get_connector
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,152 @@
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, :connector, :torrents, :ids
10
+
11
+ def initialize(torrent_object, connector)
12
+ if torrent_object.is_a? Array
13
+ is_single = torrent_object.size == 1
14
+ @attributes = is_single ? torrent_object.first : {}
15
+ @ids = is_single ? [@attributes['id'].to_i] : []
16
+ @torrents = torrent_object.inject([]) do |torrents, torrent|
17
+ @ids << torrent['id'].to_i
18
+ torrents << Torrent.new([torrent], connector)
19
+ end unless is_single
20
+ end
21
+ @connector = connector
22
+ end
23
+
24
+ def delete!(remove_local_data = false)
25
+ connector.remove_torrent @ids, remove_local_data
26
+ @deleted = true
27
+ end
28
+
29
+ def save!
30
+ filtered = Transmission::Arguments::TorrentSet.filter @attributes
31
+ connector.set_torrent @ids, filtered
32
+ end
33
+
34
+ def move_up!
35
+ connector.move_up_torrent @ids
36
+ end
37
+
38
+ def move_down!
39
+ connector.move_down_torrent @ids
40
+ end
41
+
42
+ def move_top!
43
+ connector.move_top_torrent @ids
44
+ end
45
+
46
+ def move_bottom!
47
+ connector.move_bottom_torrent @ids
48
+ end
49
+
50
+ def start!
51
+ connector.start_torrent @ids
52
+ end
53
+
54
+ def start_now!
55
+ connector.start_torrent_now @ids
56
+ end
57
+
58
+ def stop!
59
+ connector.stop_torrent @ids
60
+ end
61
+
62
+ def verify!
63
+ connector.verify_torrent @ids
64
+ end
65
+
66
+ def re_announce!
67
+ connector.re_announce_torrent @ids
68
+ end
69
+
70
+ def set_location(new_location, move = false)
71
+ connector.torrent_set_location @ids, location: new_location, move: move
72
+ end
73
+
74
+ def is_multi?
75
+ @ids.size > 1
76
+ end
77
+
78
+ # Note: this is overidden by extensions.
79
+ # def finished?
80
+ # percent_done == 1
81
+ # end
82
+
83
+ def reload!
84
+ torrents = Torrent.find @ids, connector: @connector
85
+ @ids = torrents.ids
86
+ @attributes = torrents.attributes
87
+ @torrents = torrents.torrents
88
+ self
89
+ end
90
+
91
+ def to_json
92
+ if is_multi?
93
+ @torrents.inject([]) do |torrents, torrent|
94
+ torrents << torrent.to_json
95
+ end
96
+ else
97
+ @attributes
98
+ end
99
+ end
100
+
101
+ def method_missing(symbol, *args)
102
+ string = symbol.to_s
103
+ if string[-1] == '='
104
+ string = string[0..-2]
105
+ key = Transmission::Arguments::TorrentSet.real_key string
106
+ return @attributes[key] = args.first unless key.nil?
107
+ else
108
+ key = Transmission::Fields::TorrentGet.real_key string
109
+ return @attributes[key] unless key.nil?
110
+ end
111
+ super
112
+ end
113
+
114
+ class << self
115
+ def all(options = {})
116
+ rpc = options[:connector] || connector
117
+ body = rpc.get_torrent nil, options[:fields]
118
+ Torrent.new body['torrents'], rpc
119
+ end
120
+
121
+ def find(id, options = {})
122
+ rpc = options[:connector] || connector
123
+ ids = id.is_a?(Array) ? id : [id]
124
+ body = rpc.get_torrent ids, options[:fields]
125
+ raise TorrentNotFoundError if body['torrents'].size.zero?
126
+ Torrent.new body['torrents'], rpc
127
+ end
128
+
129
+ def add(options = {})
130
+ rpc = options[:connector] || connector
131
+ body = rpc.add_torrent options[:arguments]
132
+ raise DuplicateTorrentError if body['torrent-duplicate']
133
+ find body['torrent-added']['id'], options
134
+ end
135
+
136
+ def start_all!(options = {})
137
+ rpc = options[:connector] || connector
138
+ rpc.start_torrent nil
139
+ end
140
+
141
+ def stop_all!(options = {})
142
+ rpc = options[:connector] || connector
143
+ rpc.stop_torrent nil
144
+ end
145
+
146
+ def connector
147
+ Transmission::Config.get_connector
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,116 @@
1
+ require_relative 'rpc/connector'
2
+
3
+ module Transmission
4
+ class RPC
5
+ attr_accessor :session, :connector
6
+
7
+ def initialize(options = {})
8
+ @connector = Connector.new options
9
+ end
10
+
11
+ def get_session(fields = nil)
12
+ fields = Transmission::Fields::SessionGet.new(fields)
13
+ arguments = { fields: fields.to_fields }
14
+ @connector.post method: 'session-get', arguments: arguments
15
+ end
16
+
17
+ def get_session_stats(fields = nil)
18
+ fields = Transmission::Fields::SessionStats.new(fields)
19
+ arguments = { fields: fields.to_fields }
20
+ @connector.post method: 'session-stats', arguments: arguments
21
+ end
22
+
23
+ def close_session
24
+ @connector.post method: 'session-close'
25
+ end
26
+
27
+ def test_port
28
+ @connector.post method: 'port-test'
29
+ end
30
+
31
+ def blocklist
32
+ @connector.post method: 'blocklist-update'
33
+ end
34
+
35
+ def free_space
36
+ @connector.post method: 'free-space'
37
+ end
38
+
39
+ def get_torrent(ids, fields = nil)
40
+ fields = Transmission::Fields::TorrentGet.new(fields)
41
+ arguments = { fields: fields.to_fields }
42
+ arguments[:ids] = ids if ids.is_a? Array
43
+ @connector.post method: 'torrent-get', arguments: arguments
44
+ end
45
+
46
+ def set_torrent(ids, arguments)
47
+ arguments[:ids] = ids
48
+ arguments = Transmission::Arguments::TorrentSet.new(arguments)
49
+ @connector.post method: 'torrent-set', arguments: arguments.to_arguments
50
+ end
51
+
52
+ def set_session(arguments)
53
+ arguments = Transmission::Arguments::SessionSet.new(arguments)
54
+ @connector.post method: 'session-set', arguments: arguments.to_arguments
55
+ end
56
+
57
+ def add_torrent(arguments)
58
+ arguments = Transmission::Arguments::TorrentAdd.new(arguments)
59
+ @connector.post method: 'torrent-add', arguments: arguments.to_arguments
60
+ end
61
+
62
+ def torrent_set_location(ids, arguments)
63
+ arguments[:ids] = ids
64
+ arguments = Transmission::Arguments::LocationSet.new(arguments)
65
+ @connector.post method: 'torrent-set-location', arguments: arguments.to_arguments
66
+ end
67
+
68
+ def remove_torrent(ids, delete_local_data = false)
69
+ @connector.post method: 'torrent-remove', arguments: { ids: ids, 'delete-local-data' => delete_local_data }
70
+ end
71
+
72
+ def start_torrent(ids)
73
+ @connector.post method: 'torrent-start', arguments: id_arguments(ids)
74
+ end
75
+
76
+ def start_torrent_now(ids)
77
+ @connector.post method: 'torrent-start-now', arguments: id_arguments(ids)
78
+ end
79
+
80
+ def stop_torrent(ids)
81
+ @connector.post method: 'torrent-stop', arguments: id_arguments(ids)
82
+ end
83
+
84
+ def verify_torrent(ids)
85
+ @connector.post method: 'torrent-verify', arguments: id_arguments(ids)
86
+ end
87
+
88
+ def re_announce_torrent(ids)
89
+ @connector.post method: 'torrent-reannounce', arguments: id_arguments(ids)
90
+ end
91
+
92
+ def move_up_torrent(ids)
93
+ @connector.post method: 'queue-move-up', arguments: id_arguments(ids)
94
+ end
95
+
96
+ def move_down_torrent(ids)
97
+ @connector.post method: 'queue-move-down', arguments: id_arguments(ids)
98
+ end
99
+
100
+ def move_top_torrent(ids)
101
+ @connector.post method: 'queue-move-top', arguments: id_arguments(ids)
102
+ end
103
+
104
+ def move_bottom_torrent(ids)
105
+ @connector.post method: 'queue-move-bottom', arguments: id_arguments(ids)
106
+ end
107
+
108
+ private
109
+
110
+ def id_arguments(ids)
111
+ arguments = {}
112
+ arguments[:ids] = ids if ids.is_a? Array
113
+ arguments
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,67 @@
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
+
10
+ attr_accessor :host, :port, :ssl, :credentials, :path, :session_id, :response
11
+
12
+ def initialize(options = {})
13
+ @host = options[:host] || 'localhost'
14
+ @port = options[:port] || 9091
15
+ @ssl = options[:ssl] || false
16
+ @credentials = options[:credentials] || nil
17
+ @path = options[:path] || '/transmission/rpc'
18
+ @session_id = options[:session_id] || ''
19
+ end
20
+
21
+ def post(params = {})
22
+ response = connection.post do |req|
23
+ req.url @path
24
+ req.headers['X-Transmission-Session-Id'] = @session_id
25
+ req.headers['Content-Type'] = 'application/json'
26
+ req.body = JSON.generate(params)
27
+ end
28
+ handle_response response, params
29
+ end
30
+
31
+ private
32
+
33
+ def json_body(response)
34
+ JSON.parse response.body
35
+ rescue
36
+ {}
37
+ end
38
+
39
+ def handle_response(response, params)
40
+ @response = response
41
+ if response.status == 409
42
+ @session_id = response.headers['x-transmission-session-id']
43
+ return post(params)
44
+ end
45
+ body = json_body response
46
+ raise AuthError if response.status == 401
47
+ raise ConnectionError, body['result'] unless response.status == 200 && body['result'] == 'success'
48
+ body['arguments']
49
+ end
50
+
51
+ def connection
52
+ @connection ||= begin
53
+ connection = Faraday.new(url: "#{scheme}://#{@host}:#{@port}", ssl: { verify: false }) do |faraday|
54
+ faraday.request :url_encoded
55
+ faraday.adapter Faraday.default_adapter
56
+ end
57
+ connection.basic_auth(@credentials[:username], @credentials[:password]) if @credentials
58
+ connection
59
+ end
60
+ end
61
+
62
+ def scheme
63
+ @ssl ? 'https' : 'http'
64
+ end
65
+ end
66
+ end
67
+ end