newznab-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,6 @@
1
+ module Newznab
2
+ module Api
3
+ # Newznab::Api gem version
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
@@ -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