ll-mktorrent 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b226f9099a7a0c1a43add68ff6e730b3229c1fb1
4
+ data.tar.gz: e3c5cb0daf276d661b6f57a044dd31c6a557268a
5
+ SHA512:
6
+ metadata.gz: 105583b686255b9f09ea34c62f88b2be042fa61da03ff62bce5622d5a2df2fcdbd2007909af8b78763d482dcb93227fde2d9ed628d96fbfe0d5f3f869fd2c67d
7
+ data.tar.gz: c75b506d907354b9123180150d41b01ad36f268cecbd0443a9ba2695b8a6e8101d060b5678b15a84906865c3b20fd6c2732ce9d1b2f5cc98c37b56ebb48650f4
data/lib/mktorrent.rb ADDED
@@ -0,0 +1,266 @@
1
+ require 'bencode'
2
+ require 'digest/sha1'
3
+ require 'date'
4
+ require 'uri'
5
+
6
+ # Sample usage
7
+ #t = Torrent.new("http://your.tracker.com")
8
+ #t.add_file("path/to/file.foo")
9
+ #t.write_torrent("~/Downloads/mytorrent.torrent")
10
+
11
+ class Torrent
12
+ include Comparable
13
+
14
+ attr_reader :torrent_file, :infohash
15
+ attr_accessor :info, :filehashes, :piecelength, :files, :defaultdir, :tracker, :size, :privacy, :webseed, :tracker_list, :pieces_from_file
16
+ attr_writer :creation_date, :from_file
17
+
18
+ # optionally initialize filename
19
+ def initialize(tracker, from_file = false)
20
+ @tracker = tracker
21
+ @piecelength = 512 * 1024 # 512 KB
22
+ @files = []
23
+ @filehashes = []
24
+ @size = 0
25
+ @tracker_list = [ [@tracker] ]
26
+ @defaultdir = "torrent"
27
+ @privacy = 0
28
+ @webseed = ""
29
+ @dirbase = ""
30
+ @from_file = from_file
31
+ build_the_torrent
32
+ yield(self) if block_given?
33
+ end
34
+
35
+ def all_files
36
+ if @files.any?
37
+ @files.collect { |file| file[:path] }
38
+ end
39
+ end
40
+
41
+ def from_file?
42
+ !!@from_file
43
+ end
44
+
45
+ def count
46
+ @files.count
47
+ end
48
+
49
+ def creation_date
50
+ @creation_date ||= DateTime.now.strftime("%s")
51
+ end
52
+
53
+ def path_for_reading_pieces(f)
54
+ if @dirbase.empty? # it's a single file torrent
55
+ f = File.join(File.join(f))
56
+ end
57
+ f = File.join(@dirbase, f) unless @dirbase.empty?
58
+ f
59
+ end
60
+
61
+ def read_pieces(files, length)
62
+ buffer = ""
63
+ files.each do |file|
64
+ f = path_for_reading_pieces(file)
65
+ next if File.directory?(f)
66
+ File.open(f) do |fh|
67
+ begin
68
+ read = fh.read(length - buffer.length)
69
+
70
+ # Make sure file not empty
71
+ unless read.nil?
72
+ if (buffer.length + read.length) == length
73
+ yield(buffer + read)
74
+ buffer = ""
75
+ else
76
+ buffer += read
77
+ end
78
+ end
79
+ end until fh.eof?
80
+ end
81
+ end
82
+
83
+ yield buffer
84
+ end
85
+
86
+ def build
87
+ @info = {
88
+ :announce => @tracker,
89
+ :'announce-list' => @tracker_list,
90
+ :'creation date' => creation_date,
91
+ :info => {
92
+ :name => @defaultdir,
93
+ :'piece length' => @piecelength,
94
+ :files => @files,
95
+ :private => @privacy,
96
+ }
97
+ }
98
+ @info[:info][:pieces] = ""
99
+ @info.merge!({ :'url-list' => @webseed }) if @webseed
100
+ if @files.count > 0
101
+ if from_file?
102
+ @info[:info][:pieces] = pieces_from_file
103
+ else
104
+ read_pieces(all_files, @piecelength) do |piece|
105
+ @info[:info][:pieces] += Digest::SHA1.digest(piece)
106
+ end
107
+ end
108
+ end
109
+ set_infohash
110
+ end
111
+
112
+ def self.from_file(filename)
113
+ data = data_from_file(filename)
114
+
115
+ to = new(data['announce'], true ) do |t|
116
+ t.tracker_list = data['announce-list']
117
+ t.creation_date = data['creation date']
118
+ t.defaultdir = data['info']['name']
119
+ t.piecelength = data['info']['piece length']
120
+ t.files = data['info']['files']
121
+ t.privacy = data['info']['private']
122
+ t.webseed = data['url-list']
123
+ t.pieces_from_file = data['info']['pieces']
124
+ t.build
125
+ end
126
+
127
+ yield to if block_given?
128
+ to
129
+ end
130
+
131
+ def self.data_from_file(filename)
132
+ torrent_file = File.absolute_path(filename)
133
+ data = nil
134
+ File.open(torrent_file, 'rb') do |f|
135
+ data = BEncode.load(StringIO.new f.read)
136
+ end
137
+ raise unless data
138
+ data
139
+ end
140
+
141
+ def write(filename)
142
+ build_the_torrent
143
+ @torrent_file = File.absolute_path(filename)
144
+ open(@torrent_file, 'wb') do |file|
145
+ file.write self.to_s
146
+ end
147
+ end
148
+
149
+ # Return the .torrent file as a string
150
+ def to_s
151
+ return "You must add at least one file." if(@files.count < 1)
152
+ build_the_torrent unless (@info[:info][:files].count == @files.count)
153
+ @info.bencode
154
+ end
155
+
156
+ def add_file(filepath)
157
+ path_for_torrent = path_for_torrent_from_file(filepath)
158
+
159
+ if((@files.select { |f| f[:path] == path_for_torrent } ).count > 0)
160
+ raise IOError, "Can't add duplicate file #{File.basename(filepath)}"
161
+ end
162
+
163
+ # Remove a leading slash
164
+ if ( ! @dirbase.empty?) && filepath[0] == '/'
165
+ filepath = filepath.slice(1, filepath.length)
166
+ end
167
+
168
+ if File.exist?(filepath)
169
+ @files << { path: path_for_torrent, length: File::open(filepath, "rb").size }
170
+ elsif @dirbase && File.exist?(File.join(@dirbase, filepath))
171
+ @files << { path: path_for_torrent, length: File::open(File.join(@dirbase, filepath), "rb").size }
172
+ else
173
+ raise IOError, "Couldn't access #{filepath}"
174
+ end
175
+ end
176
+
177
+ def add_directory(path)
178
+ path = Pathname.new(path)
179
+ @dirbase = File.dirname(path) unless path.relative?
180
+ add_directory_to_torrent(path)
181
+ end
182
+
183
+ def add_directory_to_torrent(path)
184
+ # Using Dir.entries instead of glob so that non-escaped paths can be used
185
+ Dir.entries(path).each do |entry|
186
+ # Ignore unix current and parent directories
187
+ next if entry == '.' or entry == '..'
188
+
189
+ filename = File.join(path, entry).gsub(@dirbase, '') # Add a relative path
190
+ if File.directory?(filename)
191
+ add_directory_to_torrent(filename)
192
+ else
193
+ add_file(filename)
194
+ end
195
+ end
196
+ end
197
+
198
+ def set_webseed(url)
199
+ validate_url!(url)
200
+ @webseed = url
201
+ end
202
+
203
+ def add_tracker(tracker)
204
+ @tracker_list << [tracker]
205
+ end
206
+
207
+ def set_private
208
+ @privacy = 1
209
+ end
210
+
211
+ def set_public
212
+ @privacy = 0
213
+ end
214
+
215
+ # Implement Comparable. Provides info about whether this torrent
216
+ # defines the same files as another one.
217
+ def <=>(obj)
218
+ build
219
+ obj.build
220
+ return -1 if infohash < obj.infohash
221
+ return 0 if infohash == obj.infohash
222
+ 1
223
+ rescue StandardError
224
+ -1
225
+ end
226
+
227
+ # Determine if this matches all the same resources
228
+ # (including tracker information)
229
+ def eql?(obj)
230
+ build
231
+ obj.build
232
+ obj.all_info_matches?(@info)
233
+ rescue StandardError
234
+ false
235
+ end
236
+
237
+ def all_info_matches?(info)
238
+ @info.eql?(info)
239
+ end
240
+
241
+ alias build_the_torrent build
242
+ alias write_torrent write
243
+
244
+ private
245
+ def validate_url!(url)
246
+ u = URI.parse(url)
247
+ if u.scheme.nil? || u.host.nil?
248
+ raise ArgumentError.new("#{url} is not a valid URL")
249
+ end
250
+ end
251
+
252
+ def path_for_torrent_from_file(filepath)
253
+ unless @dirbase.empty?
254
+ filepath = filepath.sub(@dirbase, '')
255
+ end
256
+
257
+ # Remove leading blank item
258
+ path_for_torrent = filepath.split('/') - [""]
259
+
260
+ path_for_torrent
261
+ end
262
+
263
+ def set_infohash
264
+ @infohash = Digest::SHA1.hexdigest @info[:info].bencode
265
+ end
266
+ end
@@ -0,0 +1,95 @@
1
+ require 'test_helper'
2
+
3
+ class MktorrentTest < Minitest::Test
4
+
5
+ def setup
6
+ @torrent = Torrent.new(TRACKER)
7
+ @torrent2 = Torrent.new(TRACKER)
8
+ # Lol. This is pretty bad :)
9
+ fail "Could not find #{VALIDFILEPATH}" unless File.exist? VALIDFILEPATH
10
+ fail "Could not find #{VALIDFILE2PATH}" unless File.exist? VALIDFILE2PATH
11
+ end
12
+
13
+ def test_create_torrent
14
+ assert_equal @torrent.tracker, TRACKER
15
+ end
16
+
17
+ def test_add_file_with_invalid_file
18
+ assert_raises(IOError) { @torrent.add_file("../tmp/bogusfile.vhd") }
19
+ end
20
+
21
+ def test_add_single_valid_file
22
+ @torrent.add_file(VALIDFILEPATH)
23
+ assert_equal @torrent.count, 1
24
+ assert(@torrent.info[:info][:files].select { |f| f[:name] == VALIDFILENAME })
25
+ end
26
+
27
+ def test_add_second_tracker
28
+ @torrent.add_tracker(SNDTRACKER)
29
+ assert_equal 2, @torrent.tracker_list.count
30
+ end
31
+
32
+ def test_add_multiple_trackers
33
+ @torrent.add_tracker(SNDTRACKER)
34
+ @torrent.add_tracker(THDTRACKER)
35
+ assert_equal SNDTRACKER, @torrent.tracker_list[1][0]
36
+ end
37
+
38
+ def test_add_valid_file_as_path_array
39
+ @torrent.add_file(VALIDFILEPATH)
40
+ path = @torrent.info[:info][:files].first[:path]
41
+ assert_kind_of Array, path, "Path should be an array, not #{path}"
42
+ end
43
+
44
+ def test_add_another_valid_file
45
+ @torrent.add_file(VALIDFILEPATH)
46
+ @torrent.add_file(VALIDFILE2PATH)
47
+ assert_equal 2, @torrent.count
48
+ end
49
+
50
+ def test_prevent_duplicate_file_from_being_added
51
+ @torrent.add_file(VALIDFILEPATH)
52
+ assert_raises(IOError) { @torrent.add_file(VALIDFILEPATH) }
53
+ end
54
+
55
+ def test_add_directory_increments_file_count
56
+ @torrent.add_directory(VALIDPATH)
57
+ assert_equal 3, @torrent.count
58
+ end
59
+
60
+ # When adding a directory, only the folder that's added (and everything below) should appear in the metadata
61
+ def test_add_directory_uses_relative_paths
62
+ assert [ VALIDFILEPATH, VALIDFILE2PATH ].each { |p| p.start_with?(VALIDPATH) }
63
+ @torrent.add_directory(VALIDPATH)
64
+ assert @torrent.files.each { |f| ! f[:path].join('/').start_with?(File.basename(File.dirname(VALIDPATH))) }
65
+ end
66
+
67
+ def test_default_privacy
68
+ @torrent.add_file(VALIDFILEPATH)
69
+ assert_equal 0, @torrent.privacy
70
+ end
71
+
72
+ def test_set_privacy
73
+ test_default_privacy
74
+ @torrent.set_private
75
+ assert_equal 1, @torrent.privacy
76
+ end
77
+
78
+ def test_set_valid_webseed
79
+ @torrent.set_webseed(WEBSEED)
80
+ assert_equal WEBSEED, @torrent.webseed
81
+ end
82
+
83
+ def test_add_invalid_webseed
84
+ assert_raises(ArgumentError) {
85
+ @torrent.set_webseed('uheoatnhuetano')
86
+ }
87
+ end
88
+
89
+ def test_torrent2_equality
90
+ assert_equal @torrent, @torrent2
91
+ @torrent2.tracker = 'http://example.com'
92
+ assert @torrent == @torrent2, 'info hash should still match, so torrent == torrent2'
93
+ assert !@torrent.eql?(@torrent2), 'announce uri changed, so torrent ! eql? torrent2'
94
+ end
95
+ end
@@ -0,0 +1,22 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/unit'
3
+ require 'minitest/reporters'
4
+ require 'shellwords'
5
+
6
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'mktorrent')
7
+ Dir.glob('test/support/*.rb').each { |r| load r }
8
+
9
+ Minitest::Reporters.use! [
10
+ Minitest::Reporters::DefaultReporter.new(color: true),
11
+ ]
12
+
13
+
14
+ TRACKER = "http://test.example.com"
15
+ SNDTRACKER = "udp://test.tracker.tk:80"
16
+ THDTRACKER = "udp://open.testtracker.com:6500/announce"
17
+ WEBSEED = "http://seed.example.com/webseed"
18
+ EXPECTED_INFOHASH = "a8036d51ada8fb699c9f29d7861e5589f2d20cf9"
19
+ VALIDPATH = File.expand_path("#{File.dirname(__FILE__)}/test_data")
20
+ VALIDFILEPATH = File.expand_path("#{File.dirname(__FILE__)}/test_data/sample_file1.vhd")
21
+ VALIDFILE2PATH = File.expand_path("#{File.dirname(__FILE__)}/test_data/sample_file2.vhd")
22
+ VALIDFILENAME = "randomfile.vhd"
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ll-mktorrent
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Timothy Mukaibo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bencode
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest-reporters
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubygems-tasks
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.2.4
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.2.4
97
+ description: Create .torrent files easily with this gem. The code is ugly, but it
98
+ works :)
99
+ email: timothy@mukaibo.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - lib/mktorrent.rb
105
+ - test/mktorrent_test.rb
106
+ - test/test_helper.rb
107
+ homepage: https://github.com/mukaibot/mktorrent
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.6.13
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Create .torrent files easily with this gem
131
+ test_files:
132
+ - test/mktorrent_test.rb
133
+ - test/test_helper.rb