class QuartzTorrent::PeerClient

Represents a client that talks to bittorrent peers. This is the main class used to download and upload bittorrents.

Attributes

port[RW]

Set the port used by the torrent peer client. This only has an effect if start has not yet been called.

Public Class Methods

new(baseDirectory, maxIncomplete = 5, maxActive = 10) click to toggle source

Create a new PeerClient that will save and load torrent data under the specified baseDirectory.

# File lib/quartz_torrent/peerclient.rb, line 1724
def initialize(baseDirectory, maxIncomplete = 5, maxActive = 10)
  @port = 9998
  @handler = nil
  @stopped = true
  @reactor = nil
  @logger = LogManager.getLogger("peerclient")
  @worker = nil
  @handler = PeerClientHandler.new baseDirectory, maxIncomplete, maxActive
  @reactor = QuartzTorrent::Reactor.new(@handler, LogManager.getLogger("peerclient.reactor"))
  @toStart = []
end

Public Instance Methods

addTorrentByMagnetURI(magnet) click to toggle source

Add a new torrent to manage given a MagnetURI object. This is generally the method to call if you have a magnet link. Returns the infoHash of the newly added torrent.

# File lib/quartz_torrent/peerclient.rb, line 1796
def addTorrentByMagnetURI(magnet)
  raise "addTorrentByMagnetURI should be called with a MagnetURI object, not a #{magnet.class}" if ! magnet.is_a?(MagnetURI)

  trackerUrl = magnet.trackers
  raise "addTorrentByMagnetURI can't handle magnet links that don't have a tracker URL." if !trackerUrl

  addTorrentWithoutMetainfo(trackerUrl, magnet.btInfoHash, magnet)
end
addTorrentByMetainfo(metainfo) click to toggle source

Add a new torrent to manage described by a Metainfo object. This is generally the method to call if you have a .torrent file. Returns the infoHash of the newly added torrent.

# File lib/quartz_torrent/peerclient.rb, line 1779
def addTorrentByMetainfo(metainfo)
  raise "addTorrentByMetainfo should be called with a Metainfo object, not #{metainfo.class}" if ! metainfo.is_a?(Metainfo)
  trackerclient = TrackerClient.createFromMetainfo(metainfo, false)
  addTorrent(trackerclient, metainfo.infoHash, metainfo.info)
end
addTorrentWithoutMetainfo(announceUrl, infoHash, magnet = nil) click to toggle source

Add a new torrent to manage given an announceUrl and an infoHash. The announceUrl may be a list. Returns the infoHash of the newly added torrent.

# File lib/quartz_torrent/peerclient.rb, line 1787
def addTorrentWithoutMetainfo(announceUrl, infoHash, magnet = nil)
  raise "addTorrentWithoutMetainfo should be called with a Magnet object, not a #{magnet.class}" if magnet && ! magnet.is_a?(MagnetURI)
  trackerclient = TrackerClient.create(announceUrl, infoHash, 0, false)
  addTorrent(trackerclient, infoHash, nil, magnet)
end
adjustBytesDownloaded(infoHash, adjustment) click to toggle source

Adjust the bytesDownloaded property of the specified torrent by the passed amount. Adjustment should be an integer. It is added to the current bytesUploaded amount.

# File lib/quartz_torrent/peerclient.rb, line 1853
def adjustBytesDownloaded(infoHash, adjustment)
  return if ! adjustment
  raise "Bytes downloaded adjustment must be an Integer, not a #{adjustment.class}" if !adjustment.is_a?(Integer)
  @handler.adjustBytesDownloaded(infoHash, adjustment)
end
adjustBytesUploaded(infoHash, adjustment) click to toggle source

Adjust the bytesUploaded property of the specified torrent by the passed amount. Adjustment should be an integer. It is added to the current bytesUploaded amount.

# File lib/quartz_torrent/peerclient.rb, line 1845
def adjustBytesUploaded(infoHash, adjustment)
  return if ! adjustment
  raise "Bytes uploaded adjustment must be an Integer, not a #{adjustment.class}" if !adjustment.is_a?(Integer)
  @handler.adjustBytesUploaded(infoHash, adjustment)
end
removeTorrent(infoHash, deleteFiles = false) click to toggle source

Remove a currently running torrent

# File lib/quartz_torrent/peerclient.rb, line 1860
def removeTorrent(infoHash, deleteFiles = false)
  @handler.removeTorrent(infoHash, deleteFiles)
end
setDownloadRateLimit(infoHash, bytesPerSecond) click to toggle source

Set the download rate limit in bytes/second.

# File lib/quartz_torrent/peerclient.rb, line 1819
def setDownloadRateLimit(infoHash, bytesPerSecond)
  raise "download rate limit must be an Integer, not a #{bytesPerSecond.class}" if bytesPerSecond && ! bytesPerSecond.is_a?(Integer)
  @handler.setDownloadRateLimit(infoHash, bytesPerSecond)
