Class: TorrentManager

Inherits:
Object
  • Object
show all
Defined in:
lib/quartz_flow/torrent_manager.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(peerClient, torrentFileDir, monthlyResetDay) ⇒ TorrentManager

Returns a new instance of TorrentManager.



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/quartz_flow/torrent_manager.rb', line 8

def initialize(peerClient, torrentFileDir, monthlyResetDay)
  @peerClient = peerClient
  @cachedTorrentData = nil
  @cachedTorrentDataMutex = Mutex.new
  @cachedAt = nil
  @cacheLifetime = 2
  @torrentFileDir = torrentFileDir
  @peerClientStopped = false
  @usageTracker = UsageTracker.new(monthlyResetDay)
  # Start a thread to keep track of usage.
  startUsageTrackerThread
  startTorrentDataThread
end

Instance Attribute Details

#peerClientObject (readonly)

Returns the value of attribute peerClient.



22
23
24
# File 'lib/quartz_flow/torrent_manager.rb', line 22

def peerClient
  @peerClient
end

Instance Method Details

#applyTorrentSettings(infoHash) ⇒ Object

Update the torrent settings (upload rate limit, etc) from database values



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/quartz_flow/torrent_manager.rb', line 246

def applyTorrentSettings(infoHash)
  asciiInfoHash = QuartzTorrent::bytesToHex(infoHash)
  helper = SettingsHelper.new

  # Set limits based on per-torrent settings if they exist, otherwise to default global limits if they exist.
  uploadRateLimit = to_i(helper.get(:uploadRateLimit, :unfiltered, asciiInfoHash))
  uploadRateLimit = to_i(helper.get(:defaultUploadRateLimit, :unfiltered)) if ! uploadRateLimit

  downloadRateLimit = to_i(helper.get(:downloadRateLimit, :unfiltered, asciiInfoHash))
  downloadRateLimit = to_i(helper.get(:defaultDownloadRateLimit, :unfiltered)) if ! downloadRateLimit

  ratio = helper.get(:ratio, :filter, asciiInfoHash)
  ratio = helper.get(:defaultRatio, :filter) if ! ratio

  uploadDuration = helper.get(:uploadDuration, :unfiltered, asciiInfoHash)
  uploadDuration = helper.get(:defaultUploadDuration, :unfiltered) if ! uploadDuration
  uploadDuration = uploadDuration.to_i if uploadDuration

  paused = helper.get(:paused, :filter, asciiInfoHash)

  bytesDownloaded = helper.get(:bytesDownloaded, :filter, asciiInfoHash)
  bytesUploaded = helper.get(:bytesUploaded, :filter, asciiInfoHash)

  @peerClient.setUploadRateLimit infoHash, uploadRateLimit
  @peerClient.setDownloadRateLimit infoHash, downloadRateLimit
  @peerClient.setUploadRatio infoHash, ratio
  @peerClient.setUploadDuration infoHash, uploadDuration
  @peerClient.setPaused infoHash, paused
  @peerClient.adjustBytesDownloaded infoHash, bytesDownloaded if bytesDownloaded
  @peerClient.adjustBytesUploaded infoHash, bytesUploaded if bytesUploaded
end

#currentPeriodUsage(periodType) ⇒ Object

Get the usage for the current period of the specified type. periodType should be one of :daily or :monthly.



280
281
282
# File 'lib/quartz_flow/torrent_manager.rb', line 280

def currentPeriodUsage(periodType)
  @usageTracker.currentUsage(periodType).value
end

#downloadTorrentFile(url) ⇒ Object

Download a .torrent file from a specified URL. Return the path to the downloaded .torrent file.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/quartz_flow/torrent_manager.rb', line 149

def downloadTorrentFile(url)
  # open-uri doesn't handle [ and ] properly
  encodedSourcePath = URI.escape(url, /[\[\]]/)

  path = nil
  open(encodedSourcePath) do |f|
    uriPath = f.base_uri.path
    raise "The file '#{uriPath}' doesn't have the .torrent extension" if uriPath !~ /.torrent$/
    path = @torrentFileDir + File::SEPARATOR + File.basename(uriPath)
    File.open(path, "w"){ |outfile| outfile.write(f.read) }
  end
  path
end

