torrent_api 0.0.9
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/CHANGELOG +4 -0
- data/Manifest +15 -0
- data/README.markdown +63 -0
- data/Rakefile +18 -0
- data/lib/demonoid/base.rb +80 -0
- data/lib/demonoid/result.rb +46 -0
- data/lib/demonoid/result_set.rb +18 -0
- data/lib/pirate_bay/base.rb +78 -0
- data/lib/pirate_bay/categories.rb +48 -0
- data/lib/pirate_bay/result.rb +62 -0
- data/lib/pirate_bay/result_set.rb +18 -0
- data/lib/torrent_api.rb +83 -0
- data/lib/torrent_reactor/base.rb +85 -0
- data/lib/torrent_reactor/result.rb +52 -0
- data/lib/torrent_reactor/result_set.rb +18 -0
- data/torrent_api.gemspec +36 -0
- metadata +122 -0
data/CHANGELOG
ADDED
data/Manifest
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
Manifest
|
3
|
+
README.markdown
|
4
|
+
Rakefile
|
5
|
+
lib/demonoid/base.rb
|
6
|
+
lib/demonoid/result.rb
|
7
|
+
lib/demonoid/result_set.rb
|
8
|
+
lib/pirate_bay/base.rb
|
9
|
+
lib/pirate_bay/categories.rb
|
10
|
+
lib/pirate_bay/result.rb
|
11
|
+
lib/pirate_bay/result_set.rb
|
12
|
+
lib/torrent_api.rb
|
13
|
+
lib/torrent_reactor/base.rb
|
14
|
+
lib/torrent_reactor/result.rb
|
15
|
+
lib/torrent_reactor/result_set.rb
|
data/README.markdown
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
The Torrent API
|
2
|
+
===============
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
### What it does
|
7
|
+
|
8
|
+
The Torrent API brings torrent downloading to the ruby language. It allows for searching numerous torrent websites using an easy-to-use ruby DSL.
|
9
|
+
|
10
|
+
### How it does it
|
11
|
+
|
12
|
+
Using Nokogiri or Hpricot, it takes a search term (and sometimes a category) and grabs the page and parses through the HTML to scrape the results, leaving you with just the most important information.
|
13
|
+
|
14
|
+
### Usage
|
15
|
+
|
16
|
+
#### Search the default service (the pirate bay)
|
17
|
+
|
18
|
+
t = TorrentApi.new
|
19
|
+
t.search_term = 'james and the giant peach'
|
20
|
+
results = t.search
|
21
|
+
|
22
|
+
#### Query all torrent websites and work with those torrents
|
23
|
+
|
24
|
+
torrents = TorrentApi.new(:all, 'the royal tenenbaums') # buggy as of 0.0.9
|
25
|
+
torrent = torrents.first
|
26
|
+
torrent.name
|
27
|
+
=> "The.Royal.Tenenbaums.XviD.DVD-Rip"
|
28
|
+
torrent.seeds
|
29
|
+
=> 128
|
30
|
+
torrent.size # size is returned in bytes
|
31
|
+
=> 702840000.0
|
32
|
+
|
33
|
+
#### Torrent methods
|
34
|
+
|
35
|
+
:name, :seeds, :leeches, :category, :link, :magnet_link, :status, :size
|
36
|
+
|
37
|
+
#### Select a service to use and search
|
38
|
+
|
39
|
+
t = TorrentApi.new
|
40
|
+
t.service = :demonoid
|
41
|
+
t.search_term = 'james and the giant peach'
|
42
|
+
results = t.search
|
43
|
+
|
44
|
+
#### Shorthand query all
|
45
|
+
|
46
|
+
TorrentApi.new(:all, "james and the giant peach")`
|
47
|
+
|
48
|
+
### Available services
|
49
|
+
|
50
|
+
* TorrentReactor
|
51
|
+
* The Piratey Bay
|
52
|
+
* Demonoid (still buggy)
|
53
|
+
|
54
|
+
### How to help
|
55
|
+
|
56
|
+
Add more torrent sites!
|
57
|
+
|
58
|
+
### To do
|
59
|
+
* Need to fix demonoid. Still buggy with newer movies.
|
60
|
+
* Refactor all three search engines to use a shared torrent model
|
61
|
+
* More search engines!
|
62
|
+
* Need to add/remove dependencies - awesome_print and nokogiri
|
63
|
+
* Improve the ability to select a category (I think it defaults to a movie category right now)
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'echoe'
|
2
|
+
Echoe.new('torrent_api', '0.0.9
|
3
|
+
|
4
|
+
|
5
|
+
') do |p|
|
6
|
+
p.description = "An API to query popular torrent websites"
|
7
|
+
p.url = "http://www.github.com/hjhart/torrent_api"
|
8
|
+
p.author = "James Hart"
|
9
|
+
p.email = "hjhart@gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/**/*", "scripts/*"]
|
11
|
+
p.development_dependencies = ['nokogiri', 'hpricot']
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :console
|
15
|
+
|
16
|
+
task :console do
|
17
|
+
sh "irb -rubygems -r ./lib/torrent_api.rb"
|
18
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Demonoid
|
2
|
+
class Search
|
3
|
+
attr_accessor :search_string, :category_id, :page, :caching, :results
|
4
|
+
|
5
|
+
def initialize(search_string, category=0)
|
6
|
+
self.search_string = URI.encode(search_string)
|
7
|
+
self.page = -1
|
8
|
+
|
9
|
+
@results = Demonoid::ResultSet.new(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_search_results
|
13
|
+
if caching && File.exists?(cached_filename)
|
14
|
+
content = File.read(cached_filename)
|
15
|
+
else
|
16
|
+
content = fetch_search_results
|
17
|
+
|
18
|
+
FileUtils.mkdir_p("tmp/searches")
|
19
|
+
File.open(cached_filename, "w") do |f|
|
20
|
+
f.write(content)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
content
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute
|
27
|
+
return nil if search_string.nil?
|
28
|
+
self.page += 1
|
29
|
+
|
30
|
+
if (@results.size < @results.total_results)
|
31
|
+
doc = get_search_results
|
32
|
+
end
|
33
|
+
|
34
|
+
next_page(doc)
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def cached_filename
|
39
|
+
File.join("tmp", "searches", "#{search_string}_#{category_id}_#{page}.html")
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch_search_results
|
43
|
+
Demonoid
|
44
|
+
searchurl = "http://www.demonoid.me/files/?category=1&subcategory=All&language=0&quality=All&seeded=0&external=2&query=#{search_string}&uid=0&sort=S"
|
45
|
+
|
46
|
+
Hpricot(URI.parse(searchurl).read)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def next_page(doc)
|
53
|
+
if @results.total_results = 1.0/0
|
54
|
+
# TODO: Figure out the total results
|
55
|
+
end
|
56
|
+
|
57
|
+
demonoid_row = []
|
58
|
+
demonoid_table = doc.search('.ctable_content_no_pad table tr')
|
59
|
+
demonoid_table = demonoid_table.select { |row| !(row.search('iframe').count == 1 || row.inner_html =~ /Sponsored links/)}
|
60
|
+
demonoid_table.each_with_index do |row, index|
|
61
|
+
next if (0..3).include? index # skip the first few rows. It's mostly junk.
|
62
|
+
break if index == 154 # end of table
|
63
|
+
|
64
|
+
demonoid_row << row
|
65
|
+
if index % 3 == 0 # this is the majority of the info
|
66
|
+
result = Demonoid::Result.new(demonoid_row)
|
67
|
+
demonoid_row = []
|
68
|
+
@results << result
|
69
|
+
elsif index % 3 == 1 # this contains the date of birth
|
70
|
+
elsif index % 3 == 2 #this contains the title and the cateogyr
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@results
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Demonoid
|
2
|
+
class Result
|
3
|
+
attr_accessor :name, :seeds, :leeches, :category, :link, :magnet_link, :status, :size, :date_created, :detail_link
|
4
|
+
|
5
|
+
def initialize(row = [])
|
6
|
+
date_string = row[0].search('.added_today').inner_html.match(/Added on (.*)/)
|
7
|
+
self.date_created = Time.parse(date_string[1]) if date_string
|
8
|
+
self.name = row[1].search('td')[1].search('a').inner_html
|
9
|
+
self.detail_link = row[1].search('td')[1].search('a').first.get_attribute('href')
|
10
|
+
self.link = row[2].search('td')[2].search('a').first.get_attribute('href')
|
11
|
+
self.magnet_link = row[2].search('td')[2].search('a')[1].get_attribute('href')
|
12
|
+
self.seeds = row[2].search('td')[6].search('font').inner_html.to_i
|
13
|
+
self.leeches = row[2].search('td')[7].search('font').inner_html.to_i
|
14
|
+
|
15
|
+
raw_filesize = row[2].search('td')[3].inner_html
|
16
|
+
self.size = Demonoid::Result.filesize_in_bytes(raw_filesize)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"<Demonoid::Result @name => #{name}, @seeds => #{seeds}, @size => #{size}>"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.filesize_in_bytes(filesize)
|
24
|
+
match = filesize.match(/([\d\.]+) (.*)/)
|
25
|
+
|
26
|
+
if match
|
27
|
+
raw_size = match[1].to_f
|
28
|
+
|
29
|
+
case match[2].strip
|
30
|
+
when /gb/i then
|
31
|
+
raw_size * 1000000000
|
32
|
+
when /mb/i then
|
33
|
+
raw_size * 1000000
|
34
|
+
when /kb/i then
|
35
|
+
raw_size * 1000
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Demonoid
|
2
|
+
class ResultSet < Array
|
3
|
+
attr_accessor :total_results, :search
|
4
|
+
|
5
|
+
def initialize(initializer)
|
6
|
+
self.search = initializer
|
7
|
+
self.total_results = 1.0/0 #Infinity
|
8
|
+
end
|
9
|
+
|
10
|
+
def retrieved_results
|
11
|
+
self.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def more
|
15
|
+
search.next_page
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module PirateBay
|
2
|
+
class Search
|
3
|
+
attr_accessor :search_string, :category_id, :page, :caching, :results
|
4
|
+
|
5
|
+
def initialize(search_string, category='movies')
|
6
|
+
self.search_string = URI.encode(search_string)
|
7
|
+
self.category_id = PirateBay::Categories::IDS[category.upcase.strip.gsub(/S$/, "").to_sym] unless category == 0
|
8
|
+
self.page = -1
|
9
|
+
|
10
|
+
@results = PirateBay::ResultSet.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_search_results
|
14
|
+
if caching && File.exists?(cached_filename)
|
15
|
+
content = File.read(cached_filename)
|
16
|
+
else
|
17
|
+
content = fetch_search_results
|
18
|
+
|
19
|
+
FileUtils.mkdir_p("tmp/searches")
|
20
|
+
File.open(cached_filename, "w") do |f|
|
21
|
+
f.write(content)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
content
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute
|
28
|
+
return nil if search_string.nil?
|
29
|
+
self.page += 1
|
30
|
+
|
31
|
+
if (@results.size < @results.total_results)
|
32
|
+
doc = Nokogiri::HTML(get_search_results)
|
33
|
+
end
|
34
|
+
|
35
|
+
next_page(doc)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def cached_filename
|
40
|
+
File.join("tmp", "searches", "#{search_string}_#{category_id}_#{page}.html")
|
41
|
+
end
|
42
|
+
|
43
|
+
def fetch_search_results
|
44
|
+
url = "http://thepiratebay.org/search/#{search_string}/#{page}/7/#{category_id}" # highest seeded first
|
45
|
+
|
46
|
+
uri = URI.parse(url)
|
47
|
+
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
response = http.request(Net::HTTP::Get.new(uri.request_uri))
|
50
|
+
|
51
|
+
response.body
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def next_page(doc)
|
57
|
+
if @results.total_results.nil?
|
58
|
+
matching_results = doc.css("h2").first.content.match(/Displaying hits from (.*) to (.*) \(approx (.*) found\)/i)
|
59
|
+
|
60
|
+
if (matching_results.nil?)
|
61
|
+
@results.total_results = 0
|
62
|
+
else
|
63
|
+
@results.total_results = matching_results[3].to_i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
doc.css("#searchResult tr").each_with_index do |row, index|
|
68
|
+
next if index == 0
|
69
|
+
result = PirateBay::Result.new(row)
|
70
|
+
@results << result
|
71
|
+
end
|
72
|
+
@results
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PirateBay
|
2
|
+
class Categories
|
3
|
+
EXTENDED_IDS = {
|
4
|
+
:MUSIC => 101,
|
5
|
+
:AUDIO_BOOKS => 102,
|
6
|
+
:SOUND_CLIPS => 103,
|
7
|
+
:FLAC => 104,
|
8
|
+
:MOVIES => 201,
|
9
|
+
:MOVIES_DVDR => 202,
|
10
|
+
:MUSIC_VIDEOS => 203,
|
11
|
+
:MOVIE_CLIPS => 204,
|
12
|
+
:TV_SHOWS => 205,
|
13
|
+
:HANDHELD => 206,
|
14
|
+
:HIGHRES_MOVIES => 207,
|
15
|
+
:HIGHRES_TV_SHOWS => 208,
|
16
|
+
:WINDOWS => 301,
|
17
|
+
:MAC => 302,
|
18
|
+
:UNIX => 303,
|
19
|
+
:HANDHELD => 304,
|
20
|
+
:OTHER_OS => 399,
|
21
|
+
:PC => 401,
|
22
|
+
:MAC => 402,
|
23
|
+
:PS2 => 403,
|
24
|
+
:XBOX360 => 404,
|
25
|
+
:WII => 405,
|
26
|
+
:HANDHELD => 406,
|
27
|
+
:OTHER => 499,
|
28
|
+
:MOVIES => 501,
|
29
|
+
:MOVIES_DVDR => 502,
|
30
|
+
:PICTURES => 503,
|
31
|
+
:GAMES => 504,
|
32
|
+
:HIGHRES_MOVIES => 505,
|
33
|
+
:MOVIE_CLIPS => 506,
|
34
|
+
:E_BOOKS => 601,
|
35
|
+
:COMICS => 602,
|
36
|
+
:PICTURES => 603,
|
37
|
+
:COVERS => 604
|
38
|
+
}
|
39
|
+
|
40
|
+
IDS = {
|
41
|
+
:APPLICATION => 100,
|
42
|
+
:MOVIE => 200,
|
43
|
+
:AUDIO => 300,
|
44
|
+
:GAMES => 400,
|
45
|
+
:OTHER => 600
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module PirateBay
|
2
|
+
class Result
|
3
|
+
attr_accessor :name, :seeds, :leeches, :category, :link, :magnet_link, :status, :size
|
4
|
+
|
5
|
+
def initialize(row = nil)
|
6
|
+
if row.css("td")[1].css("img[alt='Trusted']").size > 0
|
7
|
+
status = "Trusted"
|
8
|
+
elsif row.css("td")[1].css("img[alt='VIP']").size > 0
|
9
|
+
status = "VIP"
|
10
|
+
else
|
11
|
+
status = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
magnet_links = row.css("td")[1].css("a[title='Download this torrent using magnet']")
|
15
|
+
if magnet_links.size > 0
|
16
|
+
magnet_link = magnet_links.first[:href]
|
17
|
+
else
|
18
|
+
magnet_link = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
raw_filesize = row.css(".detDesc").first.content.match(/Size (.*[G|M|K]iB)/i)[1]
|
22
|
+
filesize_in_bytes = PirateBay::Result.filesize_in_bytes(raw_filesize)
|
23
|
+
|
24
|
+
self.name = row.css(".detName").first.content
|
25
|
+
self.seeds = row.css("td")[2].content.to_i
|
26
|
+
self.leeches = row.css("td")[3].content.to_i
|
27
|
+
self.category = row.css("td")[0].css("a").map(&:content).join(" > ")
|
28
|
+
self.link = row.css("td")[1].css("a[title='Download this torrent']").first[:href]
|
29
|
+
self.magnet_link = magnet_link
|
30
|
+
self.status = status
|
31
|
+
self.size = filesize_in_bytes
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"<PirateBay::Result @name => #{name}, @seeds => #{seeds}, @size => #{size}>"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.filesize_in_bytes(filesize)
|
40
|
+
match = filesize.match(/([\d.]+)(.*)/)
|
41
|
+
|
42
|
+
if match
|
43
|
+
raw_size = match[1].to_f
|
44
|
+
|
45
|
+
case match[2].strip
|
46
|
+
when /gib/i then
|
47
|
+
raw_size * 1000000000
|
48
|
+
when /mib/i then
|
49
|
+
raw_size * 1000000
|
50
|
+
when /kib/i then
|
51
|
+
raw_size * 1000
|
52
|
+
else
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module PirateBay
|
2
|
+
class ResultSet < Array
|
3
|
+
attr_accessor :total_results, :search
|
4
|
+
|
5
|
+
def initialize(initializer)
|
6
|
+
self.search = initializer
|
7
|
+
self.total_results = 1.0/0 #Infinity
|
8
|
+
end
|
9
|
+
|
10
|
+
def retrieved_results
|
11
|
+
self.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def more
|
15
|
+
search.next_page
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/torrent_api.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
require 'hpricot'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
require 'nokogiri'
|
8
|
+
require 'uri'
|
9
|
+
|
10
|
+
%w(result base result_set).each do |filename|
|
11
|
+
require File.join(File.dirname(__FILE__), 'torrent_reactor', filename)
|
12
|
+
end
|
13
|
+
%w(result base categories result_set).each do |filename|
|
14
|
+
require File.join(File.dirname(__FILE__), 'pirate_bay', filename)
|
15
|
+
end
|
16
|
+
%w(result base result_set).each do |filename|
|
17
|
+
require File.join(File.dirname(__FILE__), 'demonoid', filename)
|
18
|
+
end
|
19
|
+
|
20
|
+
class TorrentApi
|
21
|
+
attr_accessor :service, :search_term, :results
|
22
|
+
|
23
|
+
def initialize(service=:pirate_bay, search_term=nil)
|
24
|
+
@service = service
|
25
|
+
@search_term = search_term
|
26
|
+
|
27
|
+
@results = search if @search_term
|
28
|
+
end
|
29
|
+
|
30
|
+
def search
|
31
|
+
if @service == :all
|
32
|
+
results = []
|
33
|
+
results << PirateBay::Search.new(@search_term).execute
|
34
|
+
results << Demonoid::Search.new(@search_term).execute
|
35
|
+
results << TorrentReactor::Search.new(@search_term).execute
|
36
|
+
results = results.flatten.sort_by { |sort| -(sort.seeds) }
|
37
|
+
else
|
38
|
+
case @service
|
39
|
+
when :pirate_bay
|
40
|
+
# do something
|
41
|
+
handler = PirateBay::Search.new(@search_term)
|
42
|
+
when :demonoid
|
43
|
+
# do something
|
44
|
+
handler = Demonoid::Search.new(@search_term)
|
45
|
+
when :torrent_reactor
|
46
|
+
# do something else
|
47
|
+
handler = TorrentReactor::Search.new(@search_term)
|
48
|
+
else
|
49
|
+
raise "You must select a valid service provider"
|
50
|
+
end
|
51
|
+
|
52
|
+
results = handler.execute
|
53
|
+
end
|
54
|
+
@results = results
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# want to be able to do
|
60
|
+
# Torrent::Search.new(:all)
|
61
|
+
# Torrent::Search.new(:demonoid)
|
62
|
+
# Torrent::Search.new(:pirate_bay)
|
63
|
+
# Torrent::Search.new(:torrent_reactor)
|
64
|
+
# Search::Mininova.new('search_term')
|
65
|
+
#t = TorrentApi.new
|
66
|
+
#t.category = :movie # category may not be implemented in every api call
|
67
|
+
#t.service = :mininova
|
68
|
+
#results = t.get_results
|
69
|
+
#results.each { |torrent| puts torrent.seeds }
|
70
|
+
#
|
71
|
+
#t.service = :pirate_bay
|
72
|
+
#results = t.get_results
|
73
|
+
#
|
74
|
+
#t.service = :all
|
75
|
+
#results = t.get_results
|
76
|
+
#
|
77
|
+
#shorthand
|
78
|
+
#results = TorrentApi(:mininova, 'search_term')
|
79
|
+
#results = TorrentApi(:mininova, 'search_term')
|
80
|
+
#
|
81
|
+
#t = Torrent::Search.new(:mini_nova)
|
82
|
+
# results = t.execute
|
83
|
+
# results.each { |torrent| torrent.class = Torrent }
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module TorrentReactor
|
2
|
+
class Search
|
3
|
+
attr_accessor :search_string, :category_id, :page, :caching, :results
|
4
|
+
|
5
|
+
def initialize(search_string)
|
6
|
+
self.search_string = URI.encode(search_string)
|
7
|
+
# self.category_id = TorrentReactor::Categories::IDS[category.upcase.strip.gsub(/S$/, "").to_sym] unless category == 0
|
8
|
+
self.page = -1
|
9
|
+
|
10
|
+
@results = TorrentReactor::ResultSet.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_search_results
|
14
|
+
if caching && File.exists?(cached_filename)
|
15
|
+
content = File.read(cached_filename)
|
16
|
+
else
|
17
|
+
content = fetch_search_results
|
18
|
+
|
19
|
+
FileUtils.mkdir_p("tmp/searches")
|
20
|
+
File.open(cached_filename, "w") do |f|
|
21
|
+
f.write(content)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
content
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute
|
28
|
+
return nil if search_string.nil?
|
29
|
+
self.page += 1
|
30
|
+
|
31
|
+
if (@results.size < @results.total_results)
|
32
|
+
doc = get_search_results
|
33
|
+
end
|
34
|
+
|
35
|
+
next_page(doc)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def cached_filename
|
40
|
+
File.join("tmp", "searches", "#{search_string}_#{category_id}_#{page}.html")
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def fetch_search_results
|
45
|
+
|
46
|
+
base_search_url = 'http://www.torrentreactor.net/search.php?search=&words='
|
47
|
+
sortstring = "&sid=&type=1&exclude=&orderby=a.seeds&cid=5&asc=0&x=47&y=6"
|
48
|
+
searchurl = base_search_url << search_string.split.join("+") << sortstring
|
49
|
+
|
50
|
+
Hpricot(URI.parse(searchurl).read)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def next_page(doc)
|
56
|
+
if @results.total_results == 1.0/0 # Infinity
|
57
|
+
results = doc.search('p[@align=center]')
|
58
|
+
links = results.search('a')
|
59
|
+
if links.count > 0
|
60
|
+
match = links.last.inner_html.match(/(\d+)-(\d+)/)
|
61
|
+
else
|
62
|
+
match = results.inner_html.match(/(\d+)-(\d+)/)
|
63
|
+
end
|
64
|
+
if (match.nil?)
|
65
|
+
@results.total_results = 0
|
66
|
+
else
|
67
|
+
puts match[2].to_i
|
68
|
+
@results.total_results = match[2].to_i
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
tables = doc.search(".styled") # there are two, the first of which is advertisements ( as of 6/22/2011 )
|
73
|
+
torrent_table = tables[1]
|
74
|
+
torrent_table.search('.torrent_odd, .torrent_even').each do |row|
|
75
|
+
result = TorrentReactor::Result.new(row)
|
76
|
+
@results << result
|
77
|
+
end
|
78
|
+
|
79
|
+
@results
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module TorrentReactor
|
2
|
+
class Result
|
3
|
+
attr_accessor :name, :seeds, :leeches, :category, :link, :magnet_link, :status, :size, :date
|
4
|
+
|
5
|
+
def initialize(row = nil)
|
6
|
+
link = ''
|
7
|
+
date = row.search("/td[1]").inner_text
|
8
|
+
title = row.search("/td[2]").inner_text.strip
|
9
|
+
raw_filesize = row.search("/td[3]").inner_html
|
10
|
+
seeders = row.search("/td[4]").inner_text.to_i
|
11
|
+
row.search("/td[2] a").each { |b| link = b.get_attribute('href') if b.get_attribute('href') =~ /download\.php/ }
|
12
|
+
leeches = row.search("/td[5]").inner_text.to_i
|
13
|
+
|
14
|
+
size = TorrentReactor::Result.filesize_in_bytes(raw_filesize)
|
15
|
+
|
16
|
+
self.date = date
|
17
|
+
self.name = title
|
18
|
+
self.link = link
|
19
|
+
self.size = size
|
20
|
+
self.seeds = seeders
|
21
|
+
self.leeches = leeches
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"<TorrentReactor::Result @name => #{name}, @seeds => #{seeds}, @size => #{size}...>"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.filesize_in_bytes(filesize)
|
30
|
+
match = filesize.match(/([\d.]+)(.*)/)
|
31
|
+
|
32
|
+
if match
|
33
|
+
raw_size = match[1].to_f
|
34
|
+
|
35
|
+
case match[2].strip
|
36
|
+
when /gb/i then
|
37
|
+
raw_size * 1000000000
|
38
|
+
when /mb/i then
|
39
|
+
raw_size * 1000000
|
40
|
+
when /kb/i then
|
41
|
+
raw_size * 1000
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
else
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TorrentReactor
|
2
|
+
class ResultSet < Array
|
3
|
+
attr_accessor :total_results, :search
|
4
|
+
|
5
|
+
def initialize(initializer)
|
6
|
+
self.search = initializer
|
7
|
+
self.total_results = 1.0/0 #Infinity
|
8
|
+
end
|
9
|
+
|
10
|
+
def retrieved_results
|
11
|
+
self.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def more
|
15
|
+
search.next_page
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/torrent_api.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{torrent_api}
|
5
|
+
s.version = "0.0.9"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["James Hart"]
|
9
|
+
s.date = %q{2011-06-24}
|
10
|
+
s.description = %q{An API to query popular torrent websites}
|
11
|
+
s.email = %q{hjhart@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "README.markdown", "lib/demonoid/base.rb", "lib/demonoid/result.rb", "lib/demonoid/result_set.rb", "lib/pirate_bay/base.rb", "lib/pirate_bay/categories.rb", "lib/pirate_bay/result.rb", "lib/pirate_bay/result_set.rb", "lib/torrent_api.rb", "lib/torrent_reactor/base.rb", "lib/torrent_reactor/result.rb", "lib/torrent_reactor/result_set.rb"]
|
13
|
+
s.files = ["CHANGELOG", "Manifest", "README.markdown", "Rakefile", "lib/demonoid/base.rb", "lib/demonoid/result.rb", "lib/demonoid/result_set.rb", "lib/pirate_bay/base.rb", "lib/pirate_bay/categories.rb", "lib/pirate_bay/result.rb", "lib/pirate_bay/result_set.rb", "lib/torrent_api.rb", "lib/torrent_reactor/base.rb", "lib/torrent_reactor/result.rb", "lib/torrent_reactor/result_set.rb", "torrent_api.gemspec"]
|
14
|
+
s.homepage = %q{http://www.github.com/hjhart/torrent_api}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Torrent_api", "--main", "README.markdown"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{torrent_api}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{An API to query popular torrent websites}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
s.add_development_dependency(%q<nokogiri>, [">= 0"])
|
27
|
+
s.add_development_dependency(%q<hpricot>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
30
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
31
|
+
end
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
34
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: torrent_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
version: 0.0.9
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- James Hart
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-06-24 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: nokogiri
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: hpricot
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
description: An API to query popular torrent websites
|
47
|
+
email: hjhart@gmail.com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- CHANGELOG
|
54
|
+
- README.markdown
|
55
|
+
- lib/demonoid/base.rb
|
56
|
+
- lib/demonoid/result.rb
|
57
|
+
- lib/demonoid/result_set.rb
|
58
|
+
- lib/pirate_bay/base.rb
|
59
|
+
- lib/pirate_bay/categories.rb
|
60
|
+
- lib/pirate_bay/result.rb
|
61
|
+
- lib/pirate_bay/result_set.rb
|
62
|
+
- lib/torrent_api.rb
|
63
|
+
- lib/torrent_reactor/base.rb
|
64
|
+
- lib/torrent_reactor/result.rb
|
65
|
+
- lib/torrent_reactor/result_set.rb
|
66
|
+
files:
|
67
|
+
- CHANGELOG
|
68
|
+
- Manifest
|
69
|
+
- README.markdown
|
70
|
+
- Rakefile
|
71
|
+
- lib/demonoid/base.rb
|
72
|
+
- lib/demonoid/result.rb
|
73
|
+
- lib/demonoid/result_set.rb
|
74
|
+
- lib/pirate_bay/base.rb
|
75
|
+
- lib/pirate_bay/categories.rb
|
76
|
+
- lib/pirate_bay/result.rb
|
77
|
+
- lib/pirate_bay/result_set.rb
|
78
|
+
- lib/torrent_api.rb
|
79
|
+
- lib/torrent_reactor/base.rb
|
80
|
+
- lib/torrent_reactor/result.rb
|
81
|
+
- lib/torrent_reactor/result_set.rb
|
82
|
+
- torrent_api.gemspec
|
83
|
+
has_rdoc: true
|
84
|
+
homepage: http://www.github.com/hjhart/torrent_api
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options:
|
89
|
+
- --line-numbers
|
90
|
+
- --inline-source
|
91
|
+
- --title
|
92
|
+
- Torrent_api
|
93
|
+
- --main
|
94
|
+
- README.markdown
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
segments:
|
111
|
+
- 1
|
112
|
+
- 2
|
113
|
+
version: "1.2"
|
114
|
+
requirements: []
|
115
|
+
|
116
|
+
rubyforge_project: torrent_api
|
117
|
+
rubygems_version: 1.3.7
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: An API to query popular torrent websites
|
121
|
+
test_files: []
|
122
|
+
|