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 | 
            +
             |