#loadMagnet(path) ⇒ Object

Load a magnet link in a file



171
172
173
174
175
# File 'lib/quartz_flow/torrent_manager.rb', line 171

def loadMagnet(path)
  raw = nil
  File.open(path, "r"){ |infile| raw = infile.read }
  QuartzTorrent::MagnetURI.new(raw)
end

#removeTorrent(infoHash, deleteFiles) ⇒ Object

Remove the specified torrent. Pass the infoHash as an ascii string, not binary.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/quartz_flow/torrent_manager.rb', line 211

def removeTorrent(infoHash, deleteFiles)
  infoHashBytes = QuartzTorrent::hexToBytes(infoHash)
  @peerClient.removeTorrent infoHashBytes, deleteFiles

  # Remove torrent from torrent dir
  Dir.new(@torrentFileDir).each do |e|
    if e =~ /\.torrent$/
      path = @torrentFileDir + File::SEPARATOR + e
      metainfo = QuartzTorrent::Metainfo.createFromFile(path)
      if metainfo.infoHash == infoHashBytes
        FileUtils.rm path
        break
      end
    end
  end

  # Remove torrent settings
  helper = SettingsHelper.new
  helper.deleteForOwner infoHash
 
  # Remove magnet file if it exists
  magnetFile = @torrentFileDir + File::SEPARATOR + infoHash + ".magnet"
  FileUtils.rm magnetFile if File.exists?(magnetFile)
end

#setTorrentPaused(infoHash, val) ⇒ Object

Pause or unpause the specified torrent. Store the pause state in the database.



237
238
239
240
241
242
243
# File 'lib/quartz_flow/torrent_manager.rb', line 237

def setTorrentPaused(infoHash, val)
  infoHashBytes = QuartzTorrent::hexToBytes(infoHash)
  @peerClient.setPaused infoHashBytes, val

  helper = SettingsHelper.new    
  helper.set :paused, val, infoHash
end

#simplifiedTorrentData(fields, where) ⇒ Object

