trans-api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,292 @@
1
+ module Trans
2
+ module Api
3
+
4
+ #
5
+ # Torrent class
6
+ #
7
+
8
+ class Torrent
9
+
10
+ # torrent status value
11
+ STATUS = [ :stopped, :checkQueue, :checkFiles, :downloadQueue, :downloading, :seedQueue, :seeding, :isolated ]
12
+
13
+ # torrent get fields
14
+ ACCESSOR_FIELDS = [
15
+ :activityDate, :addedDate, :bandwidthPriority, :comment, :corruptEver, :creator, :dateCreated, :desiredAvailable,
16
+ :doneDate, :downloadDir, :downloadedEver, :downloadLimit, :downloadLimited, :error, :errorString, :eta, :files,
17
+ :fileStats, :hashString, :haveUnchecked, :haveValid, :honorsSessionLimits, :id, :isFinished, :isPrivate, :isStalled,
18
+ :leftUntilDone, :magnetLink, :manualAnnounceTime, :maxConnectedPeers, :metadataPercentComplete, :name, :peer_limit,
19
+ :peers, :peersConnected, :peersFrom, :peersGettingFromUs, :peersSendingToUs, :percentDone, :pieces, :pieceCount,
20
+ :pieceSize, :priorities, :queuePosition, :rateDownload, :rateUpload, :recheckProgress, :secondsDownloading,
21
+ :secondsSeeding, :seedIdleLimit, :seedIdleMode, :seedRatioLimit, :seedRatioMode, :sizeWhenDone, :startDate, :status,
22
+ :trackers, :trackerStats, :totalSize, :torrentFile, :uploadedEver, :uploadLimit, :uploadLimited, :uploadRatio,
23
+ :wanted, :webseeds, :webseedsSendingToUs ]
24
+
25
+ # torrent set fields
26
+ MUTATOR_FIELDS = [ :bandwidthPriority, :downloadLimit, :downloadLimited, :files_wanted, :files_unwanted, :honorsSessionLimits,
27
+ :ids, :location, :peer_limit, :priority_high, :priority_low, :priority_normal, :queuePosition, :seedIdleLimit,
28
+ :seedIdleMode, :seedRatioLimit, :seedRatioMode, :trackerAdd, :trackerRemove, :trackerReplace, :uploadLimit, :uploadLimited ]
29
+
30
+ # torrent add fields
31
+ ADD = [ :cookies, :download_dir, :filename, :metainfo, :paused, :peer_limit, :bandwidthPriority, :files_wanted,
32
+ :files_unwanted, :priority_high, :priority_low, :priority_normal ]
33
+
34
+ DELETE = [ :ids, :delete_local_data ]
35
+
36
+ LOCATION = [ :ids, :move, :location ]
37
+
38
+
39
+ @@default_fields = [ :id, :name, :status ]
40
+
41
+
42
+ # constructor
43
+ def initialize(options={})
44
+ @client = Client.new
45
+ @fields = options[:torrent] if options.include? :torrent
46
+ @old_fields = @fields.clone
47
+ @last_error = {error: "", message: ""}
48
+ self.attach_methods!
49
+ end
50
+
51
+ # placeholder for fields
52
+ def metaclass
53
+ class << self; self; end
54
+ end
55
+
56
+ def status_name
57
+ STATUS[self.status]
58
+ end
59
+
60
+ # store changed field
61
+ def save!
62
+ # reject unchanged fields
63
+ changed = @fields.reject{|k,v| @old_fields[k] == v}
64
+ # reject inmutable fields
65
+ changed.reject!{|k,v| !MUTATOR_FIELDS.include?(k) }
66
+ # reject empty arrays
67
+ changed.reject!{|k,v| v.class == Array && v.empty? }
68
+ if changed.size > 0
69
+ # call api, and store changed fields
70
+ @client.connect.torrent_set changed, [self.id]
71
+ @old_fields = @fields.clone
72
+ end
73
+ end
74
+
75
+ # get registered fields
76
+ def fields
77
+ @fields
78
+ end
79
+
80
+ # files wrapped by an File object
81
+ def files_objects
82
+ ret = []
83
+ # i = -1
84
+ torrent = @client.connect.torrent_get([:files, :fileStats], [self.id]).first
85
+ @fields[:files] = torrent[:files]
86
+ @fields[:fileStatus] = torrent[:fileStats]
87
+ self.files.each_with_index{ |f,i| ret << Trans::Api::File.new( torrent: self, fields: @fields,
88
+ file: f.merge(id: i).merge(fileStat: torrent[:fileStats][i])) }
89
+ ret
90
+ end
91
+
92
+ # reload current object
93
+ def reset!
94
+ @fields = @client.connect.torrent_get( @fields.map{|k,v| k}, [self.id]).first
95
+ @old_fields = @fields.clone
96
+ @last_error = {error: "", message: ""}
97
+ end
98
+
99
+ def start!
100
+ @client.connect.torrent_start [self.id]
101
+ end
102
+
103
+ def start_now!
104
+ @client.connect.torrent_start_now [self.id]
105
+ end
106
+
107
+ def stop!
108
+ @client.connect.torrent_stop [self.id]
109
+ end
110
+
111
+ def verify!
112
+ @client.connect.torrent_verify [self.id]
113
+ end
114
+
115
+ def reannounce!
116
+ @client.connect.torrent_reannounce [self.id]
117
+ end
118
+
119
+ def set_location!(file, move = false)
120
+ @client.connect.torrent_set_location({location: file, move: move}, [self.id])
121
+ end
122
+
123
+ def delete!(options={})
124
+ options[:delete_local_data] = false unless options.include? :delete_local_data # optional
125
+ @client.connect.torrent_remove options, [self.id]
126
+ end
127
+
128
+ def queue_top!
129
+ @client.connect.queue_move_top [self.id]
130
+ end
131
+
132
+ def queue_bottom!
133
+ @client.connect.queue_move_bottom [self.id]
134
+ end
135
+
136
+ def queue_up!
137
+ @client.connect.queue_move_up [self.id]
138
+ end
139
+
140
+ def queue_down!
141
+ @client.connect.queue_move_down [self.id]
142
+ end
143
+
144
+ # wait for a specific torrent lambda after call
145
+ # Ex: lamb = instance.waitfor_after(lambda{|torrent| torrent.status_name != :stopped}, :after).start!
146
+ def waitfor(lamb, place = :after)
147
+ TorrentDummy.new lamb, self, place
148
+ end
149
+
150
+ # static methods
151
+ class << self
152
+ def all
153
+ torrents = Client.new.connect.torrent_get( @@default_fields )
154
+ torrents.map{|t| Torrent.new torrent: t}
155
+ end
156
+
157
+ def find(id)
158
+ torrents = Client.new.connect.torrent_get( @@default_fields , [id])
159
+ remap = torrents.map{|t| Torrent.new torrent: t }
160
+ return remap.first if torrents.size == 1
161
+ return nil if torrents.empty?
162
+ remap
163
+ end
164
+
165
+ def find_by_field_value(field, value)
166
+ # set a reduced search field (with some default values)
167
+ fields = [field, :id, :name, :status]
168
+ torrents = Client.new.connect.torrent_get( fields )
169
+ torrents.reject!{|t| t[field] != value}
170
+ # TODO: perhaps add original default values??
171
+ remap = torrents.map{|t| Torrent.new torrent: t }
172
+ return remap.first if torrents.size == 1
173
+ return nil if torrents.empty?
174
+ remap
175
+ end
176
+
177
+ def start_all
178
+ torrents = Client.new.connect.torrent_get [:id, :status]
179
+ remap = torrents.map{ |t| Torrent.new torrent: t }
180
+ Client.new.connect.torrent_start remap.map{|t| t.id}
181
+ end
182
+
183
+ def stop_all
184
+ torrents = Client.new.connect.torrent_get [:id, :status]
185
+ remap = torrents.map{ |t| Torrent.new torrent: t }
186
+ Client.new.connect.torrent_stop remap.map{|t| t.id}
187
+ end
188
+
189
+ def delete_all(torrents, options={})
190
+ raise "no ids assigned" if torrents.empty? || !torrents.kind_of?(Array)
191
+ raise "expected type: Torrent" if torrents.size > 0 && !torrents.first.kind_of?(Torrent)
192
+ ids = torrents.map{|torrent| torrent.id}
193
+ client = Client.new
194
+ options[:delete_local_data] = false unless options.include? :delete_local_data # optional
195
+ client.connect.torrent_remove options
196
+ end
197
+
198
+ def add_file(filename, options={})
199
+ raise "file not found: #{filename}" unless ::File.exists? filename
200
+ client = Client.new
201
+ options[:filename] = filename
202
+ torrent = client.connect.torrent_add options
203
+ torrent = client.connect.torrent_get( @@default_fields, [torrent[:id]]).first
204
+ Torrent.new torrent: torrent
205
+ end
206
+
207
+ def default_fields=(list=[])
208
+ @@default_fields << :id unless list.include? :id
209
+ @@default_fields |= list
210
+ end
211
+ end
212
+
213
+
214
+ protected
215
+
216
+ # define helper methods for setting and getting field information
217
+ def attach_methods!
218
+
219
+ # NOTE: not all accessor fields are mutators as well!!
220
+
221
+ MUTATOR_FIELDS.each do |k|
222
+ # setter
223
+ self.metaclass.send(:define_method, "#{k}=") do |value|
224
+ unless @fields.include? k
225
+ fields = @client.connect.torrent_get([k], [self.id]).first
226
+ @fields.merge! fields
227
+ @old_fields.merge! fields
228
+ end
229
+ @fields[k] = value
230
+ end
231
+ end
232
+
233
+
234
+ ACCESSOR_FIELDS.each do |k|
235
+ # getter
236
+ self.metaclass.send(:define_method, "#{k}") do
237
+ # request handler to get new field
238
+ unless @fields.include? k
239
+ fields = @client.connect.torrent_get([k], [self.id]).first
240
+ @fields.merge! fields
241
+ @old_fields.merge! fields
242
+ end
243
+ @fields[k]
244
+ end
245
+ end
246
+ end
247
+
248
+ end
249
+
250
+ protected
251
+
252
+ # Dummy class to handle chained calling
253
+ # Inspired by delayed_job (message_sending.rb)
254
+ # https://github.com/collectiveidea/delayed_job/blob/master/lib/delayed/message_sending.rb
255
+ class TorrentDummy
256
+
257
+ def initialize(task, target_object, place)
258
+ @task = task # lambda
259
+ @target_object = target_object
260
+ @place = place
261
+
262
+ self.wait if @place == :before
263
+ end
264
+
265
+ # handling Torrent method calls
266
+ def method_missing(method, *args)
267
+
268
+ unless args.empty?
269
+ @target_object.send method, args
270
+ else
271
+ @target_object.send method
272
+ end
273
+
274
+ self.wait if @place == :after
275
+
276
+ end
277
+
278
+ def wait
279
+ # keep trying task (and refreshing the torrent file)
280
+ while true do
281
+ @target_object.reset!
282
+ t = @task.call @target_object
283
+ break if t # task achieved!
284
+ end
285
+ end
286
+
287
+ end
288
+
289
+
290
+
291
+ end
292
+ end
@@ -0,0 +1,5 @@
1
+ module Trans
2
+ module Api
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/lib/trans-api.rb ADDED
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/trans-api/version")
2
+ require File.expand_path(File.dirname(__FILE__) + "/trans-api/client")
3
+ require File.expand_path(File.dirname(__FILE__) + "/trans-api/connect")
4
+ require File.expand_path(File.dirname(__FILE__) + "/trans-api/session")
5
+ require File.expand_path(File.dirname(__FILE__) + "/trans-api/torrent")
6
+ require File.expand_path(File.dirname(__FILE__) + "/trans-api/file")
7
+
8
+ module Trans
9
+ module Api
10
+ # placeholder
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+
2
+ class ActiveSupport::TestCase
3
+ setup :global_setup
4
+
5
+ def global_setup
6
+ end
7
+ end
8
+