torrent_api 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|