Return the torrent data as a hash. If ‘fields` is non-null, then only those fields are returned. If `where` is non-null, it should be a hash of fields for which the values must match to be returned.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/quartz_flow/torrent_manager.rb', line 68

def simplifiedTorrentData(fields, where)
  result = {}

  fieldsHash = {}
  fields.each{ |e| fieldsHash[e] = true } if fields

  if where
    w = {}
    where.each do |k,v|
      w[k.to_sym] = v
    end
    where = w
  end

  torrentData.each do |k,d|
    h = d.to_h

    asciiInfoHash = QuartzTorrent::bytesToHex(h[:infoHash])
    h[:infoHash] = asciiInfoHash
    h[:downloadRate] = QuartzTorrent::Formatter.formatSpeed(h[:downloadRate])
    h[:uploadRate] = QuartzTorrent::Formatter.formatSpeed(h[:uploadRate])
    h[:downloadRateDataOnly] = QuartzTorrent::Formatter.formatSpeed(h[:downloadRateDataOnly])
    h[:uploadRateDataOnly] = QuartzTorrent::Formatter.formatSpeed(h[:uploadRateDataOnly])
    h[:dataLength] = QuartzTorrent::Formatter.formatSize(h[:dataLength])
    h[:completedBytes] = QuartzTorrent::Formatter.formatSize(h[:completedBytes])
    # Sort peers
    h[:peers].sort! do |a,b|
      c = (b[:uploadRate].to_i <=> a[:uploadRate].to_i)
      c = (b[:downloadRate].to_i <=> a[:downloadRate].to_i) if c == 0
      c
    end
    # Format peer rates
    h[:peers].each do |p| 
      p[:uploadRate] = QuartzTorrent::Formatter.formatSpeed(p[:uploadRate])
      p[:downloadRate] = QuartzTorrent::Formatter.formatSpeed(p[:downloadRate])
    end
    if h[:info] 
      h[:info][:files].each do |file|
        file[:length] = QuartzTorrent::Formatter.formatSize(file[:length])
      end
    end
    h[:uploadRateLimit] = QuartzTorrent::Formatter.formatSpeed(h[:uploadRateLimit])
    h[:downloadRateLimit] = QuartzTorrent::Formatter.formatSize(h[:downloadRateLimit])
    h[:bytesUploaded] = QuartzTorrent::Formatter.formatSize(h[:bytesUploaded])
    h[:bytesDownloaded] = QuartzTorrent::Formatter.formatSize(h[:bytesDownloaded])
    h[:uploadDuration] = QuartzTorrent::Formatter.formatTime(h[:uploadDuration]) if h[:uploadDuration]

    h[:completePieces] = d.completePieceBitfield ? d.completePieceBitfield.countSet : 0
    h[:totalPieces] = d.completePieceBitfield ? d.completePieceBitfield.length : 0

    # Only send 8 alarms
    h[:alarms] = h[:alarms].first(8)

    if where
      matches = true
      where.each do |k,v|
        if h[k] != v
          matches = false
          break
        end
      end
      next if ! matches
    end

    if fields
      newHash = {}
      h.each do |k,v|
        if fieldsHash.has_key?(k)
          newHash[k] = v
        end
      end
      h = newHash
    end

    result[asciiInfoHash] = h
  end
  result
end

#startExistingTorrentsObject

Start torrents that already exist in the torrent file directory



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/quartz_flow/torrent_manager.rb', line 44

def startExistingTorrents
  Dir.new(@torrentFileDir).each do |e|
    path = @torrentFileDir + File::SEPARATOR + e
    if e =~ /\.torrent$/
      puts "Starting .torrent '#{path}'"
      begin
        startTorrentFile(path)
      rescue
        puts "  Starting .torrent '#{path}' failed: #{$!}"
      end
    elsif e =~ /\.magnet$/   
      magnet = loadMagnet(path)
      puts "Starting magnet '#{magnet.raw}'"
      begin
        startMagnet magnet
      rescue
        puts "  Starting magnet '#{magnet.raw}' failed: #{$!}"
      end
    end
  end
end

#startMagnet(magnet) ⇒ Object

Start running the magnet



204
205
206
207
208
# File 'lib/quartz_flow/torrent_manager.rb', line 204

def startMagnet(magnet)
  startTorrent do
    @peerClient.addTorrentByMagnetURI(magnet)
  end
end

#startTorrentFile(path) ⇒ Object

Start running the torrent specified by the .torrent file given in path.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/quartz_flow/torrent_manager.rb', line 187

def startTorrentFile(path)
  startTorrent do
    begin
      metainfo = QuartzTorrent::Metainfo.createFromFile(path)
      @peerClient.addTorrentByMetainfo(metainfo)
    rescue BEncode::DecodeError
      # Delete the file
      begin
        FileUtils.rm path
      rescue
      end
      raise $!
    end
  end
end

#stopPeerClientObject



36
37
38
39
40
41
# File 'lib/quartz_flow/torrent_manager.rb', line 36

def stopPeerClient
  @peerClient.stop
  @peerClientStopped = true
  stopTorrentDataThread
  stopUsageTrackerThread
end

#storeMagnet(magnet) ⇒ Object

Store a magnet link in a file in the torrent file directory.



164
165
166
167
168
# File 'lib/quartz_flow/torrent_manager.rb', line 164

def storeMagnet(magnet)
  asciiInfoHash = QuartzTorrent::bytesToHex(magnet.btInfoHash)
  path = @torrentFileDir + File::SEPARATOR + asciiInfoHash + ".magnet"
  File.open(path, "w"){ |outfile| outfile.write(magnet.raw) }
end

#storeUploadedTorrentFile(path, name) ⇒ Object

Store an uploaded .torrent file in the torrent directory. Return the path to the final .torrent file.



179
180
181
182
183
184
# File 'lib/quartz_flow/torrent_manager.rb', line 179

def storeUploadedTorrentFile(path, name)
  name += ".torrent" if name !~ /\.torrent$/
  dpath = @torrentFileDir + File::SEPARATOR + name
  FileUtils.mv path, dpath
  dpath
end

#torrentData(infoHash = nil) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/quartz_flow/torrent_manager.rb', line 24

def torrentData(infoHash = nil)
  result = nil

  # The first time, we may need to wait for the thread to load the data.
  sleep(0.25) while ! @cachedTorrentData

  @cachedTorrentDataMutex.synchronize do
    result = @cachedTorrentData 
  end
  result
end