newznab-api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +0 -0
- data/.env.example +9 -0
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/Rakefile +38 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/certs/homans.pem +21 -0
- data/lib/newznab/api.rb +311 -0
- data/lib/newznab/api/item.rb +91 -0
- data/lib/newznab/api/list.rb +139 -0
- data/lib/newznab/api/search.rb +193 -0
- data/lib/newznab/api/version.rb +6 -0
- data/newznab-api.gemspec +42 -0
- metadata +288 -0
- metadata.gz.sig +1 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
module Newznab
|
2
|
+
module Api
|
3
|
+
##
|
4
|
+
# Class representing a single Newznab item
|
5
|
+
class Item
|
6
|
+
|
7
|
+
attr_reader :title, :guid, :link, :pub_date, :description, :metadata
|
8
|
+
|
9
|
+
##
|
10
|
+
# @param args [Hash<String, Object>] Item hash from response
|
11
|
+
# @return [Newznab::Item]
|
12
|
+
# @since 0.1.0
|
13
|
+
def initialize(args)
|
14
|
+
|
15
|
+
@raw_resp = args
|
16
|
+
@metadata = {}
|
17
|
+
|
18
|
+
args.each_pair do |k, v|
|
19
|
+
case k
|
20
|
+
when 'title'
|
21
|
+
@title = v
|
22
|
+
when 'guid'
|
23
|
+
@guid = v
|
24
|
+
when 'link'
|
25
|
+
@link = v
|
26
|
+
when 'pubDate'
|
27
|
+
@pub_date = Date.parse(v)
|
28
|
+
when 'description'
|
29
|
+
@description = v
|
30
|
+
when 'enclosure'
|
31
|
+
@_attributes = v['@attributes']
|
32
|
+
when 'attr'
|
33
|
+
@metadata = _parse_attr(v)
|
34
|
+
else
|
35
|
+
# Do nothing
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
##
|
43
|
+
# @param attrs [Array<Hash<Hash<String, String>>>] Newznab attr array response
|
44
|
+
# @return [Hash<String, Array<String>>]
|
45
|
+
# @since 0.1.0
|
46
|
+
def _parse_attr(attrs)
|
47
|
+
|
48
|
+
metadata = {}
|
49
|
+
attrs.each do |attr|
|
50
|
+
name = attr['@attributes']['name']
|
51
|
+
value = attr['@attributes']['value']
|
52
|
+
|
53
|
+
if metadata.has_key? name
|
54
|
+
metadata[name].push value
|
55
|
+
else
|
56
|
+
metadata[name] = [value]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
new_meta = {}
|
60
|
+
metadata.each { |k, v| new_meta[k] = v.count.eql?(1) ? v.first : v }
|
61
|
+
new_meta
|
62
|
+
end
|
63
|
+
|
64
|
+
# @since 0.1.0
|
65
|
+
def method_missing(id, *args)
|
66
|
+
begin
|
67
|
+
if @_attributes.has_key? id.to_s
|
68
|
+
@_attributes[id.to_s]
|
69
|
+
elsif @metadata.has_key? id.to_s
|
70
|
+
@metadata[id.to_s]
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @since 0.1.0
|
78
|
+
def respond_to_missing?(id, *args)
|
79
|
+
begin
|
80
|
+
if @_attributes.has_key? id.to_s
|
81
|
+
true
|
82
|
+
elsif @metadata.has_key? id.to_s
|
83
|
+
true
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'newznab/api'
|
2
|
+
|
3
|
+
module Newznab
|
4
|
+
module Api
|
5
|
+
##
|
6
|
+
# Enumerable list for multiple results
|
7
|
+
class List
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
attr_reader :total_count
|
11
|
+
attr_reader :offset
|
12
|
+
attr_reader :limit
|
13
|
+
attr_reader :cvos
|
14
|
+
|
15
|
+
# @since 0.1.0
|
16
|
+
def initialize(resp, options)
|
17
|
+
update_ivals(resp)
|
18
|
+
|
19
|
+
if options.has_key? :limit
|
20
|
+
@limit = options[:limit]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @since 0.1.0
|
25
|
+
def each
|
26
|
+
@cvos.each { |c| yield c }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the current page the object is on
|
30
|
+
# @return [Integer]
|
31
|
+
# @since 0.1.0
|
32
|
+
def page
|
33
|
+
(self.offset / self.limit) + 1
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the total number of pages available
|
37
|
+
# @return [Integer] Total number of pages
|
38
|
+
# @since 0.1.0
|
39
|
+
def total_pages
|
40
|
+
(self.total_count / self.limit) + 1
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns if there are more pages to load
|
44
|
+
# @return [true, false]
|
45
|
+
# @since 0.1.0
|
46
|
+
def has_more?
|
47
|
+
self.total_pages > self.page ? true : false
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
##
|
53
|
+
# Updates array list to new values
|
54
|
+
# @param new_cvol [Hash] Response hash from {Newznab::Api}
|
55
|
+
# @since 0.1.0
|
56
|
+
def update_ivals(new_cvol)
|
57
|
+
@_attributes = new_cvol['@attributes']
|
58
|
+
|
59
|
+
if new_cvol.has_key?('channel') && new_cvol['channel'].has_key?('response')
|
60
|
+
@total_count = new_cvol['channel']['response']['@attributes']['total'].to_i
|
61
|
+
@offset = new_cvol['channel']['response']['@attributes']['offset'].to_i
|
62
|
+
|
63
|
+
@cvos = new_cvol['channel']['item']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @since 0.1.0
|
68
|
+
def method_missing(id, *args)
|
69
|
+
begin
|
70
|
+
if @_attributes.has_key? id.to_s
|
71
|
+
@_attributes[id.to_s]
|
72
|
+
elsif @cvos.respond_to? id
|
73
|
+
@cvos.method(id).call(*args)
|
74
|
+
else
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @since 0.1.0
|
81
|
+
def respond_to_missing?(id, *args)
|
82
|
+
begin
|
83
|
+
if @_attributes.has_key? id.to_s
|
84
|
+
true
|
85
|
+
elsif @cvos.respond_to? id
|
86
|
+
true
|
87
|
+
else
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Enumerable list for multiple {Newznab::Api::Item} results. Including pagination, and methods to navigate them
|
96
|
+
class SearchResults < List
|
97
|
+
|
98
|
+
attr_reader :raw_resp, :query, :function
|
99
|
+
|
100
|
+
# @param resp [Hash] Response hash from {Newznab::Api}
|
101
|
+
# @param function [Symbol] Function from {Newznab::Api::API_FUNCTIONS}
|
102
|
+
# @param query [Hash] Query parameters from search
|
103
|
+
# @since 0.1.0
|
104
|
+
def initialize(resp, function, query)
|
105
|
+
super(resp, query)
|
106
|
+
|
107
|
+
@function = function
|
108
|
+
@raw_resp = resp
|
109
|
+
@query = query
|
110
|
+
|
111
|
+
# Check for multiple/single results
|
112
|
+
if resp['channel']['item'].kind_of? Array
|
113
|
+
@cvos = resp['channel']['item'].collect { |o| Newznab::Api::Item.new(o) }
|
114
|
+
elsif resp['channel']['item'].kind_of? Hash
|
115
|
+
@cvos = [Newznab::Api::Item.new(resp['channel']['item'])]
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
# ##
|
121
|
+
# Moves search to the next offset results
|
122
|
+
# @since 0.1.0
|
123
|
+
def next_page!
|
124
|
+
return nil if (self.offset + self.total_pages) >= self.total_count
|
125
|
+
@query[:offset] = self.offset + self.limit
|
126
|
+
self.update_ivals(Newznab::Api.get(api_function: self.function, **self.query))
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Moves search to the previous offset results
|
131
|
+
# @since 0.1.0
|
132
|
+
def prev_page!
|
133
|
+
return nil if @offset == 0
|
134
|
+
@query[:offset] = self.offset - self.limit
|
135
|
+
self.update_ivals(Newznab::Api.get(api_function: self.function, **self.query))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
## Yard Doc generation stuff
|
2
|
+
# @!macro [new] raise.FunctionNotSupportedError
|
3
|
+
# @raise [FunctionNotSupportedError] indicating the resource requested is not supported
|
4
|
+
# @!macro [new] raise.NewznabAPIError
|
5
|
+
# @raise [NewznabAPIError] indicating the api request code received
|
6
|
+
# @!macro [new] search.params
|
7
|
+
# @param query [String] Search input (URL/UTF-8 encoded). Case insensitive.
|
8
|
+
# @param group [Array] List of usenet groups to search delimited by ”,”
|
9
|
+
# @param limit [Integer] Upper limit for the number of items to be returned.
|
10
|
+
# @param cat [Array] List of categories to search delimited by ”,”
|
11
|
+
# @param attrs [Array] List of requested extended attributes delimeted by ”,”
|
12
|
+
# @param extended [true, false] List all extended attributes (attrs ignored)
|
13
|
+
# @param delete [true, false] Delete the item from a users cart on download.
|
14
|
+
# @param maxage [Integer] Only return results which were posted to usenet in the last x days.
|
15
|
+
# @param offset [Integer] The 0 based query offset defining which part of the response we want.
|
16
|
+
|
17
|
+
module Newznab
|
18
|
+
module Api
|
19
|
+
##
|
20
|
+
# Module to hold search specific functions
|
21
|
+
module Search
|
22
|
+
|
23
|
+
##
|
24
|
+
# Perform a search with the provided optional params
|
25
|
+
# @macro search.params
|
26
|
+
# @return [Newznab::SearchResults]
|
27
|
+
# @since 0.1.0
|
28
|
+
# @macro raise.NewznabAPIError
|
29
|
+
def search(**params)
|
30
|
+
args = _parse_search_args(**params)
|
31
|
+
Newznab::Api::SearchResults.new(_make_request(:search, **args), :search, args)
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Perform a tv-search with the provided optional params
|
36
|
+
# @param rid [Integer] TVRage id of the item being queried.
|
37
|
+
# @param season [String] Season string, e.g S13 or 13 for the item being queried.
|
38
|
+
# @param ep [String] Episode string, e.g E13 or 13 for the item being queried.
|
39
|
+
# @macro search.params
|
40
|
+
# @macro raise.NewznabAPIError
|
41
|
+
# @return [Newznab::SearchResults]
|
42
|
+
# @since 0.1.0
|
43
|
+
def tv_search(rid: nil, season: nil, ep: nil, **params)
|
44
|
+
args = _parse_search_args(**params)
|
45
|
+
|
46
|
+
unless rid.nil?
|
47
|
+
args[:rid] = rid.to_s.encode('utf-8')
|
48
|
+
end
|
49
|
+
|
50
|
+
unless season.nil?
|
51
|
+
args[:season] = season.to_s.encode('utf-8')
|
52
|
+
end
|
53
|
+
|
54
|
+
unless ep.nil?
|
55
|
+
args[:ep] = ep.to_s.encode('utf-8')
|
56
|
+
end
|
57
|
+
|
58
|
+
Newznab::Api::SearchResults.new(_make_request(:tvsearch, **args), :tvsearch, args)
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Perform a movie-search with the provided optional params
|
63
|
+
# @param imdbid [String] IMDB id of the item being queried e.g. 0058935.
|
64
|
+
# @param genre [String] A genre string i.e. ‘Romance’ would match ‘(Comedy, Drama, Indie, Romance)’
|
65
|
+
# @macro search.params
|
66
|
+
# @macro raise.NewznabAPIError
|
67
|
+
# @return [Newznab::SearchResults]
|
68
|
+
# @since 0.1.0
|
69
|
+
def movie_search(imdbid: nil, genre: nil, **params)
|
70
|
+
args = _parse_search_args(**params)
|
71
|
+
|
72
|
+
unless imdbid.nil?
|
73
|
+
args[:imdbid] = imdbid.to_s.encode('utf-8')
|
74
|
+
end
|
75
|
+
|
76
|
+
unless genre.nil?
|
77
|
+
args[:genre] = genre.to_s.encode('utf-8')
|
78
|
+
end
|
79
|
+
|
80
|
+
Newznab::Api::SearchResults.new(_make_request(:movie, **args), :movie, args)
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Perform a music-search with the provided optional params
|
85
|
+
# @param album [String] Album title (URL/UTF-8 encoded). Case insensitive.
|
86
|
+
# @param artist [String] Artist name (URL/UTF-8 encoded). Case insensitive.
|
87
|
+
# @param label [String] Publisher/Label name (URL/UTF-8 encoded). Case insensitive.
|
88
|
+
# @param track [String] Track name (URL/UTF-8 encoded). Case insensitive.
|
89
|
+
# @param year [String] Four digit year of release.
|
90
|
+
# @param genre [String] A genre string i.e. ‘Romance’ would match ‘(Comedy, Drama, Indie, Romance)’
|
91
|
+
# @macro search.params
|
92
|
+
# @macro raise.NewznabAPIError
|
93
|
+
# @return [Newznab::SearchResults]
|
94
|
+
# @since 0.1.0
|
95
|
+
def music_search(album: nil, artist: nil, label: nil, track: nil, year: nil, genre: nil, **params)
|
96
|
+
args = _parse_search_args(**params)
|
97
|
+
|
98
|
+
unless album.nil?
|
99
|
+
args[:album] = album.to_s.encode('utf-8')
|
100
|
+
end
|
101
|
+
|
102
|
+
unless artist.nil?
|
103
|
+
args[:artist] = artist.to_s.encode('utf-8')
|
104
|
+
end
|
105
|
+
|
106
|
+
unless label.nil?
|
107
|
+
args[:label] = label.to_s.encode('utf-8')
|
108
|
+
end
|
109
|
+
|
110
|
+
unless track.nil?
|
111
|
+
args[:track] = track.to_s.encode('utf-8')
|
112
|
+
end
|
113
|
+
|
114
|
+
unless year.nil?
|
115
|
+
args[:year] = year.to_s.encode('utf-8')
|
116
|
+
end
|
117
|
+
|
118
|
+
unless genre.nil?
|
119
|
+
args[:genre] = genre.to_s.encode('utf-8')
|
120
|
+
end
|
121
|
+
|
122
|
+
Newznab::Api::SearchResults.new(_make_request(:music, **args), :music, args)
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Perform a book-search with the provided optional params
|
127
|
+
# @param title [String] Book title (URL/UTF-8 encoded). Case insensitive.
|
128
|
+
# @param author [String] Author name (URL/UTF-8 encoded). Case insensitive.
|
129
|
+
# @macro search.params
|
130
|
+
# @macro raise.NewznabAPIError
|
131
|
+
# @return [Newznab::SearchResults]
|
132
|
+
# @since 0.1.0
|
133
|
+
def book_search(title: nil, author: nil, **params)
|
134
|
+
args = _parse_search_args(**params)
|
135
|
+
|
136
|
+
unless title.nil?
|
137
|
+
args[:title] = title.to_s.encode('utf-8')
|
138
|
+
end
|
139
|
+
|
140
|
+
unless author.nil?
|
141
|
+
args[:author] = author.to_s.encode('utf-8')
|
142
|
+
end
|
143
|
+
|
144
|
+
Newznab::Api::SearchResults.new(_make_request(:book, **args), :book, args)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
##
|
151
|
+
# @macro search.params
|
152
|
+
# @return [Hash]
|
153
|
+
# @since 0.1.0
|
154
|
+
def _parse_search_args(query: nil, group: [], limit: nil, cat: [], attrs: [], extended: false, delete: false, maxage: nil, offset: nil)
|
155
|
+
params = {
|
156
|
+
extended: extended ? '1' : '0',
|
157
|
+
del: delete ? '1' : '0',
|
158
|
+
}
|
159
|
+
|
160
|
+
unless query.nil?
|
161
|
+
params[:q] = query.to_s.encode('utf-8')
|
162
|
+
end
|
163
|
+
|
164
|
+
unless maxage.nil?
|
165
|
+
params[:maxage] = maxage.to_i
|
166
|
+
end
|
167
|
+
|
168
|
+
unless offset.nil?
|
169
|
+
params[:offset] = offset.to_i
|
170
|
+
end
|
171
|
+
|
172
|
+
unless limit.nil?
|
173
|
+
params[:limit] = limit.to_i
|
174
|
+
end
|
175
|
+
|
176
|
+
unless group.empty?
|
177
|
+
params[:group] = group.collect { |o| o.to_s.encode('utf-8') }.join(',')
|
178
|
+
end
|
179
|
+
|
180
|
+
unless cat.empty?
|
181
|
+
params[:cat] = cat.collect { |o| o.to_s.encode('utf-8') }.join(',')
|
182
|
+
end
|
183
|
+
|
184
|
+
unless attrs.empty?
|
185
|
+
params[:group] = attrs.collect { |o| o.to_s.encode('utf-8') }.join(',')
|
186
|
+
end
|
187
|
+
|
188
|
+
params
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
data/newznab-api.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'newznab/api/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'newznab-api'
|
8
|
+
spec.version = Newznab::Api::VERSION
|
9
|
+
spec.authors = ['Holden Omans']
|
10
|
+
spec.email = ['holden.omans@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = %q{Api interface to Newznab servers.}
|
13
|
+
spec.description = %q{Api interface to Newznab servers. Allows for searches and returning specific information on resources.}
|
14
|
+
spec.homepage = 'https://github.com/kalinon/ruby-newznab-api'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = 'exe'
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
spec.required_ruby_version = '>= 2.2'
|
24
|
+
|
25
|
+
spec.cert_chain = ['certs/homans.pem']
|
26
|
+
spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem') if $0 =~ /gem\z/
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'minitest', '~> 5.9', '>= 5.9.1'
|
31
|
+
spec.add_development_dependency 'minitest-reporters', '~> 1.1', '>= 1.1.12'
|
32
|
+
spec.add_development_dependency 'rdoc', '~> 4.2', '>= 4.2.1'
|
33
|
+
spec.add_development_dependency 'yard', '~> 0.9', '>= 0.9.5'
|
34
|
+
spec.add_development_dependency 'dotenv-rails', '~> 2.2'
|
35
|
+
spec.add_development_dependency 'faker', '~> 1.7', '>= 1.7.3'
|
36
|
+
spec.add_development_dependency 'minitest-vcr', '~> 1.4'
|
37
|
+
spec.add_development_dependency 'nokogiri', '~> 1.7', '>= 1.7.1'
|
38
|
+
|
39
|
+
# Dependencies
|
40
|
+
spec.add_dependency 'rest-client', '~> 2.0'
|
41
|
+
spec.add_dependency 'mono_logger', '~> 1.1'
|
42
|
+
end
|