mktorrent 1.5.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
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