mktorrent 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2c1e1c11c1f953694f5c37137f7bae096fcafef
4
- data.tar.gz: ba510aa5d85d4d5ae56a1dd603e3e218d408d156
3
+ metadata.gz: 5d4731c3daab47190d6c9e8bb7ab4a3e6c2969da
4
+ data.tar.gz: bb119f7dbab44825092c5662c3b72764cd4c0b96
5
5
  SHA512:
6
- metadata.gz: 27a025b8b1f7c17ac729610e5b85a4c41d84d4224f510738bfa32fa04277b82aaa37fb8d01bf102b339d27a9f76e815f5aa4fca9e3b22f847e30aed317fd111d
7
- data.tar.gz: e3a661b103900c77446bda96cb45371462eea67094f681f277298854a919f7e1082a45c213c4711a68c20f7a45ea0811c976088ffa0ca39fd977fb039eb1bffd
6
+ metadata.gz: 8a6af671931fa2fc18678567a748890e59513c75f901db964fb4c440b1981205cb658ad776a5f7e537a7e30d1c81a0a82d85e1439c7b345a901be1aba7a88410
7
+ data.tar.gz: 775bd09318ccf12386317b6dc31225f95d4f621cb2ee6e05361961e450ee8ff1b11d509f9a90272ac791497d1659df08f6d733510232b20bccd6cc121fa69bc6
data/lib/mktorrent.rb CHANGED
@@ -1,17 +1,16 @@
1
1
  require 'bencode'
2
2
  require 'digest/sha1'
3
3
  require 'date'
4
+ require 'uri'
4
5
 
5
6
  # Sample usage
6
7
  #t = Torrent.new("http://your.tracker.com")
7
8
  #t.add_file("path/to/file.foo")
8
9
  #t.write_torrent("~/Downloads/mytorrent.torrent")
9
10
 
10
- # TODO
11
- # Support tracker-list
12
-
13
11
  class Torrent
14
- attr_accessor :info, :filehashes, :piecelength, :files, :defaultdir, :tracker, :size, :privacy
12
+ attr_reader :torrent_file
13
+ attr_accessor :info, :filehashes, :piecelength, :files, :defaultdir, :tracker, :size, :privacy, :webseeds
15
14
 
16
15
  # optionally initialize filename
17
16
  def initialize(tracker)
@@ -22,17 +21,15 @@ class Torrent
22
21
  @size = 0
23
22
  @defaultdir = "torrent"
24
23
  @privacy = 0
24
+ @webseeds = []
25
+ @dirbase = ""
25
26
  build_the_torrent
26
27
  end
27
28
 
28
29
  def all_files
29
30
  unless @files.count < 1
30
- all_files = []
31
- @files.each do |f|
32
- all_files << f[:path]
33
- end
31
+ @files.collect { |file| file[:path] }
34
32
  end
35
- all_files
36
33
  end
37
34
 
38
35
  def count
@@ -42,13 +39,13 @@ class Torrent
42
39
  def read_pieces(files, length)
43
40
  buffer = ""
44
41
  files.each do |f|
45
- puts "hashing #{f.join("/")}"
46
- File.open(f.join("/")) do |fh|
42
+ f = File.join(@dirbase, f) unless @dirbase.empty?
43
+ File.open(f) do |fh|
47
44
  begin
48
45
  read = fh.read(length - buffer.length)
49
46
 
50
47
  # Make sure file not empty
51
- if not read.nil?
48
+ unless read.nil?
52
49
  if (buffer.length + read.length) == length
53
50
  yield(buffer + read)
54
51
  buffer = ""
@@ -64,35 +61,34 @@ class Torrent
64
61
  end
65
62
 
66
63
  def build
67
- @info = { :announce => @tracker,
68
- :'creation date' => DateTime.now.strftime("%s"),
69
- :info => { :name => @defaultdir,
70
- :'piece length' => @piecelength,
71
- :files => @files,
72
- :private => @privacy
73
- }
74
- }
64
+ @info = {
65
+ :announce => @tracker,
66
+ :'creation date' => DateTime.now.strftime("%s"),
67
+ :info => {
68
+ :name => @defaultdir,
69
+ :'piece length' => @piecelength,
70
+ :files => @files,
71
+ :private => @privacy,
72
+ }
73
+ }
75
74
  @info[:info][:pieces] = ""
