class Torrent

Sample usage

t = Torrent.new(“your.tracker.com”) t.add_file(“path/to/file.foo”) t.write_torrent(“~/Downloads/mytorrent.torrent”)

Attributes

defaultdir[RW]
filehashes[RW]
files[RW]
info[RW]
infohash[R]
piecelength[RW]
privacy[RW]
size[RW]
torrent_file[R]
tracker[RW]
tracker_list[RW]
webseed[RW]

Public Class Methods

new(tracker) click to toggle source

optionally initialize filename

# File lib/mktorrent.rb, line 16
def initialize(tracker)
  @tracker = tracker
  @piecelength = 512 * 1024 # 512 KB
  @files = []
  @filehashes = []
  @size = 0
  @tracker_list = [ [@tracker] ]
  @defaultdir = "torrent"
  @privacy = 0
  @webseed = ""
  @dirbase = ""
  build_the_torrent
end

Public Instance Methods

add_directory(path) click to toggle source
# File lib/mktorrent.rb, line 131
def add_directory(path)
  path = Pathname.new(path)
  @dirbase = File.dirname(path) unless path.relative?
  add_directory_to_torrent(path)
end
add_directory_to_torrent(path) click to toggle source
# File lib/mktorrent.rb, line 137
def add_directory_to_torrent(path)
  # Using Dir.entries instead of glob so that non-escaped paths can be used
  Dir.entries(path).each do |entry|
    # Ignore unix current and parent directories
    next if entry == '.' or entry == '..'

    filename = File.join(path, entry).gsub(@dirbase, '') # Add a relative path
    if File.directory?(filename)
      add_directory_to_torrent(filename)
    else
      add_file(filename)
    end
  end
end
add_file(filepath) click to toggle source
# File lib/mktorrent.rb, line 110
def add_file(filepath)
  path_for_torrent = path_for_torrent_from_file(filepath)

  if((@files.select { |f| f[:path] == path_for_torrent } ).count > 0)
    raise IOError, "Can't add duplicate file #{File.basename(filepath)}"
  end

  # Remove a leading slash
  if ( ! @dirbase.empty?) && filepath[0] == '/'
    filepath = filepath.slice(1, filepath.length)
  end

  if File.exist?(filepath)
    @files << { path: path_for_torrent, length: File::open(filepath, "rb").size }
  elsif @dirbase && File.exist?(File.join(@dirbase, filepath))
    @files << { path: path_for_torrent, length: File::open(File.join(@dirbase, filepath), "rb").size }
  else
    raise IOError, "Couldn't access #{filepath}"
  end
end
add_tracker(tracker) click to toggle source
# File lib/mktorrent.rb, line 157
def add_tracker(tracker)
  @tracker_list << [tracker]
end
all_files() click to toggle source
# File lib/mktorrent.rb, line 30
def all_files
  if @files.any?
    @files.collect { |file| file[:path] }
  end
end
build() click to toggle source
# File lib/mktorrent.rb, line 73
def build
  @info = {
    :announce => @tracker,
    :'announce-list' => @tracker_list,
    :'creation date' => DateTime.now.strftime("%s"),
    :info => {
      :name => @defaultdir,
      :'piece length' => @piecelength,
      :files => @files,
      :private => @privacy,
    }
  }
  @info[:info][:pieces] = ""
  @info.merge!({ :'url-list' => @webseed }) unless @webseed.empty?
  if @files.count > 0
    read_pieces(all_files, @piecelength) do |piece|
      @info[:info][:pieces] += Digest::SHA1.digest(piece)
    end
  end
end
Also aliased as: build_the_torrent
build_the_torrent()
Alias for: build
count() click to toggle source
# File lib/mktorrent.rb, line 36
def count
  @files.count
end
path_for_reading_pieces(f) click to toggle source
# File lib/mktorrent.rb, line 40
def path_for_reading_pieces(f)
  if @dirbase.empty? # it's a single file torrent
    f = File.join(File.join(f))
  end
  f = File.join(@dirbase, f) unless @dirbase.empty?
  f
end
read_pieces(files, length) { |buffer| ... } click to toggle source
# File lib/mktorrent.rb, line 48
def read_pieces(files, length)
  buffer = ""
  files.each do |file|
    f = path_for_reading_pieces(file)
    next if File.directory?(f)
    File.open(f) do |fh|
      begin
        read = fh.read(length - buffer.length)

        # Make sure file not empty
        unless read.nil?
          if (buffer.length + read.length) == length
            yield(buffer + read)
            buffer = ""
          else
            buffer += read
          end
        end
      end until fh.eof?
    end
  end

  yield buffer
end
set_private() click to toggle source
# File lib/mktorrent.rb, line 161
def set_private
  @privacy = 1
end
set_public() click to toggle source
# File lib/mktorrent.rb, line 165
def set_public
  @privacy = 0
end
set_webseed(url) click to toggle source
# File lib/mktorrent.rb, line 152
def set_webseed(url)
  validate_url!(url)
  @webseed = url
end
to_s() click to toggle source

Return the .torrent file as a string

# File lib/mktorrent.rb, line 104
def to_s
  return "You must add at least one file." if(@files.count < 1)
  build_the_torrent unless (@info[:info][:files].count == @files.count)
  @info.bencode
end
write_torrent(filename) click to toggle source
# File lib/mktorrent.rb, line 94
def write_torrent(filename)
  build_the_torrent
  @torrent_file = File.absolute_path(filename)
  open(@torrent_file, 'wb') do |file|
    file.write self.to_s
  end
  set_infohash
end

Private Instance Methods

path_for_torrent_from_file(filepath) click to toggle source
# File lib/mktorrent.rb, line 179
def path_for_torrent_from_file(filepath)
  unless @dirbase.empty?
    filepath = filepath.sub(@dirbase, '')
  end

  # Remove leading blank item
  path_for_torrent = filepath.split('/') - [""]

  path_for_torrent
end
set_infohash() click to toggle source
# File lib/mktorrent.rb, line 190
def set_infohash
  @infohash = Digest::SHA1.hexdigest @info[:info].bencode
end
validate_url!(url) click to toggle source
# File lib/mktorrent.rb, line 172
def validate_url!(url)
  u = URI.parse(url)
  if u.scheme.nil? || u.host.nil?
    raise ArgumentError.new("#{url} is not a valid URL")
  end
end