bencview 0.0.2 → 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 92a0139e1908590118e2d0bd7e0db48b3f8a94e7
4
+ data.tar.gz: a18f7b361bb9d4dcb5e83f45fd1d0773306db79f
5
+ SHA512:
6
+ metadata.gz: b8221a19897cc70e617566f8379def9e18fbe1085a4867a0265cf77566c4244383fe60b2d01700bdc99dd3ecbb0fff2ff7d298f8520825384acf081c4c9989a9
7
+ data.tar.gz: ab6bc66c3b656aa476e673d352e23e8ec1bd1de14008d53799d54d7b7c0bbcdd23abe97bc414f82ac4b116ba48c85fabb5249d944ff04fd63440e374f9b1418a
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # bencview
2
+
3
+ `bencview` - a .torrent files viewer for terminal pagers.\
4
+ `json2bencode` - a converter from JSON to the torrent files format.
5
+
6
+ (Bencode is a data serialization format used by the BitTorrent network.)
7
+
8
+ ## Installation
9
+
10
+ (Ruby 2.3.0+)
11
+
12
+ $ gem install bencview
13
+
14
+ Add to your `lesspipe.sh`:
15
+
16
+ <pre>
17
+ case "$1" in
18
+ ...
19
+ <b> *.torrent) bencview "$1" ;;</b>
20
+ ...
21
+ esac
22
+ </pre>
23
+
24
+ Optionally:
25
+
26
+ $ npm -g i json
27
+
28
+ ## Usage
29
+
30
+ View a torrent file:
31
+
32
+ ~~~
33
+ $ bencview gimp.torrent
34
+ infohash: a85b7e7f035c55f684238d0e252b273fe2a1ccf5
35
+ uri: magnet:?xt=urn:btih:a85b7e7f035c55f684238d0e252b273fe2a1ccf5&dn=gimp-2.8.14-setup-1.exe.torrent
36
+ announce: udp://tracker.publicbt.com:80
37
+ announce-list: 2
38
+ udp://tracker.publicbt.com:80
39
+ udp://tracker.openbittorrent.com:80
40
+ comment: GIMP 2.8.14 Installer for Microsoft Windows - updated
41
+ created by: mktorrent 1.0
42
+ creation date: Tue, 2 Sep 2014 22:05:50 +0000
43
+ url-list: 34
44
+ http://gimper.net/downloads/pub/gimp/v2.8/windows
45
+ http://gimp.afri.cc/pub/gimp/v2.8/windows
46
+ [...]
47
+ info/name: gimp-2.8.14-setup-1.exe
48
+ info/files: 1
49
+ 91,931,728 gimp-2.8.14-setup-1.exe
50
+ info/files size: 91,931,728
51
+ ~~~
52
+
53
+ Remove all trackers from it:
54
+
55
+ ~~~
56
+ $ bencview -j gimp.torrent | json -e 'delete this.announce; delete this["announce-list"]' | json2bencode > file.torrent
57
+
58
+ $ file file.torrent
59
+ file.torrent: BitTorrent file
60
+
61
+ $ bencview file.torrent
62
+ infohash: a85b7e7f035c55f684238d0e252b273fe2a1ccf5
63
+ uri: magnet:?xt=urn:btih:a85b7e7f035c55f684238d0e252b273fe2a1ccf5&dn=gimp-2.8.14-setup-1.exe.torrent
64
+ comment: GIMP 2.8.14 Installer for Microsoft Windows - updated
65
+ created by: mktorrent 1.0
66
+ creation date: Tue, 2 Sep 2014 22:05:50 +0000
67
+ url-list: 34
68
+ http://gimper.net/downloads/pub/gimp/v2.8/windows
69
+ http://gimp.afri.cc/pub/gimp/v2.8/windows
70
+ [...]
71
+ info/name: gimp-2.8.14-setup-1.exe
72
+ info/files: 1
73
+ 91,931,728 gimp-2.8.14-setup-1.exe
74
+ info/files size: 91,931,728
75
+ ~~~
76
+
77
+ Note that the infohash hasn't changed.
78
+
79
+ ## History
80
+
81
+ This is a complete rewrite of the original
82
+ bencview-0.0.x. `bencview_clean` util is gone, for `bencview` can
83
+ export torrent files into JSON.
84
+
85
+ 1.0.0 version is also ~2 times smaller.
86
+
87
+ ## Bugs
88
+
89
+ * Both utils assume the UTF8 locale.
90
+
91
+ ## License
92
+
93
+ MIT.
data/bencview ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ require_relative './bencview'
6
+
7
+ opt = {}
8
+ OptionParser.new do |o|
9
+ o.banner = "Usage: #{File.basename $0} [-jr] [input]"
10
+ o.on("-j", "Output as JSON") do
11
+ opt[:json] = true
12
+ end
13
+ o.on("-r", "Print as a Ruby hash (debug)") do
14
+ opt[:raw] = true
15
+ end
16
+ o.on('-v', '-V', 'Print the version number') do
17
+ puts (eval File.read File.join __dir__, 'package.gemspec').version
18
+ exit
19
+ end
20
+ end.parse!
21
+
22
+ io = ARGV.size > 0 ? File.open(ARGV[0]) : $stdin
23
+ torrent = Bencview::Torrent.new io
24
+
25
+ if opt[:raw]
26
+ require 'pp'
27
+ pp torrent.input
28
+ exit
29
+ end
30
+
31
+ puts opt[:json] ? torrent.to_json : torrent
data/bencview.rb ADDED
@@ -0,0 +1,152 @@
1
+ require 'date'
2
+ require 'digest'
3
+ require 'uri'
4
+ require 'json'
5
+ require "base64"
6
+
7
+ require 'bencode'
8
+
9
+ module Bencview
10
+ # walk through object, firing `block` on each string leaf
11
+ def self.hash_walk obj, &block
12
+ if obj.respond_to? :to_hash
13
+ r = {}
14
+ obj.to_hash.each do |key, value|
15
+ r[key] = hash_walk value, &block # recursion
16
+ end
17
+ r
18
+ elsif obj.respond_to? :to_ary
19
+ obj.to_ary.map { |a| hash_walk a, &block } # recursion
20
+ elsif obj.kind_of? String
21
+ yield obj
22
+ else
23
+ obj
24
+ end
25
+ end
26
+ end
27
+
28
+ class Bencview::Torrent
29
+ attr_reader :input, :infohash
30
+ def initialize io
31
+ @input = (BEncode::Parser.new io).parse!
32
+ raise 'invalid input' unless @input
33
+ @infohash = sha1
34
+ end
35
+
36
+ def any_to_s obj
37
+ obj.kind_of?(Array) ? [obj.size, obj].join("\n ").strip : obj.to_s
38
+ end
39
+
40
+ def num n
41
+ n.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
42
+ end
43
+
44
+ # BitTorrent specific
45
+ def sha1
46
+ return nil unless @input['info'].kind_of?(Hash)
47
+ Digest::SHA1.hexdigest @input['info'].bencode
48
+ end
49
+
50
+ # BitTorrent specific
51
+ def magnet name
52
+ return nil unless @infohash
53
+ "magnet:?xt=urn:btih:#{@infohash}" + (name ? "&dn=#{URI.escape name}.torrent" : "")
54
+ end
55
+
56
+ # BitTorrent specific
57
+ def bti obj, prefix
58
+ return nil unless obj.kind_of?(Hash)
59
+
60
+ rkey = -> key { "#{prefix}#{key}" }
61
+
62
+ r = []
63
+ obj.each do |key,val|
64
+ key = key.to_s.strip
65
+ if key =~ /^piece/
66
+ # TODO: calc the chunks
67
+ elsif key =~ /\.utf-8$/
68
+ # ignore
69
+ elsif key =~ /^(files|length)$/
70
+ # ignore, but see below `files_add`
71
+ elsif key == 'profiles' && val.kind_of?(Array)
72
+ val.each {|item| r.concat to_a item, "#{prefix}#{key}/" }
73
+ else
74
+ r.push "#{rkey.call key}: #{val}"
75
+ end
76
+ end
77
+
78
+ files = []
79
+ files_add = -> arr do
80
+ bytes = 0
81
+ max = 0
82
+ arr.each do |file|
83
+ bytes += file['length']
84
+ max = file['length'] if max < file['length']
85
+ end
86
+ max = (num max).size + 1
87
+
88
+ files.push "#{rkey.call 'files'}: #{arr.size}"
89
+ arr.each do |file|
90
+ files.push "%#{max}s %s" % [num(file['length']), file['path'].join('/')]
91
+ end
92
+ files.push "#{rkey.call 'files size'}: #{num bytes}"
93
+ end
94
+
95
+ if obj['files']
96
+ files_add.call obj['files']
97
+ elsif obj['length'] && obj['name']
98
+ files_add.call [{'length' => obj['length'], 'path' => [obj['name']]}]
99
+ end
100
+
101
+ r.concat files if files
102
+ end
103
+
104
+ def to_a obj=nil, prefix=''
105
+ obj = @input unless obj
106
+
107
+ r = []
108
+ info = nil
109
+
110
+ rkey = -> key { "#{prefix}#{key}" }
111
+
112
+ if @infohash && prefix == ''
113
+ r.push "infohash: #{@infohash}"
114
+ r.push "uri: #{magnet @input.dig 'info', 'name'}"
115
+ end
116
+
117
+ obj.each do |key,val|
118
+ key = key.to_s.strip
119
+ if key == 'info'
120
+ info = bti val, "#{prefix}info/"
121
+ elsif val.kind_of? Hash
122
+ r.concat to_a val, "#{prefix}#{key}/" # recursion
123
+ elsif key =~ /date/
124
+ r.push "#{rkey.call key}: #{DateTime.strptime(val.to_s, '%s').rfc2822}"
125
+ elsif key =~ /-list$/
126
+ r.push "#{rkey.call key}: #{any_to_s val}"
127
+ else
128
+ r.push "#{rkey.call key}: #{val}"
129
+ end
130
+ end
131
+
132
+ r.concat info if info
133
+ r
134
+ end
135
+
136
+ def to_s obj=nil, prefix=''
137
+ (to_a obj, prefix).join "\n"
138
+ end
139
+
140
+ # walk through object, return a hash suitable for JSON.stringify
141
+ def to_json
142
+ Bencview.hash_walk(@input) do |str|
143
+ begin
144
+ str.to_json
145
+ rescue
146
+ next Base64.strict_encode64 str
147
+ end
148
+ str
149
+ end.to_json
150
+ end
151
+
152
+ end
data/json2bencode ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative './bencview'
4
+
5
+ io = ARGV.size > 0 ? File.open(ARGV[0]) : $stdin
6
+ print Bencview.hash_walk(JSON.parse io.read) do |str|
7
+ # check if the string is in base64
8
+ if str =~ /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/
9
+ next Base64.decode64 str
10
+ else
11
+ str
12
+ end
13
+ end.bencode
data/package.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ Gem::Specification.new do |s|
2
+ s.version = '1.0.0'
3
+
4
+ s.name = 'bencview'
5
+ s.summary = "View .torrent files; JSON export/import"
6
+ s.author = 'Alexander Gromnitsky'
7
+ s.email = 'alexander.gromnitsky@gmail.com'
8
+ s.homepage = 'https://github.com/gromnitsky/bencview'
9
+ s.license = 'MIT'
10
+ s.files = [
11
+ 'bencview',
12
+ 'bencview.rb',
13
+ 'json2bencode',
14
+ 'package.gemspec',
15
+ 'README.md',
16
+ ]
17
+
18
+ s.require_paths = ['.']
19
+ s.bindir = '.'
20
+ s.executables = ['bencview', 'json2bencode']
21
+
22
+ s.add_runtime_dependency 'bencode', '~> 0.8.2'
23
+
24
+ s.required_ruby_version = '>= 2.3.0'
25
+
26
+ s.post_install_message = <<~END
27
+ *************************************************************************
28
+ If you were using bencview-0.0.x, please read
29
+ #{s.homepage},
30
+ for it's a different program now, totally incompatible w/ 0.0.x releases.
31
+ *************************************************************************
32
+ END
33
+ end
metadata CHANGED
@@ -1,102 +1,71 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: bencview
3
- version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
6
5
  platform: ruby
