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 +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
|