75
+ @info[:url-list] = @webseeds if @webseeds.any?
76
76
  if @files.count > 0
77
77
  i = 0
78
78
  read_pieces(all_files, @piecelength) do |piece|
79
79
  @info[:info][:pieces] += Digest::SHA1.digest(piece)
80
80
  i += 1
81
- if (i % 100) == 0
82
- #print "#{(i.to_f / num_pieces * 100.0).round}%... "; $stdout.flush
83
- end
84
81
  end
85
82
  end
86
83
  end
87
84
 
88
85
  def write_torrent(filename)
89
86
  build_the_torrent
90
- open(filename, 'wb') do |torrentfile|
91
- torrentfile.write self.to_s
87
+ @torrent_file = File.absolute_path(filename)
88
+ open(@torrent_file, 'wb') do |file|
89
+ file.write self.to_s
92
90
  end
93
- torrent_file = "#{`pwd`.chomp}/#{filename}"
94
- puts "Wrote #{torrent_file}"
95
- torrent_file
91
+ @torrent_file
96
92
  end
97
93
 
98
94
  # Return the .torrent file as a string
@@ -103,45 +99,50 @@ class Torrent
103
99
  end
104
100
 
105
101
  def add_file(filepath)
106
- if((@files.select { |f| f[:path].join('/') == filepath } ).count > 0)
102
+ path_for_torrent = path_for_torrent_from_file(filepath)
103
+
104
+ if((@files.select { |f| f[:path] == path_for_torrent } ).count > 0)
107
105
  raise IOError, "Can't add duplicate file #{File.basename(filepath)}"
108
106
  end
109
107
 
108
+ # Remove a leading slash
109
+ if ( ! @dirbase.empty?) && filepath[0] == '/'
110
+ filepath = filepath.slice(1, filepath.length)
111
+ end
112
+
110
113
  if File.exist?(filepath)
111
- #filesize = hash_pieces(filepath)
112
- # TODO tidy the path up...
113
- @files << { path: filepath.split('/'), length: File::open(filepath, "rb").size }
114
+ @files << { path: path_for_torrent, length: File::open(filepath, "rb").size }
115
+ elsif @dirbase && File.exist?(File.join(@dirbase, filepath))
116
+ @files << { path: path_for_torrent, length: File::open(File.join(@dirbase, filepath), "rb").size }
114
117
  else
115
118
  raise IOError, "Couldn't access #{filepath}"
116
119
  end
117
120
  end
118
121
 
119
122
  def add_directory(path)
123
+ path = Pathname.new(path)
124
+ @dirbase = File.dirname(path) unless path.relative?
125
+ add_directory_to_torrent(path)
126
+ end
127
+
128
+ def add_directory_to_torrent(path)
120
129
  # Using Dir.entries instead of glob so that non-escaped paths can be used
121
130
  Dir.entries(path).each do |entry|
122
131
  # Ignore unix current and parent directories
123
132
  next if entry == '.' or entry == '..'
124
133
 
125
- filename = path + File::SEPARATOR + entry
134
+ filename = File.join(path, entry).gsub(@dirbase, '') # Add a relative path
126
135
  if File.directory?(filename)
127
- add_directory(filename)
128
- else
136
+ add_directory_to_torrent(filename)
137
+ else
129
138
  add_file(filename)
130
139
  end
131
140
  end
132
141
  end
133
142
 
134
- # Need to read the files in @piecelength chunks and hash against that
135
- def hash_pieces(files)
136
- offset = 0
137
- f = File::open(file, "rb")
138
- @size += f.size
139
- while offset < f.size do
140
- offset += @piecelength
141
- STDOUT.write "\r#{File.basename(file)}: hashed #{(offset.to_f / f.size.to_f)*100}%"
142
- STDOUT.flush
143
- end
144
- return f.size
143
+ def add_webseed(url)
144
+ validate_url!(url)
145
+ webseeds << url unless webseeds.include?(url)
145
146
  end
