trans-api 0.0.1

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.
@@ -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
+