nanny 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 922e91e738bb2e20f7dd0e2b775c2c5dabc2d4b9
4
+ data.tar.gz: 5f044c48c7107dc31639b6a4ed744d75b935b2f2
5
+ SHA512:
6
+ metadata.gz: d8afcb6ea3007e32a0813b20de87215029ccab54011e11398f5f1c2ee63119d7febcaa7f0351d1e5d4d2468d7de455d60dc0a3e7b9b819b22b7a9b4d41a371d0
7
+ data.tar.gz: 05ac6fb0577555586e8eeb433b8f73daa6c439d389e249d8528af594f8863f5f973bf2976e8b9ae5f2a7b55606bc0f2e9929559c20e956188e115cd1d283d031
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - "1.9.3"
5
+
6
+ script: "bundle exec rspec spec"
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in torrent.gemspec
4
+ gemspec
5
+
6
+ gem 'vcr'
7
+ gem 'rspec'
8
+ gem 'webmock'
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nanny (0.0.1)
5
+ active_support
6
+ addressable
7
+ colored
8
+ highline
9
+ nokogiri
10
+ rest-client
11
+ ruby-progressbar
12
+ terminal-table
13
+ trollop
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ active_support (3.0.0)
19
+ activesupport (= 3.0.0)
20
+ activesupport (3.0.0)
21
+ addressable (2.3.2)
22
+ colored (1.2)
23
+ crack (0.3.2)
24
+ diff-lcs (1.1.3)
25
+ highline (1.6.15)
26
+ mime-types (1.20.1)
27
+ nokogiri (1.5.6)
28
+ rake (10.0.4)
29
+ rest-client (1.6.7)
30
+ mime-types (>= 1.16)
31
+ rspec (2.12.0)
32
+ rspec-core (~> 2.12.0)
33
+ rspec-expectations (~> 2.12.0)
34
+ rspec-mocks (~> 2.12.0)
35
+ rspec-core (2.12.2)
36
+ rspec-expectations (2.12.1)
37
+ diff-lcs (~> 1.1.3)
38
+ rspec-mocks (2.12.2)
39
+ ruby-progressbar (1.0.2)
40
+ terminal-table (1.4.5)
41
+ trollop (2.0)
42
+ vcr (2.4.0)
43
+ webmock (1.9.0)
44
+ addressable (>= 2.2.7)
45
+ crack (>= 0.1.7)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ nanny!
52
+ rake
53
+ rspec
54
+ vcr
55
+ webmock
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Stefano Verna
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # Nanny [![Build Status](https://travis-ci.org/stefanoverna/nanny.png?branch=master)](https://travis-ci.org/stefanoverna/nanny)
2
+
3
+ Nanny is a library that uses multiple torrent search engine to find a valid direct
4
+ link to a Torrent. It currently supports Torrentz.eu, Torcache.net and Torrage.com
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'nanny'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install nanny
19
+
20
+ ## Usage
21
+
22
+ You can use `nanny` programmatically:
23
+
24
+ ```ruby
25
+ require 'nanny'
26
+
27
+ torrents = Nanny::Search.new.search_torrents("Ubuntu")
28
+
29
+ torrent.first
30
+ # => <Nanny::Torrent @title="Ubuntu 12 10 Desktop i386", @url="http://torrentz.eu/335990d615594b9be409ccfeb95864e24ec702c7", @seeds=2158, @peers=32, @size=789577728, @hash="335990d615594b9be409ccfeb95864e24ec702c7">
31
+
32
+ torrent.first.torrent_url
33
+ # => "http://torcache.net/torrent/335990D615594B9BE409CCFEB95864E24EC702C7.torrent"
34
+ ```
35
+
36
+ or as a CLI tool:
37
+
38
+ ![cli usage](https://raw.github.com/stefanoverna/nanny/master/doc/cli.png)
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'nanny'
3
+ cli = Nanny::CLI.new
4
+ cli.run!
5
+
Binary file
@@ -0,0 +1,13 @@
1
+ module Nanny
2
+ autoload :AttrInitializer, 'nanny/attr_initializer'
3
+ autoload :Search, 'nanny/search'
4
+ autoload :Torrent, 'nanny/torrent'
5
+ autoload :Torcache, 'nanny/torcache'
6
+ autoload :Tracker, 'nanny/tracker'
7
+ autoload :MagnetURI, 'nanny/magnet_uri'
8
+ autoload :VERSION, 'nanny/version'
9
+ autoload :CLI, 'nanny/cli'
10
+ autoload :HTTPClient, 'nanny/http_client'
11
+ autoload :Progress, 'nanny/progress'
12
+ end
13
+
@@ -0,0 +1,13 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/core_ext/hash/keys'
3
+
4
+ module Nanny
5
+ module AttrInitializer
6
+ def initialize(opts = {})
7
+ opts.each do |opt, val|
8
+ instance_variable_set("@#{opt}", val) if respond_to? opt
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,90 @@
1
+ require 'active_support/core_ext/string/strip'
2
+ require 'trollop'
3
+ require 'terminal-table'
4
+ require 'colored'
5
+ require 'highline'
6
+ require 'ruby-progressbar'
7
+
8
+ module Nanny
9
+ class CLI
10
+
11
+ def run!
12
+ options = process_options!
13
+
14
+ if query.size.zero?
15
+ Trollop::die "A query is required"
16
+ end
17
+
18
+ torrents = Nanny::Search.new.search_torrents(query)
19
+ results_range = 0 .. options[:limit] - 1
20
+
21
+ puts formatted_torrents(torrents[results_range])
22
+ puts
23
+ torrent_index = highline.ask("Choose a torrent to download", Integer) do |q|
24
+ q.default = 0
25
+ q.in = results_range
26
+ end
27
+ torrent = torrents[torrent_index]
28
+
29
+ puts
30
+
31
+ bar = ProgressBar.create(title: "Searching")
32
+ progress = Progress.new do |p|
33
+ bar.progress = 100.0 * p.total_done / p.total_todo
34
+ end
35
+
36
+ highline.say torrent.torrent_url(progress)
37
+ end
38
+
39
+ def formatted_torrents(torrents)
40
+ rows = torrents.each_with_index.map do |torrent, i|
41
+ [
42
+ i,
43
+ truncate_string(torrent.title),
44
+ torrent.human_size,
45
+ torrent.seeds.to_s.red,
46
+ torrent.peers.to_s.green
47
+ ]
48
+ end
49
+ rows = [
50
+ [ "#", "Title", "Size", "Seeds", "Peers" ],
51
+ :separator
52
+ ] + rows
53
+ Terminal::Table.new(rows: rows).to_s
54
+ end
55
+
56
+ def process_options!
57
+ Trollop::options do
58
+ banner <<-RAW.strip_heredoc
59
+
60
+ nanny is your friendly and lovely torrent finder
61
+
62
+ Usage:
63
+ nanny [options] <query>
64
+
65
+ Options:
66
+ RAW
67
+ version "Nanny #{Nanny::VERSION} (c) Stefano Verna"
68
+ opt :limit, "The number of results", default: 5
69
+ end
70
+ end
71
+
72
+ def query
73
+ ARGV.join(" ")
74
+ end
75
+
76
+ def truncate_string(text)
77
+ if text.size > 50
78
+ text[0..50] + "..."
79
+ else
80
+ text
81
+ end
82
+ end
83
+
84
+ def highline
85
+ @highline ||= HighLine.new
86
+ end
87
+
88
+ end
89
+ end
90
+
@@ -0,0 +1,26 @@
1
+ require 'rest-client'
2
+
3
+ module Nanny
4
+ class HTTPClient
5
+
6
+ class Exception < RuntimeError; end
7
+
8
+ def self.headers(url)
9
+ RestClient.head(url).headers
10
+ rescue URI::InvalidURIError
11
+ rescue SocketError
12
+ rescue RestClient::Exception
13
+ raise Exception
14
+ end
15
+
16
+ def self.get(url)
17
+ RestClient.get(url).body.force_encoding("utf-8")
18
+ rescue URI::InvalidURIError
19
+ rescue SocketError
20
+ rescue RestClient::Exception
21
+ raise Exception
22
+ end
23
+
24
+ end
25
+ end
26
+
@@ -0,0 +1,15 @@
1
+ module Nanny
2
+ class MagnetURI
3
+ attr_reader :uri
4
+
5
+ def initialize(uri)
6
+ @uri = uri
7
+ end
8
+
9
+ def hash
10
+ uri.match(/xt=urn:btih:(?<hash>[a-z0-9]+)/i)["hash"]
11
+ end
12
+
13
+ end
14
+ end
15
+
@@ -0,0 +1,65 @@
1
+ require 'active_support/core_ext/enumerable'
2
+
3
+ module Nanny
4
+ class Progress
5
+
6
+ attr_reader :childs
7
+ attr_reader :my_todo
8
+ attr_reader :my_done
9
+ attr_reader :parent
10
+
11
+ def initialize(parent = nil, &block)
12
+ @listener = block
13
+ @parent = parent
14
+ @my_todo = 0
15
+ @my_done = 0
16
+ @childs = []
17
+ end
18
+
19
+ def step
20
+ todo(1)
21
+ result = yield
22
+ done!(1)
23
+ result
24
+ end
25
+
26
+ def todo(n)
27
+ @my_todo += n
28
+ notify!
29
+ end
30
+
31
+ def done!(n)
32
+ @my_done += n
33
+ notify!
34
+ end
35
+
36
+ def complete!
37
+ if @my_done != my_todo
38
+ @my_done = my_todo
39
+ childs.each(&:complete!)
40
+ notify!
41
+ end
42
+ end
43
+
44
+ def child
45
+ Progress.new(self).tap do |child|
46
+ @childs << child
47
+ end
48
+ end
49
+
50
+ def total_todo
51
+ my_todo + childs.sum(&:total_todo)
52
+ end
53
+
54
+ def total_done
55
+ my_done + childs.sum(&:total_done)
56
+ end
57
+
58
+ def notify!
59
+ parent.notify! if parent
60
+ @listener.call(self) if @listener
61
+ end
62
+
63
+ end
64
+ end
65
+
@@ -0,0 +1,49 @@
1
+ require 'nokogiri'
2
+ require 'addressable/template'
3
+
4
+ module Nanny
5
+ class Search
6
+
7
+ SIZE_UNITS = %w(b kb mb gb tb)
8
+ FEED_ITEMS_XPATH = '//rss/channel/item'
9
+ FEED_TEMPLATE_URL = Addressable::Template.new("http://torrentz.eu/feed?q={query}")
10
+ FEED_DESC_REGEXP = /
11
+ Size: \s* (?<size>\d+) \s* (?<size_unit>[KMGT]?B)
12
+ .*
13
+ Seeds: \s* (?<seeds>[\d,]+)
14
+ \s+
15
+ Peers: \s* (?<peers>[\d,]+)
16
+ \s+
17
+ Hash: \s* (?<hash>[a-f0-9]+)
18
+ /xi
19
+
20
+ def search_torrents(query)
21
+ doc = document_for_query(query)
22
+ doc.xpath(FEED_ITEMS_XPATH).map do |item|
23
+ torrent_from_item(item)
24
+ end
25
+ end
26
+
27
+ def torrent_from_item(item)
28
+ desc = item.at_xpath('.//description').text
29
+ matches = desc.match(FEED_DESC_REGEXP)
30
+ exp = SIZE_UNITS.index(matches['size_unit'].downcase)
31
+ size = matches['size'].to_i * (1024 ** exp)
32
+ Torrent.new(
33
+ title: item.at_xpath('.//title').text,
34
+ url: item.at_xpath('.//link').text,
35
+ seeds: matches['seeds'].gsub(",", "").to_i,
36
+ peers: matches['peers'].gsub(",", "").to_i,
37
+ size: size,
38
+ hash: matches['hash']
39
+ )
40
+ end
41
+
42
+ def document_for_query(query)
43
+ url = FEED_TEMPLATE_URL.expand(query: query).to_s
44
+ Nokogiri::XML HTTPClient.get(url)
45
+ end
46
+
47
+ end
48
+ end
49
+
@@ -0,0 +1,24 @@
1
+ require 'addressable/template'
2
+
3
+ module Nanny
4
+ class Torcache
5
+ TORCACHE_URL = Addressable::Template.new("http://torcache.net/torrent/{hash}.torrent")
6
+ TORRAGE_URL = Addressable::Template.new("http://torrage.com/torrent/{hash}.torrent")
7
+
8
+ class HashNotFound < RuntimeError; end
9
+
10
+ def self.url_for(hash)
11
+ [ TORCACHE_URL, TORRAGE_URL ].each do |template|
12
+ begin
13
+ url = template.expand(hash: hash.upcase).to_s
14
+ HTTPClient.headers(url)
15
+ return url
16
+ rescue HTTPClient::Exception
17
+ end
18
+ end
19
+ raise Torcache::HashNotFound
20
+ end
21
+
22
+ end
23
+ end
24
+