class RARBG::API

Base class for RARBG API.

Constants

API_ENDPOINT

RARBG API endpoint.

APP_ID

App name identifier.

NO_RESULTS_ERRORS_REGEXPS

Endpoint error strings to return as no results.

RATE_LIMIT

Default API rate limit (seconds).

SEARCH_KEYS

Supported search parameters.

TOKEN_EXPIRATION

Default token expiration time (seconds).

Attributes

connection[R]

The underlying `Faraday::Connection` object used to perform requests.

@note For more info regarding this object refer to the

{https://www.rubydoc.info/gems/faraday/Faraday Faraday documentation}.

@return [Faraday::Connection] The Faraday connection object.

last_request[R]

The monotonic timestamp of the last request performed.

Used to comply with the endpoint rate limit based on {RATE_LIMIT}.

@return [Float] The monotonic timestamp of the last request performed.

token[R]

The token used for authentication.

This is generated and rate limited automatically when calling {#list}
or {#search}, but can also be generated forcefully using {#token!}.

@return [String] The token used for authentication.

token_time[R]

The monotonic timestamp of the token request.

Used to compute the next required token request based on {TOKEN_EXPIRATION}.

@return [Float] The monotonic timestamp of the token request.

Public Class Methods

new() click to toggle source

Initialize a new instance of `RARBG::API`.

@example

rarbg = RARBG::API.new
# File lib/rarbg/api.rb, line 56
def initialize
  @connection = Faraday.new(url: API_ENDPOINT) do |conn|
    conn.response :logger if $VERBOSE
    conn.adapter  Faraday.default_adapter

    conn.options.timeout      = 30
    conn.options.open_timeout = 10

    conn.headers[:user_agent] = APP_ID
    conn.params[:app_id]      = APP_ID
  end
end

Public Instance Methods

list(params = {}) click to toggle source

List torrents.

@example List last 100 ranked torrents in `Movies/x264/1080` category

rarbg = RARBG::API.new
rarbg.list(limit: 100, ranked: true, category: [44])

@example List torrents with at least 50 seeders

rarbg = RARBG::API.new
rarbg.list(min_seeders: 50)

@param params [Hash] A customizable set of parameters.

@option params [Array<Integer>] :category Filter results by category. @option params [Symbol] :format Format results.

Accepted values: `:json`, `:json_extended`.
Default: `:json`.

@option params [Integer] :limit Limit results number.

Accepted values: `25`, `50`, `100`.
Default: `25`.

@option params [Integer] :min_seeders Filter results by minimum seeders. @option params [Integer] :min_leechers Filter results by minimum leechers. @option params [Boolean] :ranked Include/exclude unranked torrents.

Default: `true`.

@option params [Symbol] :sort Sort results.

Accepted values: `:last`, `:seeders`, `:leechers`.
Default: `:last`.

@return [Array<Hash>] Torrents that match the specified parameters.

@raise [ArgumentError] If `params` is not an `Hash`. @raise [RARBG::APIError] If the request fails or the endpoint responds

with an error.

@raise [Faraday::Error] On low-level connection errors (e.g. timeouts).

# File lib/rarbg/api.rb, line 102
def list(params = {})
  raise ArgumentError, 'Expected params hash' unless params.is_a?(Hash)

  params.update(mode: 'list', token: token?)
  call(params)
end
token!() click to toggle source

Generate the authentication token.

@example Generate the token immediately after object instantiation.

rarbg = RARBG::API.new
rarbg.token!

@return [String] The currently valid authentication token.

# File lib/rarbg/api.rb, line 162
def token!
  token?
end

Private Instance Methods

call(params) click to toggle source

Wrap requests for error handling.

# File lib/rarbg/api.rb, line 180
def call(params)
  response = request(validate(params))

  return [] if response['error'] =~ NO_RESULTS_ERRORS_REGEXPS
  raise APIError, response['error'] if response.key?('error')

  response.fetch('torrent_results', [])
end
normalize() click to toggle source

Convert ruby syntax to expected value format.

# File lib/rarbg/api.rb, line 221
def normalize
  {
    'category'    => (->(v) { v.join(';') }),
    'ranked'      => (->(v) { v == false ? 0 : 1 }),
    'search_imdb' => (->(v) { v.to_s[/^tt/] ? v.to_s : "tt#{v}" })
  }
end
rate_limit!(seconds) click to toggle source

Rate-limit requests to comply with endpoint limits.

# File lib/rarbg/api.rb, line 253
def rate_limit!(seconds)
  sleep(0.1) until time >= (last_request.to_f + seconds)
end
request(params) click to toggle source

Perform API request.

# File lib/rarbg/api.rb, line 241
def request(params)
  rate_limit!(RATE_LIMIT)

  response      = connection.get(nil, params)
  @last_request = time

  return JSON.parse(response.body) if response.success?

  raise APIError, "#{response.reason_phrase} (#{response.status})"
end
stringify(params) click to toggle source

Convert symbol keys to string and remove nil values.

# File lib/rarbg/api.rb, line 202
def stringify(params)
  Hash[params.reject { |_k, v| v.nil? }.map { |k, v| [k.to_s, v] }]
end
time() click to toggle source

Monotonic clock for elapsed time calculations.

# File lib/rarbg/api.rb, line 258
def time
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
token?() click to toggle source

Return or renew auth token.

# File lib/rarbg/api.rb, line 230
def token?
  if token.nil? || time >= (token_time.to_f + TOKEN_EXPIRATION)
    response    = request(get_token: 'get_token')
    @token      = response.fetch('token')
    @token_time = time
  end

  token
end
validate(params) click to toggle source

Validate parameters.

# File lib/rarbg/api.rb, line 190
def validate(params)
  params = stringify(params)
  params = validate_search!(params) if params['mode'] == 'search'

  normalize.each_pair do |key, proc|
    params[key] = proc.call(params[key]) if params.key?(key)
  end

  params
end
validate_search!(params) click to toggle source

Validate search type parameters.

# File lib/rarbg/api.rb, line 207
def validate_search!(params)
  if (params.keys & SEARCH_KEYS).none?
    raise(ArgumentError,
          "One search parameter required among: #{SEARCH_KEYS.join(', ')}")
  end

  SEARCH_KEYS.each do |k|
    params["search_#{k}"] = params.delete(k) if params.key?(k)
  end

  params
end