146
147
 
147
148
  def set_private
@@ -153,4 +154,23 @@ class Torrent
153
154
  end
154
155
 
155
156
  alias build_the_torrent build
157
+
158
+ private
159
+ def validate_url!(url)
160
+ u = URI.parse(url)
161
+ if u.scheme.nil? || u.host.nil?
162
+ raise ArgumentError.new("#{url} is not a valid URL")
163
+ end
164
+ end
165
+
166
+ def path_for_torrent_from_file(filepath)
167
+ unless @dirbase.empty?
168
+ filepath = filepath.sub(@dirbase, '')
169
+ end
170
+
171
+ # Remove leading blank item
172
+ path_for_torrent = filepath.split('/') - [""]
173
+
174
+ path_for_torrent
175
+ end
156
176
  end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+
4
+ # Run an external command against a torrent we generate for validation
5
+ class MktorrentAcceptanceTest < Minitest::Test
6
+
7
+ def setup
8
+ acceptance_command_file = File.open(File.join(File.dirname(__FILE__), 'acceptance_test_command')).read
9
+ content = acceptance_command_file.split("\n")
10
+ @command = content.delete_if { |line| line.start_with?("#") || line.strip.empty? }
11
+ @torrent = create_torrent_for_test
12
+ end
13
+
14
+ def teardown
15
+ if @torrent && @torrent.torrent_file && File.exist?(@torrent.torrent_file)
16
+ FileUtils.rm @torrent.torrent_file
17
+ end
18
+ end
19
+
20
+ def test_the_command_to_run
21
+ assert_equal 1, @command.count, "Couldn't determine command to run"
22
+ end
23
+
24
+ def test_directory_has_data
25
+ assert_operator Dir.entries(VALIDPATH).count, :>, 3 # At least one file, include '.' and '..'
26
+ end
27
+
28
+ def test_torrent_passes_acceptance
29
+ assert File.exist?(@torrent.torrent_file)
30
+ command = set_validation_command!
31
+ output = `#{command}`
32
+ result = $?
33
+ assert result.success?, "Expected success but was #{result.to_i}. Command run: \n\n#{command}\nOutput: \n\n#{output}"
34
+ end
35
+
36
+ private
37
+ def create_torrent_for_test
38
+ torrent = Torrent.new('http://some.tracker.com')
39
+ torrent.add_directory(VALIDPATH)
40
+ torrent.write_torrent('torrent_test.torrent')
41
+ torrent
42
+ end
43
+
44
+ def set_validation_command!
45
+ @command.first.sub('<TORRENT_FILE>', @torrent.torrent_file).split(' ').shelljoin
46
+ end
47
+ end
@@ -1,12 +1,6 @@
1
- require 'minitest/autorun'
2
- require File.join(File.dirname(__FILE__), '..', 'lib', 'mktorrent')
1
+ require 'test_helper'
3
2
 
4
3
  class MktorrentTest < Minitest::Test
5
- TRACKER = "http://test.example.com"
6
- VALIDPATH = File.expand_path("#{File.dirname(__FILE__)}/test_data")
7
- VALIDFILEPATH = File.expand_path("#{File.dirname(__FILE__)}/test_data/sample_file1.vhd")
8
- VALIDFILE2PATH = File.expand_path("#{File.dirname(__FILE__)}/test_data/sample_file2.vhd")
9
- VALIDFILENAME = "randomfile.vhd"
10
4
 
11
5
  def setup
12
6
  @torrent = Torrent.new(TRACKER)
@@ -29,6 +23,12 @@ class MktorrentTest < Minitest::Test
29
23
  assert(@torrent.info[:info][:files].select { |f| f[:name] == VALIDFILENAME })
30
24
  end
31
25
 
