Class: Torrent

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/mktorrent.rb

Overview

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tracker, from_file = false) {|_self| ... } ⇒ Torrent

optionally initialize filename

Yields:

  • (_self)

Yield Parameters:

  • _self (Torrent)

    the object that the method was called on



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mktorrent.rb', line 19

def initialize(tracker, from_file = false)
  @tracker = tracker
  @piecelength = 512 * 1024 # 512 KB
  @files = []
  @filehashes = []
  @size = 0
  @tracker_list = [ [@tracker] ]
  @defaultdir = "torrent"
  @privacy = 0
  @webseed = ""
  @dirbase = ""
  @from_file = from_file
  build_the_torrent
  yield(self) if block_given?
end

Instance Attribute Details

#creation_dateObject



49
50
51
# File 'lib/mktorrent.rb', line 49

def creation_date
  @creation_date ||= DateTime.now.strftime("%s")
end

#defaultdirObject

Returns the value of attribute defaultdir.



15
16
17
# File 'lib/mktorrent.rb', line 15

def defaultdir
  @defaultdir
end

#filehashesObject

Returns the value of attribute filehashes.



15
16
17
# File 'lib/mktorrent.rb', line 15

def filehashes
  @filehashes
end

#filesObject

Returns the value of attribute files.



15
16
17
# File 'lib/mktorrent.rb', line 15

def files
  @files
end

#from_file=(value) ⇒ Object (writeonly)

Sets the attribute from_file

Parameters:

  • value

    the value to set the attribute from_file to.



16
17
18
# File 'lib/mktorrent.rb', line 16

def from_file=(value)
  @from_file = value
end

#infoObject

Returns the value of attribute info.



15
16
17
# File 'lib/mktorrent.rb', line 15

def info
  @info
end

#infohashObject (readonly)

Returns the value of attribute infohash.



14
15
16
# File 'lib/mktorrent.rb', line 14

def infohash
  @infohash
end

#piecelengthObject

Returns the value of attribute piecelength.



15
16
17
# File 'lib/mktorrent.rb', line 15

def piecelength
  @piecelength
end

#pieces_from_fileObject

Returns the value of attribute pieces_from_file.



15
16
17
# File 'lib/mktorrent.rb', line 15

def pieces_from_file
  @pieces_from_file
end

#privacyObject

Returns the value of attribute privacy.



15
16
17
# File 'lib/mktorrent.rb', line 15

def privacy
  @privacy
end

#sizeObject

Returns the value of attribute size.



15
16
17
# File 'lib/mktorrent.rb', line 15

def size
  @size
end

#torrent_fileObject (readonly)

Returns the value of attribute torrent_file.



14
15
16
# File 'lib/mktorrent.rb', line 14

def torrent_file
  @torrent_file
end

#trackerObject

Returns the value of attribute tracker.



15
16
17
# File 'lib/mktorrent.rb', line 15

def tracker
  @tracker
end

#tracker_listObject

Returns the value of attribute tracker_list.



15
16
17
# File 'lib/mktorrent.rb', line 15

def tracker_list
  @tracker_list
end

#webseedObject

Returns the value of attribute webseed.



15
16
17
# File 'lib/mktorrent.rb', line 15

def webseed
  @webseed
end

Class Method Details

.data_from_file(filename) ⇒ Object



131
132
133
134
135
136
137
138
139
# File 'lib/mktorrent.rb', line 131

def self.data_from_file(filename)
  torrent_file = File.absolute_path(filename)
  data = nil
  File.open(torrent_file, 'rb') do |f|
    data = BEncode.load(StringIO.new f.read)
  end
  raise unless data
  data
end

.from_file(filename) {|to| ... } ⇒ Object

Yields:

  • (to)


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/mktorrent.rb', line 112

