georgboe-ucli 0.2.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.
- data/README.md +126 -0
- data/Rakefile +48 -0
- data/bin/ucli +142 -0
- data/lib/lsfile.rb +1 -0
- data/lib/torrent.rb +110 -0
- data/lib/utilities.rb +34 -0
- metadata +77 -0
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
## ucli
|
2
|
+
|
3
|
+
Ucli is a simple commandline interface against uTorrent's WebUI.
|
4
|
+
|
5
|
+
### Requirements
|
6
|
+
* curl
|
7
|
+
* trollop >= 1.10 (gem)
|
8
|
+
* json (gem)
|
9
|
+
|
10
|
+
### Getting started
|
11
|
+
|
12
|
+
Ucli uses a yaml file to store your configuration. An examplefile will be generated at first run.
|
13
|
+
|
14
|
+
~% ucli
|
15
|
+
No config file found.
|
16
|
+
Check out ~/.ucli_config.yml.example
|
17
|
+
|
18
|
+
Now edit this configuration file with your information and rename it to `.ucli_config.yml`.
|
19
|
+
You are now ready to use the commands.
|
20
|
+
|
21
|
+
### Usage
|
22
|
+
|
23
|
+
#### List all torrents
|
24
|
+
|
25
|
+
Running the command `ucli --list` or `ucli -l` will give you a list of all the torrents in utorrent.
|
26
|
+
The output will look like this:
|
27
|
+
|
28
|
+
+-----------------------------------------------------------+----------+-------+
|
29
|
+
| Torrentname | Uploaded | Ratio |
|
30
|
+
+-----------------------------------------------------------+----------+-------+
|
31
|
+
| ubuntu-8.10-desktop-i386.iso | 1.24 GB| 1.82|
|
32
|
+
| *ubuntu-8.10-server-i386.iso* | 0.00 KB| 0.00|
|
33
|
+
+-----------------------------------------------------------+----------+-------+
|
34
|
+
|
35
|
+
The stars surrounding the last torrent means that the downloading has not completed yet.
|
36
|
+
|
37
|
+
#### Uploading torrents
|
38
|
+
|
39
|
+
You can upload torrents from your local machine to the webui the `ucli -u` command.
|
40
|
+
A label/category is required for all torrents.
|
41
|
+
|
42
|
+
ucli -u *.torrent -c "MISC"
|
43
|
+
|
44
|
+
The command above will upload all torrent files in your current working directory and
|
45
|
+
add the label "MISC" all of them.
|
46
|
+
|
47
|
+
#### Remove torrents
|
48
|
+
|
49
|
+
You can remove torrents with the `ucli -r` command.
|
50
|
+
You can specify a name or regex to match.
|
51
|
+
It will show you all the matches along with their shareratio and will not delete anything before you have confirmed it.
|
52
|
+
|
53
|
+
~% ucli -r server
|
54
|
+
Found the following torrents
|
55
|
+
============================
|
56
|
+
ubuntu-8.10-server-i386.iso (0.06)
|
57
|
+
|
58
|
+
Are you sure you want to remove these torrents? (y or n):
|
59
|
+
|
60
|
+
In this case there was only on matching torrent, but you can remove large amount of torrents using regexes.
|
61
|
+
|
62
|
+
#### Removing old tv episodes
|
63
|
+
|
64
|
+
`ucli --oldepisodes` looks for torrents of the same tv show. If you have two episode of the same show, it will suggest remove the oldest.
|
65
|
+
Let's say you have these episodes in uTorrent:
|
66
|
+
|
67
|
+
Tvshow.S01E01.HDTV-XviD-GROUP
|
68
|
+
Tvshow.S01E02.HDTV-XviD-GROUP
|
69
|
+
|
70
|
+
Another.show.S02E04.HDTV-XviD-GROUP
|
71
|
+
Another.show.S02E12.HDTV-XviD-GROUP
|
72
|
+
|
73
|
+
Now we try to run the command.
|
74
|
+
|
75
|
+
~% ucli --oldepisodes
|
76
|
+
Found the following old episodes
|
77
|
+
================================
|
78
|
+
Tvshow.S01E01.HDTV-XviD-GROUP (1.49)
|
79
|
+
Another.show.S02E04.HDTV-XviD-GROUP (1.20)
|
80
|
+
|
81
|
+
Do you want to remove them? (y or n):
|
82
|
+
|
83
|
+
#### Listing files in torrents
|
84
|
+
|
85
|
+
`ucli --ls` command will show you all the files with filesizes in the torrents matching you input.
|
86
|
+
|
87
|
+
~% ucli --ls server
|
88
|
+
ubuntu-8.10-server-i386.iso
|
89
|
+
===========================
|
90
|
+
ubuntu-8.10-server-i386.iso (637.32 MB)
|
91
|
+
|
92
|
+
|
93
|
+
### Notes
|
94
|
+
|
95
|
+
* I have not tried this script on Windows, and I don't think uploading torrents will work. This is due to the difference in the way windows passes arguments to programs as opposed to unix/linux. The other functionality shouldn't be a problem though.
|
96
|
+
|
97
|
+
* This application was made to work with uTorrent WebUI 0.361. It will not work with earlier version, but probably will work with later versions.
|
98
|
+
|
99
|
+
* You can actually use a regex for all the options that requires input to match a torrent. `ucli -r "\d\d"` will remove all torrents containing two digits.
|
100
|
+
|
101
|
+
[More information about the Web UI API](http://forum.utorrent.com/viewtopic.php?id=25661#p258858 "Web UI API on the uTorrent forum")
|
102
|
+
|
103
|
+
### LICENSE:
|
104
|
+
|
105
|
+
(The MIT License)
|
106
|
+
|
107
|
+
Copyright (c) 2009 Georg Alexander Bøe
|
108
|
+
|
109
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
110
|
+
a copy of this software and associated documentation files (the
|
111
|
+
'Software'), to deal in the Software without restriction, including
|
112
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
113
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
114
|
+
permit persons to whom the Software is furnished to do so, subject to
|
115
|
+
the following conditions:
|
116
|
+
|
117
|
+
The above copyright notice and this permission notice shall be
|
118
|
+
included in all copies or substantial portions of the Software.
|
119
|
+
|
120
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
121
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
122
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
123
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
124
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
125
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
126
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
|
7
|
+
GEM = "ucli"
|
8
|
+
GEM_VERSION = "0.2.0"
|
9
|
+
SUMMARY = "Commandline interface to uTorrent WebUI 0.361"
|
10
|
+
AUTHOR = "Georg Alexander Bøe"
|
11
|
+
EMAIL = "georg.boe@gmail.com"
|
12
|
+
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.name = GEM
|
16
|
+
s.version = GEM_VERSION
|
17
|
+
s.platform = Gem::Platform::RUBY
|
18
|
+
s.summary = SUMMARY
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
s.files = ['README.md', 'Rakefile'] + Dir["{lib,bin,spec}/**/*"]
|
21
|
+
s.bindir = 'bin'
|
22
|
+
s.executables = ['ucli']
|
23
|
+
|
24
|
+
s.author = AUTHOR
|
25
|
+
s.email = EMAIL
|
26
|
+
|
27
|
+
s.add_dependency 'json'
|
28
|
+
s.add_dependency 'trollop', '>=1.10'
|
29
|
+
|
30
|
+
s.rubyforge_project = GEM # GitHub bug, gem isn't being build when this miss
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
35
|
+
pkg.gem_spec = spec
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Install the gem locally"
|
39
|
+
task :install => [:package] do
|
40
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Create a gemspec file"
|
44
|
+
task :make_spec do
|
45
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
46
|
+
file.puts spec.to_ruby
|
47
|
+
end
|
48
|
+
end
|
data/bin/ucli
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'trollop'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
require 'lsfile.rb'
|
10
|
+
require 'torrent.rb'
|
11
|
+
require 'utilities.rb'
|
12
|
+
|
13
|
+
PROGRAM_VERSION = '0.2.0'
|
14
|
+
|
15
|
+
begin
|
16
|
+
|
17
|
+
file = YAML.load_file(File.expand_path('~/.ucli_config.yml'))
|
18
|
+
@@settings = OpenStruct.new(file)
|
19
|
+
rescue Exception => e
|
20
|
+
STDERR.puts "No config file found.\nCheck out ~/.ucli_config.yml.example"
|
21
|
+
open(File.expand_path('~/.ucli_config.yml.example'), 'w+') do |file|
|
22
|
+
file.puts "
|
23
|
+
# rename this file to '.ucli_config.yml'
|
24
|
+
host: ipadrress_or_hostname
|
25
|
+
port: 8080
|
26
|
+
username: your_username
|
27
|
+
password: your_password
|
28
|
+
".strip
|
29
|
+
end
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
opts = Trollop::options do
|
34
|
+
version "utorrent version #{PROGRAM_VERSION}"
|
35
|
+
opt :list, "List all torrents"
|
36
|
+
opt :upload, "Uploads the given torrentfiles", :type => :ios
|
37
|
+
opt :category, "Category for uploaded file", :type => :string, :default => nil
|
38
|
+
opt :oldepisodes, "Show and remove old tv episodes"
|
39
|
+
opt :remove, "Remove torrents by name", :type => :strings
|
40
|
+
opt :ls, "Show files for a given torrent", :type => :string
|
41
|
+
end
|
42
|
+
|
43
|
+
Trollop.die("No option selected") unless opts[:list] or opts[:upload] or opts[:download] or opts[:oldepisodes] or opts[:remove] or opts[:ls]
|
44
|
+
Trollop.die("Category is required for uploaded torrent") if opts[:upload] and !opts[:category]
|
45
|
+
|
46
|
+
if opts[:list]
|
47
|
+
# Textmate & DTerm tput hack
|
48
|
+
if `echo $TERM` == "dumb\n"
|
49
|
+
TORRENTNAME_WIDTH = 58
|
50
|
+
else
|
51
|
+
TORRENTNAME_WIDTH = (`tput cols`.to_i != 0 ? (`tput cols`.to_i) - 22 : 58)
|
52
|
+
end
|
53
|
+
|
54
|
+
Utilities.print_separator(TORRENTNAME_WIDTH)
|
55
|
+
puts "| #{'Torrentname'.ljust(TORRENTNAME_WIDTH)}|#{'Uploaded'.center(10)}|#{'Ratio'.center(7)}|"
|
56
|
+
Utilities.print_separator(TORRENTNAME_WIDTH)
|
57
|
+
Torrent.all.sort { |x,y| x.name <=> y.name }.each { |x| x.name = "*#{x.name}*" unless x.done == 100.0 }.each do |torrent|
|
58
|
+
puts "| #{Utilities.cut_string(torrent.name, TORRENTNAME_WIDTH).ljust(TORRENTNAME_WIDTH)}|#{torrent.uploaded.to_s.rjust(10)}|#{torrent.ratio.to_s.rjust(7)}|"
|
59
|
+
end
|
60
|
+
Utilities.print_separator(TORRENTNAME_WIDTH)
|
61
|
+
|
62
|
+
elsif opts[:upload]
|
63
|
+
|
64
|
+
opts[:upload].each do |torrent|
|
65
|
+
|
66
|
+
path = File.expand_path(torrent.path)
|
67
|
+
result = Torrent.upload_torrent(path, opts[:category])
|
68
|
+
|
69
|
+
if result
|
70
|
+
puts "#{File.basename(path)} successfully uploaded."
|
71
|
+
else
|
72
|
+
puts "Uploading torrent failed."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
delete = Utilities.agree?("Do you want to delete the .torrent files?")
|
77
|
+
if delete
|
78
|
+
opts[:upload].each do |e|
|
79
|
+
path = File.expand_path(e.path)
|
80
|
+
File.delete(path)
|
81
|
+
puts "#{File.basename(path)} deleted."
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
elsif opts[:oldepisodes]
|
86
|
+
old_episodes = Torrent.find_old_episodes
|
87
|
+
|
88
|
+
if (old_episodes.length == 0)
|
89
|
+
puts "No old episodes found."
|
90
|
+
exit 0
|
91
|
+
end
|
92
|
+
|
93
|
+
puts "Found the following old episodes"
|
94
|
+
puts "================================"
|
95
|
+
old_episodes.each { |episode| puts "#{episode.name} (#{episode.ratio})" }
|
96
|
+
puts
|
97
|
+
should_remove = Utilities.agree?("Do you want to remove them?")
|
98
|
+
|
99
|
+
if should_remove
|
100
|
+
old_episodes.each do |e|
|
101
|
+
e.remove
|
102
|
+
end
|
103
|
+
puts "Old episodes removed."
|
104
|
+
end
|
105
|
+
|
106
|
+
elsif opts[:remove]
|
107
|
+
torrents = opts[:remove].inject([]) { |sum, x| sum += Torrent.find_by_name(x) }
|
108
|
+
|
109
|
+
if torrents.length == 0
|
110
|
+
puts "No torrents mached #{opts[:remove]}"
|
111
|
+
exit 1
|
112
|
+
end
|
113
|
+
|
114
|
+
puts "Found the following torrents"
|
115
|
+
puts "============================"
|
116
|
+
torrents.each { |torrent| puts "#{torrent.name} (#{torrent.ratio})" }
|
117
|
+
puts
|
118
|
+
should_remove = Utilities.agree?("Are you sure you want to remove these torrents?")
|
119
|
+
|
120
|
+
if should_remove
|
121
|
+
torrents.each do |torrent|
|
122
|
+
torrent.remove
|
123
|
+
puts "#{torrent.name} removed."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
elsif opts[:ls]
|
128
|
+
torrents = Torrent.find_by_name(opts[:ls])
|
129
|
+
|
130
|
+
if torrents.length == 0
|
131
|
+
puts "No torrents mached #{opts[:ls]}"
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
|
135
|
+
torrents.each_with_index do |torrent, index|
|
136
|
+
puts if index != 0
|
137
|
+
puts torrent.name
|
138
|
+
puts "=" * torrent.name.length
|
139
|
+
torrent.list_files.each { |file| puts "#{file.name} (#{file.size})" }
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
data/lib/lsfile.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
class LsFile < Struct.new(:name, :size); end
|
data/lib/torrent.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
class Torrent
|
2
|
+
require 'lsfile.rb'
|
3
|
+
|
4
|
+
attr_accessor :hash, :name, :uploaded, :ratio, :label, :done, :basename
|
5
|
+
def initialize(hash, name, uploaded, ratio, label, done)
|
6
|
+
@hash = hash
|
7
|
+
@name = name
|
8
|
+
@uploaded = uploaded
|
9
|
+
@ratio = ratio
|
10
|
+
@done = done
|
11
|
+
@label = label
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_category(category)
|
15
|
+
open("http://#{@@settings.host}:#{@@settings.port}/gui/?action=setprops&s=label&hash=#{@hash}&v=#{category}", :http_basic_authentication => [@@settings.username, @@settings.password])
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove
|
19
|
+
template = "http://#{@@settings.host}:#{@@settings.port}/gui/?action=remove&hash=%s"
|
20
|
+
open(template % @hash, :http_basic_authentication => [@@settings.username, @@settings.password]) {|f| f.read }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
json = JSON.parse(open("http://#{@@settings.host}:#{@@settings.port}/gui/?list=1", :http_basic_authentication => [@@settings.username, @@settings.password]).read)
|
25
|
+
torrents = Array.new
|
26
|
+
|
27
|
+
json['torrents'].each do |item|
|
28
|
+
torrents << Torrent.new(item[0], item[2], format_size(item[6].to_i), format_ratio(item[7]), item[11], (item[4].to_f / 10.00))
|
29
|
+
end
|
30
|
+
torrents
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.upload_torrent(file, category=nil)
|
34
|
+
result = system("curl --basic --user #{@@settings.username}:#{@@settings.password} -F \"torrent_file=@#{file};type=application/x-bittorrent\" 'http://#{@@settings.host}:#{@@settings.port}/gui/?action=add-file' &> /dev/null")
|
35
|
+
|
36
|
+
if result and category
|
37
|
+
Torrent.all.last.set_category(category)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.find_old_episodes
|
42
|
+
|
43
|
+
episodes = all.reject {|x| !x.name.match(/(S\d+(\.|)E\d+)|(\d+x\d+)/)}
|
44
|
+
old_episodes = Array.new
|
45
|
+
|
46
|
+
episodes.each { |e| e.basename = e.name.gsub(/(.+)(\.S\d+(\.|)E\d+|\d+x\d+).+/, '\1')}.sort! {|x,y| x.name <=> y.name }
|
47
|
+
|
48
|
+
duplicates = Hash.new
|
49
|
+
|
50
|
+
episodes.each { |e|
|
51
|
+
if duplicates.has_key?(e.basename)
|
52
|
+
duplicates[e.basename] += 1
|
53
|
+
else
|
54
|
+
duplicates[e.basename] = 1
|
55
|
+
end
|
56
|
+
}
|
57
|
+
|
58
|
+
duplicates.reject { |k,v| v == 1 }
|
59
|
+
|
60
|
+
episodes.each_with_index do |episode, i|
|
61
|
+
|
62
|
+
if !duplicates.has_key?(episode.basename)
|
63
|
+
next
|
64
|
+
end
|
65
|
+
|
66
|
+
next if i == episodes.length-1
|
67
|
+
|
68
|
+
old_episodes << episode unless episodes[i+1].basename != episode.basename
|
69
|
+
end
|
70
|
+
|
71
|
+
old_episodes
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.find_by_name(name)
|
75
|
+
torrents = Array.new
|
76
|
+
all.each { |torrent| torrents << torrent if torrent.name =~ Regexp.new(name, true)}
|
77
|
+
torrents
|
78
|
+
end
|
79
|
+
|
80
|
+
def list_files
|
81
|
+
json = JSON.parse(open("http://#{@@settings.host}:#{@@settings.port}/gui/?action=getfiles&hash=#{@hash}", :http_basic_authentication => [@@settings.username, @@settings.password]).read)
|
82
|
+
files = Array.new
|
83
|
+
json['files'][1].each { |file| files << LsFile.new(file[0], Torrent.format_size(file[1]))}
|
84
|
+
files
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def self.format_size(bytes)
|
90
|
+
|
91
|
+
bytes = bytes.to_f
|
92
|
+
|
93
|
+
if bytes > 1024**3
|
94
|
+
return "%1.2f GB" % (bytes / 1024**3)
|
95
|
+
elsif bytes > 1024**2
|
96
|
+
return "%1.2f MB" % (bytes / 1024**2)
|
97
|
+
else
|
98
|
+
return "%1.2f KB" % (bytes / 1024)
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.format_ratio(ratio)
|
105
|
+
return "0.00" if ratio.to_s.length == 1
|
106
|
+
|
107
|
+
"%0.2f" % (ratio.to_f / 1000.00)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/lib/utilities.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class Utilities
|
2
|
+
|
3
|
+
def self.cut_string(string, length=58)
|
4
|
+
if string.length > length-1
|
5
|
+
string = string[0,length-4] + '...'
|
6
|
+
end
|
7
|
+
string
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.print_separator(length=58)
|
11
|
+
puts "+" + ("-" * length) + "-+----------+-------+"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.agree?(message)
|
15
|
+
valid = false
|
16
|
+
answer = false
|
17
|
+
|
18
|
+
while (!valid) do
|
19
|
+
|
20
|
+
print "#{message} (y or n): "
|
21
|
+
line = gets.strip
|
22
|
+
|
23
|
+
if (line.downcase == 'y')
|
24
|
+
valid = true
|
25
|
+
answer = true
|
26
|
+
elsif (line.downcase == 'n')
|
27
|
+
valid = true
|
28
|
+
answer = false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
answer
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: georgboe-ucli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Georg Alexander B\xC3\xB8e"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-14 00:00:00 -07:00
|
13
|
+
default_executable: ucli
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: trollop
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "1.10"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: georg.boe@gmail.com
|
37
|
+
executables:
|
38
|
+
- ucli
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- README.md
|
45
|
+
- Rakefile
|
46
|
+
- lib/lsfile.rb
|
47
|
+
- lib/torrent.rb
|
48
|
+
- lib/utilities.rb
|
49
|
+
- bin/ucli
|
50
|
+
has_rdoc: false
|
51
|
+
homepage:
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project: ucli
|
72
|
+
rubygems_version: 1.2.0
|
73
|
+
signing_key:
|
74
|
+
specification_version: 2
|
75
|
+
summary: Commandline interface to uTorrent WebUI 0.361
|
76
|
+
test_files: []
|
77
|
+
|