7
- authors:
6
+ authors:
8
7
  - Alexander Gromnitsky
9
8
  autorequire:
10
- bindir: bin
9
+ bindir: "."
11
10
  cert_chain: []
12
-
13
- date: 2011-04-29 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: open4
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.0.1
24
- type: :runtime
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
11
+ date: 2017-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
27
14
  name: bencode
28
- prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
30
- none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: 0.6.0
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.2
35
20
  type: :runtime
36
- version_requirements: *id002
37
- description: Torrent file viewer & metadata cleaner.
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.2
27
+ description:
38
28
  email: alexander.gromnitsky@gmail.com
39
- executables:
40
- - bencview_clean
29
+ executables:
41
30
  - bencview
31
+ - json2bencode
42
32
  extensions: []
43
-
44
- extra_rdoc_files:
45
- - doc/NEWS.rdoc
46
- - doc/LICENSE
47
- - doc/bencview.rdoc
48
- - doc/README.rdoc
49
- - doc/bencview_clean.rdoc
50
- files:
51
- - README.rdoc
52
- - Rakefile
53
- - bin/bencview
54
- - bin/bencview_clean
55
- - doc/LICENSE
56
- - doc/NEWS.rdoc
57
- - doc/README.rdoc
58
- - doc/bencview.rdoc
59
- - doc/bencview_clean.rdoc
60
- - etc/bencview.yaml
61
- - lib/bencview/meta.rb
62
- - lib/bencview/trestle.rb
63
- - test/helper.rb
64
- - test/helper_trestle.rb
65
- - test/rake_git.rb
66
- - test/test_bencview.rb
67
- - test/torrent/[rutracker.org].t3128973.torrent
68
- - test/torrent/[rutracker.org]2642547.torrent
69
- - test/torrent/[rutracker.org]314407.torrent
70
- - test/torrent/[rutracker.org]838460.torrent
71
- - test/torrent/stack overflow data dump - nov 2010.torrent
72
- - test/viewer_output_01.txt
73
- homepage: http://github.com/gromnitsky/bencview
74
- licenses: []
75
-
76
- post_install_message:
77
- rdoc_options:
78
- - -m
79
- - doc/README.rdoc
80
- require_paths:
81
- - lib
82
- required_ruby_version: !ruby/object:Gem::Requirement
83
- none: false
84
- requirements:
33
+ extra_rdoc_files: []
34
+ files:
35
+ - "./bencview"
36
+ - "./json2bencode"
37
+ - README.md
38
+ - bencview
39
+ - bencview.rb
40
+ - json2bencode
41
+ - package.gemspec
42
+ homepage: https://github.com/gromnitsky/bencview
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
46
+ post_install_message: |
47
+ *************************************************************************
48
+ If you were using bencview-0.0.x, please read
49
+ https://github.com/gromnitsky/bencview,
50
+ for it's a different program now, totally incompatible w/ 0.0.x releases.
51
+ *************************************************************************
52
+ rdoc_options: []
53
+ require_paths:
54
+ - "."
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
85
57
  - - ">="