def self.from_file(filename)
  data = data_from_file(filename)

  to = new(data['announce'], true ) do |t|
    t.tracker_list = data['announce-list']
    t.creation_date = data['creation date']
    t.defaultdir = data['info']['name']
    t.piecelength = data['info']['piece length']
    t.files = data['info']['files']
    t.privacy = data['info']['private']
    t.webseed = data['url-list']
    t.pieces_from_file = data['info']['pieces']
    t.build
  end

  yield to if block_given?
  to
end

Instance Method Details

#<=>(obj) ⇒ Object

Implement Comparable. Provides info about whether this torrent defines the same files as another one.



217
218
219
220
221
222
223
224
225
# File 'lib/mktorrent.rb', line 217

def <=>(obj)
  build
  obj.build
  return -1 if infohash < obj.infohash
  return 0 if infohash == obj.infohash
  1
rescue StandardError
  -1
end

#add_directory(path) ⇒ Object



177
178
179
180
181
# File 'lib/mktorrent.rb', line 177

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) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/mktorrent.rb', line 183

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) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/mktorrent.rb', line 156

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) ⇒ Object



203
204
205
# File 'lib/mktorrent.rb', line 203

def add_tracker(tracker)
  @tracker_list << [tracker]
end

#all_filesObject



35
36
37
38
39
# File 'lib/mktorrent.rb', line 35

def all_files
  if @files.any?
    @files.collect { |file| file[:path] }
  end
end

#all_info_matches?(info) ⇒ Boolean

Returns:

  • (Boolean)


237
238
239
# File 'lib/mktorrent.rb', line 237

def all_info_matches?(info)
  @info.eql?(info)
end

#buildObject Also known as: build_the_torrent



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
# File 'lib/mktorrent.rb', line 86

def build
  @info = {
    :announce => @tracker,
    :'announce-list' => @tracker_list,
    :'creation date' => creation_date,
    :info => {
      :name => @defaultdir,
      :'piece length' => @piecelength,
      :files => @files,
      :private => @privacy,
    }
  }
  @info[:info][:pieces] = ""
  @info.merge!({ :'url-list' => @webseed }) if @webseed
  if @files.count > 0
    if from_file?
      @info[:info][:pieces] = pieces_from_file
    else
      read_pieces(all_files, @piecelength) do |piece|
        @info[:info][:pieces] += Digest::SHA1.digest(piece)
      end
    end
  end
  set_infohash
end

#countObject



45
46
47
# File 'lib/mktorrent.rb', line 45

def count
  @files.count
end

#eql?(obj) ⇒ Boolean

Determine if this matches all the same resources (including tracker information)

Returns:

  • (Boolean)


229
230
231
232
233
234
235
# File 'lib/mktorrent.rb', line 229

def eql?(obj)
  build
  obj.build
  obj.all_info_matches?(@info)
rescue StandardError
  false
end

#from_file?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/mktorrent.rb', line 41

def from_file?
  !!@from_file
end

#path_for_reading_pieces(f) ⇒ Object



53
54
55
56
57
58
59
# File 'lib/mktorrent.rb', line 53

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| ... } ⇒ Object

Yields:

  • (buffer)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/mktorrent.rb', line 61

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_privateObject



207
208
209
# File 'lib/mktorrent.rb', line 207

def set_private
  @privacy = 1
end

#set_publicObject



211
212
213
# File 'lib/mktorrent.rb', line 211

def set_public
  @privacy = 0
end

#set_webseed(url) ⇒ Object



198
199
200
201
# File 'lib/mktorrent.rb', line 198

def set_webseed(url)
  validate_url!(url)
  @webseed = url
end

#to_sObject

Return the .torrent file as a string



150
151
152
153
154
# File 'lib/mktorrent.rb', line 150

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(filename) ⇒ Object Also known as: write_torrent



141
142
143
144
145
146
147
# File 'lib/mktorrent.rb', line 141

def write(filename)
  build_the_torrent
  @torrent_file = File.absolute_path(filename)
  open(@torrent_file, 'wb') do |file|
    file.write self.to_s
  end
end