Class: Kat::Search

Inherits:
Object
  • Object
show all
Defined in:
lib/kat/search.rb

Constant Summary collapse

@@doc =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(search_term = nil, opts = {}) ⇒ Search

Create a new Kat::Search object to search Kickass Torrents. The search_term can be nil, a string/symbol, or an array of strings/symbols. Valid options are in STRING_FIELDS, SELECT_FIELDS or SWITCH_FIELDS.



67
68
69
70
71
72
73
74
75
# File 'lib/kat/search.rb', line 67

def initialize(search_term = nil, opts = {})
  @search_term = []
  @options = {}
  @error = nil
  @message = nil

  self.query = search_term
  self.options = opts
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object (private)

If method or its plural form is a field name in the results list, fetch the list of values. Can only happen after a successful search.



316
317
318
# File 'lib/kat/search.rb', line 316

def method_missing(method, *args, &block)
  respond_to?(method) ? results_column(method) : super
end

Instance Attribute Details

#errorObject (readonly)

Any error in searching is stored here



28
29
30
# File 'lib/kat/search.rb', line 28

def error
  @error
end

#messageObject (readonly)

Returns the value of attribute message.



30
31
32
# File 'lib/kat/search.rb', line 30

def message
  @message
end

#pagesObject (readonly)

The number of pages of results



25
26
27
# File 'lib/kat/search.rb', line 25

def pages
  @pages
end

Class Method Details

.checksObject



57
# File 'lib/kat/search.rb', line 57

def self.checks;  field_map :check  end

.field_map(type = nil) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/kat/search.rb', line 41

def self.field_map(type = nil)
  return FIELD_MAP.dup unless type

  FIELD_MAP.each_with_object({}) do |(k, v), h|
    case type
    when :select
      h[k] = { select: v[:select], id: v[:id] || k }
    when :sort
      h[k] = v[:sort] && h[v[:sort]] = v[:sort]
      h[v[:id]] = v[:sort] if v[:id]
    else
      h[k] = v[type]
    end if v[type]
  end
end

.inputsObject



58
# File 'lib/kat/search.rb', line 58

def self.inputs;  field_map :input  end

.quick_search(search_term = nil) ⇒ Object

Kat.quick_search will do a quick search and return the results



37
38
39
# File 'lib/kat/search.rb', line 37

def self.quick_search(search_term = nil)
  new(search_term).search
end

.selectsObject



59
# File 'lib/kat/search.rb', line 59

def self.selects; field_map :select end

.sortsObject



60
# File 'lib/kat/search.rb', line 60

def self.sorts;   field_map :sort   end

Instance Method Details

#checksObject



206
# File 'lib/kat/search.rb', line 206

def checks;  Search.checks  end

#go(page = 0) ⇒ Object Also known as: do_search

For message chaining



191
192
193
194
# File 'lib/kat/search.rb', line 191

def go(page = 0)
  search page
  self
end

#inputsObject



207
# File 'lib/kat/search.rb', line 207

def inputs;  Search.inputs  end

#optionsObject

Get a copy of the search options hash



113
114
115
# File 'lib/kat/search.rb', line 113

def options
  Marshal.load(Marshal.dump(@options))
end

#options=(options) ⇒ Object

Change search options with a hash, triggering a query string rebuild and clearing past results.

Raises ArgumentError if options is not a Hash



123
124
125
126
127
128
129
130
# File 'lib/kat/search.rb', line 123

def options=(options)
  fail ArgumentError, 'options must be a Hash. ' \
                      "#{ options.inspect } given." unless options.is_a?(Hash)

  @options.merge! options

  build_query
end

#query=(search_term) ⇒ Object

Change the search term, triggering a query rebuild and clearing past results.

Raises ArgumentError if search_term is not a String, Symbol or Array



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/kat/search.rb', line 97

def query=(search_term)
  @search_term =
    case search_term
    when nil, ''        then []
    when String, Symbol then [search_term]
    when Array          then search_term.flatten.select { |e| [String, Symbol].include? e.class }
    else fail ArgumentError, 'search_term must be a String, Symbol or Array. ' \
                             "#{ search_term.inspect } given."
    end

  build_query
end

#query_str(page = 0) ⇒ Object

Generate a query string from the stored options, supplying an optional page number



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/kat/search.rb', line 80

def query_str(page = 0)
  str = [SEARCH_PATH, @query.join(' ').gsub(/[^a-z0-9: _-]/i, '')]
  str = [RECENT_PATH] if str[1].empty?
  str << page + 1 if page > 0

  sorts.detect { |k, v| @options[:sort] && k == @options[:sort].intern }.tap do |k, v|
    str << (k ? "?field=#{ v }&sorder=#{ options[:asc] ? 'asc' : 'desc' }" : '')
  end

  str.join '/'
end

#resultsObject

Get a copy of the results



202
203
204
# File 'lib/kat/search.rb', line 202

def results
  Marshal.load(Marshal.dump(@results))
end

#search(page_num = 0) ⇒ Object

Perform the search, supplying an optional page number to search on. Returns a result set limited to the 25 results Kickass Torrents returns itself. Will cache results for subsequent calls of search with the same query string.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/kat/search.rb', line 137

def search(page_num = 0)
  @error = nil
  @message = nil

  search_proc = lambda do |page|
    begin
      uri = URI(URI.encode(to_s page))
      res = Net::HTTP.get_response(uri)
      if res.code == '301'
        path = Net::HTTP::Get.new(res.header['location'])
        res = Net::HTTP.start(uri.host) { |http| http.request path }
      end

      @pages = 0 and return if res.code == '404'

      doc = Nokogiri::HTML(res.body)

      @results[page] = doc.xpath('//table[@class="data"]//tr[position()>1]/td[1]').map do |node|
        { path:     href_of(node, 'a.torType'),
          title:    node.css('a.cellMainLink').text,
          magnet:   href_of(node, 'a[title="Torrent magnet link"]'),
          download: href_of(node, 'a[title="Download torrent file"]'),
          size:     (node = node.next_element).text,
          files:    (node = node.next_element).text.to_i,
          age:      (node = node.next_element).text,
          seeds:    (node = node.next_element).text.to_i,
          leeches:  node.next_element.text.to_i }
      end

      # If we haven't previously performed a search with this query string, get the
      # number of pages from the pagination bar at the bottom of the results page.
      # If there's no pagination bar there's only 1 page of results.
      if @pages == -1
        p = doc.css('div.pages > a').last
        @pages = p ? [1, p.text.to_i].max : 1
      end
    rescue => e
      @error = { error: e }
    end unless @results[page] || (@pages > -1 && page >= @pages)
  end

  # Make sure we do a query for the first page of results before getting
  # subsequent pages in order to correctly figure out the total number of
  # pages of results.
  pages = (page_num.is_a?(Range) ? page_num.to_a : [page_num])
  pages.unshift(0) if @pages == -1 && !pages.include?(0)
  pages.each { |i| search_proc.call i }

  results[page_num.is_a?(Range) ? page_num.max : page_num]
end

#selectsObject



208
# File 'lib/kat/search.rb', line 208

def selects; Search.selects end

#sortsObject



209
# File 'lib/kat/search.rb', line 209

def sorts;   Search.sorts   end

#to_s(page = 0) ⇒ Object

Use the search url as the string representation of the object



214
215
216
# File 'lib/kat/search.rb', line 214

def to_s(page = 0)
  "#{ BASE_URL }/#{ query_str page }"
end