86
- - !ruby/object:Gem::Version
87
- version: 1.9.2
88
- required_rubygems_version: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
58
+ - !ruby/object:Gem::Version
59
+ version: 2.3.0
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
91
62
  - - ">="
92
- - !ruby/object:Gem::Version
93
- version: "0"
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
94
65
  requirements: []
95
-
96
66
  rubyforge_project:
97
- rubygems_version: 1.8.0
67
+ rubygems_version: 2.5.2
98
68
  signing_key:
99
- specification_version: 3
100
- summary: Torrent file viewer & metadata cleaner
101
- test_files:
102
- - test/test_bencview.rb
69
+ specification_version: 4
70
+ summary: View .torrent files; JSON export/import
71
+ test_files: []
data/README.rdoc DELETED
@@ -1,9 +0,0 @@
1
- bencview suite contains several utils to manage .torrent files.
2
-
3
- bencview:: Torrent file viewer.
4
-
5
- bencview_clean:: Cleans a list of announce trackers & replaces
6
- main announce tracker (to be able to cheat,
7
- for example, http://rutracker.org, ha-ha).
8
-
9
- Read about them in corresponding <tt>*.rdoc</tt> files.
data/Rakefile DELETED
@@ -1,50 +0,0 @@
1
- # -*-ruby-*-
2
-
3
- require 'rake'
4
- require 'rake/gempackagetask'
5
- require 'rake/clean'
6
- require 'rake/rdoctask'
7
- require 'rake/testtask'
8
-
9
- require_relative 'lib/bencview/meta'
10
- include Bencview
11
-
12
- require_relative 'test/rake_git'
13
-
14
- spec = Gem::Specification.new {|i|
15
- i.name = Meta::NAME
16
- i.version = `bin/#{i.name} -V`
17
- i.summary = 'Torrent file viewer & metadata cleaner'
18
- i.description = i.summary + '.' # eat this, freaking rubygems
19
- i.author = Meta::AUTHOR
20
- i.email = Meta::EMAIL
21
- i.homepage = Meta::HOMEPAGE
22
-
23
- i.platform = Gem::Platform::RUBY
24
- i.required_ruby_version = '>= 1.9.2'
25
- i.files = git_ls('.')
26
-
27
- i.executables = FileList['bin/*'].gsub(/^bin\//, '')
28
-
29
- i.test_files = FileList['test/test_*.rb']
30
-
31
- i.rdoc_options << '-m' << 'doc/README.rdoc'
32
- i.extra_rdoc_files = FileList['doc/*']
33
-
34
- i.add_dependency('open4', '>= 1.0.1')
35
- i.add_dependency('bencode', '>= 0.6.0')
36
- }
37
-
38
- Rake::GemPackageTask.new(spec).define
39
-
40
- task default: [:repackage]
41
-
42
- Rake::RDocTask.new('doc') {|i|
43
- i.main = 'doc/README.rdoc'
44
- i.rdoc_files = FileList['doc/*', 'lib/**/*.rb']
45
- # i.rdoc_files.exclude("lib/**/some-nasty-staff")
46
- }
47
-
48
- Rake::TestTask.new {|i|
49
- i.test_files = FileList['test/test_*.rb']
50
- }
data/bin/bencview DELETED
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # -*-ruby-*-
3
-
4
- require 'bencode'
5
- require_relative '../lib/bencview/trestle.rb'
6
-
7
- include Bencview
8
-
9
- $conf = Hash.new
10
- u = Trestle.new($conf)
11
-
12
- $conf[:banner] = "Usage: #{File.basename($0)} [options] file [file ...]"
13
- $conf[:dump] = false
14
-
15
- # Return a string--human redable number, e.g. 123456 becomes '123,456'.
16
- def dn(n)
17
- return n.to_s if n.to_i/1000.0 < 1
18
- n.to_s.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
19
- end
20
-
21
- # Convert _s_ from _enc_ to the default external encoding.
22
- def ds(s, enc)
23
- s = s.to_s
24
- return s if (enc == Encoding.default_external || enc == "" || !enc)
25
- return dn(s) if s.match /^[0-9]+$/
26
- s.encode(Encoding.default_external, enc,
27
- :undef => :replace,
28
- :invalid => :replace)
29
- end
30
-
31
- # Read _s_ as a number of seconds from the Epoch and return a human
32
- # readable date string.
33
- def ddate(s)
34
- DateTime.strptime(s.to_s, '%s').rfc2822
35
- end
36
-
37
- # --[ main ]------------------------------------------------------------
38
-
39
- u.config_parse(['foobar']) {|src|
40
- o = u.cl_parse(src) # create an OptionParser object
41
- o.on('-d', 'Dump all as raw hash') {|i| $conf[:dump] = true }
42
- u.cl_parse(src, o) # run cl parser
43
- }
44
-
45
- # print our env
46
- if $conf[:verbose] >= 2
47
- puts 'Libs dir: ' + Trestle.gem_libdir
48
- pp $conf
49
- end
50
-
51
- Trestle.errx(1, $conf[:banner]) if ARGV.size == 0
52
-
53
- ec = 0
54
- visfilesep = ARGV.size > 1 ? "\n" : "" # visual file separator
55
- ARGV.each {|i|
56
- begin
57
- t = BEncode.load_file i
58
- rescue
59
- STDERR.puts "#{File.basename i}: #{$!}"
60
- ec += 1
61
- next
62
- end
63
-
64
- puts visfilesep + 'FILE: ' + File.basename(i)
65
- if $conf[:dump]
66
- pp t
67
- next
68
- end
69
- enc = t['encoding'].to_s
70
-
71
- t.each {|k,v|
72
- case k
73
- when "announce-list"
74
- puts k + (v.size == 0 ? ':' : ":\n\t" + v.join("\n\t"))
75
- when "info"
76
- v.each {|info, infoval|
77
- case info
78
- when /piece/
79
- next # a big unredable blob
80
- when "files"
81
- total = 0
82
- puts "files (#{infoval.size}): "
83
-
84
- biggest = 0
85
- infoval.each {|i|
86
- biggest = i['length'] if biggest < i['length']
87
- total += i['length']
88
- }
89
- biggest = dn(biggest)
90
-
91
- infoval.each {|i|
92
- printf(" %#{biggest.size}s %s\n",
93
- dn(i['length']), ds(i['path'].join('/'), enc))
94
- }
95
- puts 'length: ' + dn(total)
96
- else
97
- puts info + ': ' + ds(infoval, enc)
98
- end
99
- }
100
- when /date/
101
- puts k + ': ' + ddate(v)
102
- else
103
- puts k + ': ' + ds(v, enc)
104
- end
105
- }
106
- }
107
- exit ec