end
setPaused(infoHash, value) click to toggle source

Pause or unpause the specified torrent.

# File lib/quartz_torrent/peerclient.rb, line 1814
def setPaused(infoHash, value)
  @handler.setPaused(infoHash, value)
end
setUploadDuration(infoHash, seconds) click to toggle source

Set the maximum amount of time (in seconds) that a torrent can be in the upload-only state before it is paused. Pass nil to disable.

# File lib/quartz_torrent/peerclient.rb, line 1838
def setUploadDuration(infoHash, seconds)
  raise "upload ratio must be Numeric, not a #{seconds.class}" if seconds && ! seconds.is_a?(Numeric)
  @handler.setUploadDuration(infoHash, seconds)
end
setUploadRateLimit(infoHash, bytesPerSecond) click to toggle source

Set the upload rate limit in bytes/second.

# File lib/quartz_torrent/peerclient.rb, line 1825
def setUploadRateLimit(infoHash, bytesPerSecond)
  raise "upload rate limit must be an Integer, not a #{bytesPerSecond.class}" if bytesPerSecond && ! bytesPerSecond.is_a?(Integer)
  @handler.setUploadRateLimit(infoHash, bytesPerSecond)
end
setUploadRatio(infoHash, ratio) click to toggle source

Set the upload ratio. Pass nil to disable

# File lib/quartz_torrent/peerclient.rb, line 1831
def setUploadRatio(infoHash, ratio)
  raise "upload ratio must be Numeric, not a #{ratio.class}" if ratio && ! ratio.is_a?(Numeric)
  @handler.setUploadRatio(infoHash, ratio)
end
start() click to toggle source

Start the PeerClient: open the listening port, and start a new thread to begin downloading/uploading pieces. If listening fails, an exception of class Errno::EADDRINUSE is thrown.

# File lib/quartz_torrent/peerclient.rb, line 1741
def start 
  return if ! @stopped
  @logger.info "Starting"

  @reactor.listen("0.0.0.0",@port,:listener_socket)

  @stopped = false
  @worker = Thread.new do
    QuartzTorrent.initThread("peerclient")
    begin
      @toStart.each{ |trackerclient| trackerclient.start }
      @reactor.start 
      @logger.info "Reactor stopped."
      @handler.torrentData.each do |k,v|
        v.trackerClient.stop
      end 
    rescue
      @logger.error "Unexpected exception in worker thread: #{$!}"
      @logger.error $!.backtrace.join("\n")
    end
  end
end
stop() click to toggle source

Stop the PeerClient. This method may take some time to complete.

# File lib/quartz_torrent/peerclient.rb, line 1765
def stop
  return if @stopped

  @logger.info "Stop called. Stopping reactor"
  @reactor.stop
  if @worker
    @logger.info "Worker wait timed out after 10 seconds. Shutting down anyway" if ! @worker.join(10)
  end
  @stopped = true
end
torrentData(infoHash = nil) click to toggle source

Get a hash of new TorrentDataDelegate objects keyed by torrent infohash. This is the method to call to get information about the state of torrents being downloaded.

# File lib/quartz_torrent/peerclient.rb, line 1807
def torrentData(infoHash = nil)
  # This will have to work by putting an event in the handler's queue, and blocking for a response.
  # The handler will build a response and return it.
  @handler.getDelegateTorrentData(infoHash)
end

Private Instance Methods

addTorrent(trackerclient, infoHash, info, magnet = nil) click to toggle source

Helper method for adding a torrent.

# File lib/quartz_torrent/peerclient.rb, line 1866
def addTorrent(trackerclient, infoHash, info, magnet = nil)
  trackerclient.port = @port

  torrentData = @handler.addTrackerClient(infoHash, info, trackerclient)
  torrentData.magnet = magnet

  trackerclient.dynamicRequestParamsBuilder = Proc.new do
    torrentData = @handler.torrentData[infoHash]
    dataLength = (info ? info.dataLength : nil)
    result = TrackerDynamicRequestParams.new(dataLength)
    if torrentData && torrentData.blockState
      result.left = torrentData.blockState.totalLength - torrentData.blockState.completedLength
      result.downloaded = torrentData.bytesDownloadedDataOnly
      result.uploaded = torrentData.bytesUploadedDataOnly
    end
    result
  end

  # If we haven't started yet then add this trackerclient to a queue of
  # trackerclients to start once we are started. If we start too soon we
  # will connect to the tracker, and it will try to connect back to us before we are listening.
  if ! trackerclient.started?
    if @stopped
      @toStart.push trackerclient
    else
      trackerclient.start 
    end
  end

  torrentData.infoHash
end