newznab-api 0.1.0
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.
- 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
|