26
+ def test_add_valid_file_as_path_array
27
+ @torrent.add_file(VALIDFILEPATH)
28
+ path = @torrent.info[:info][:files].first[:path]
29
+ assert_kind_of Array, path, "Path should be an array, not #{path}"
30
+ end
31
+
32
32
  def test_add_another_valid_file
33
33
  @torrent.add_file(VALIDFILEPATH)
34
34
  @torrent.add_file(VALIDFILE2PATH)
@@ -40,11 +40,17 @@ class MktorrentTest < Minitest::Test
40
40
  assert_raises(IOError) { @torrent.add_file(VALIDFILEPATH) }
41
41
  end
42
42
 
43
- def test_add_directory
43
+ def test_add_directory_increments_file_count
44
44
  @torrent.add_directory(VALIDPATH)
45
45
  assert_equal 2, @torrent.count
46
46
  end
47
47
 
48
+ def test_add_directory_uses_relative_paths
49
+ assert [ VALIDFILEPATH, VALIDFILE2PATH ].each { |p| p.start_with?(VALIDPATH) }
50
+ @torrent.add_directory(VALIDPATH)
51
+ assert @torrent.files.each { |f| ! f[:path].join('/').start_with?(VALIDPATH) }
52
+ end
53
+
48
54
  def test_default_privacy
49
55
  @torrent.add_file(VALIDFILEPATH)
50
56
  assert_equal 0, @torrent.privacy
@@ -56,4 +62,15 @@ class MktorrentTest < Minitest::Test
56
62
  assert_equal 1, @torrent.privacy
57
63
  end
58
64
 
65
+ def test_add_valid_webseed
66
+ @torrent.add_webseed(WEBSEED)
67
+ assert_equal 1, @torrent.webseeds.count
68
+ assert_equal WEBSEED, @torrent.webseeds.first
69
+ end
70
+
71
+ def test_add_invalid_webseed
72
+ assert_raises(ArgumentError) {
73
+ @torrent.add_webseed('uheoatnhuetano')
74
+ }
75
+ end
59
76
  end
@@ -0,0 +1,18 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/unit'
3
+ require 'minitest/reporters'
4
+
5
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'mktorrent')
6
+ Dir.glob('test/support/*.rb').each { |r| load r }
7
+
8
+ Minitest::Reporters.use! [
9
+ Minitest::Reporters::DefaultReporter.new(color: true),
10
+ ]
11
+
12
+
13
+ TRACKER = "http://test.example.com"
14
+ WEBSEED = "http://seed.example.com/webseed"
15
+ VALIDPATH = File.expand_path("#{File.dirname(__FILE__)}/test_data")
16
+ VALIDFILEPATH = File.expand_path("#{File.dirname(__FILE__)}/test_data/sample_file1.vhd")
17
+ VALIDFILE2PATH = File.expand_path("#{File.dirname(__FILE__)}/test_data/sample_file2.vhd")
18
+ VALIDFILENAME = "randomfile.vhd"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mktorrent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timothy Mukaibo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-09 00:00:00.000000000 Z
11
+ date: 2015-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bencode
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.1'
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'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: minitest
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,20 @@ dependencies:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
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'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rubygems-tasks
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -74,7 +102,9 @@ extensions: []
74
102
  extra_rdoc_files: []
75
103
  files:
76
104
  - lib/mktorrent.rb
105
+ - test/mktorrent_acceptance_test.rb
77
106
  - test/mktorrent_test.rb
107
+ - test/test_helper.rb
78
108
  homepage: https://github.com/mukaibot/mktorrent
79
109
  licenses:
80
110
  - MIT
@@ -95,9 +125,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
125
  version: '0'
96
126
  requirements: []
97
127
  rubyforge_project:
98
- rubygems_version: 2.4.5
128
+ rubygems_version: 2.4.5.1
99
129
  signing_key:
100
130
  specification_version: 4
101
131
  summary: Create .torrent files easily with this gem
102
132
  test_files:
133
+ - test/mktorrent_acceptance_test.rb
103
134
  - test/mktorrent_test.rb
135
+ - test/test_helper.rb