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 +4 -4
- data/lib/mktorrent.rb +66 -46
- data/test/mktorrent_acceptance_test.rb +47 -0
- data/test/mktorrent_test.rb +25 -8
- data/test/test_helper.rb +18 -0
- metadata +35 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d4731c3daab47190d6c9e8bb7ab4a3e6c2969da
|
4
|
+
data.tar.gz: bb119f7dbab44825092c5662c3b72764cd4c0b96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
-
|
46
|
-
File.open(f
|
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
|
-
|
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 = {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
91
|
-
|
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
|
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
|
-
|
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
|
-
|
112
|
-
|
113
|
-
@files << { path:
|
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
|
134
|
+
filename = File.join(path, entry).gsub(@dirbase, '') # Add a relative path
|
126
135
|
if File.directory?(filename)
|
127
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
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
|
data/test/mktorrent_test.rb
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
-
require '
|
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
|
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
|
data/test/test_helper.rb
ADDED
@@ -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.
|
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-
|
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
|