thegamesdb 1.1.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rake/testtask'
2
4
  require 'bundler/gem_tasks'
3
5
 
@@ -5,6 +7,7 @@ Rake::TestTask.new('test') do |t|
5
7
  t.pattern = 'test/**/*_test.rb'
6
8
  end
7
9
 
10
+ desc 'Run a Ruby console with gamesdb already loaded'
8
11
  task :console do
9
12
  require 'irb'
10
13
  require 'irb/completion'
@@ -1,246 +1,146 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thegamesdb/version'
2
- require 'thegamesdb/config'
4
+ require 'thegamesdb/developers'
5
+ require 'thegamesdb/games'
6
+ require 'thegamesdb/genres'
7
+ require 'thegamesdb/platforms'
8
+ require 'thegamesdb/publishers'
3
9
  require 'net/http'
4
10
  require 'json'
5
11
 
6
- # Client for TheGamesDB API (thegamesdb.net)
7
12
  module Gamesdb
8
- BASE_URL = 'https://api.thegamesdb.net/v1/'.freeze
9
- IMAGES_BASE_URL = 'https://legacy.thegamesdb.net/banners/'.freeze
10
-
11
- # Method for listing platform's games
12
- #
13
- # See: https://api.thegamesdb.net/#/operations/Games/GamesByPlatformID
14
- #
15
- # Parameters: platform id (int), page (int)
16
- #
17
- # = Returns:
18
- # Array of Hashes with games info
19
- #
20
- def self.games_by_platform_id(platform_id, page = 1)
21
- url = 'Games/ByPlatformID'
22
- params = {id: platform_id, page: page, include: 'boxart'}
23
- data = json_response(url, params)
24
- process_platform_games(data)
25
- end
13
+ # Client for TheGamesDB API (thegamesdb.net)
14
+ class Client
15
+ include Gamesdb::Developers
16
+ include Gamesdb::Games
17
+ include Gamesdb::Genres
18
+ include Gamesdb::Platforms
19
+ include Gamesdb::Publishers
26
20
 
27
- # Method for listing platforms
28
- # https://api.thegamesdb.net/#/operations/Platforms/Platforms
29
- #
30
- # Parameters: none
31
- #
32
- # == Returns:
33
- # Array of Hashes with platforms info
34
- #
35
- def self.platforms
36
- url = 'Platforms'
37
- params = { fields: 'icon,console,controller,developer,manufacturer,media,cpu,memory,graphics,sound,maxcontrollers,display,overview,youtube' }
38
- data = json_response(url, params)
39
-
40
- data['data']['platforms'].map do |p|
41
- symbolize_keys(p.last)
42
- end
43
- end
21
+ BASE_URL = 'https://api.thegamesdb.net/v1/'
22
+ IMAGES_BASE_URL = 'https://legacy.thegamesdb.net/banners/'
44
23
 
45
- # This API feature returns a set of metadata and artwork data for a
46
- # specified Platform ID.
47
- # https://api.thegamesdb.net/#/operations/Platforms/PlatformsByPlatformID
48
- #
49
- # Parameters:
50
- # - id - (int) The numeric ID of the platform in the GamesDB database
51
- #
52
- # == Returns:
53
- # Hash with platform info
54
- #
55
- def self.platform_by_id(id)
56
- url = 'Platforms/ByPlatformID'
57
- params = {
58
- id: id,
59
- fields: 'icon,console,controller,developer,manufacturer,media,cpu,memory,graphics,sound,maxcontrollers,display,overview,youtube'
60
- }
61
- data = json_response(url, params)
62
-
63
- response = data['data']['platforms'].values.first
64
- symbolize_keys(response)
65
- end
24
+ attr_reader :remaining_monthly_allowance, :extra_allowance, :allowance_refresh_timer
66
25
 
67
- # Method for getting game info
68
- # TODO: check (and test) that we support ',' delimited list
69
- # https://api.thegamesdb.net/#/operations/Games/GamesByGameID
70
- #
71
- # Parameters:
72
- # - id - (int) Game id
73
- #
74
- # == Returns:
75
- # Hash with game info
76
- #
77
- def self.game_by_id(id)
78
- url = 'Games/ByGameID'
79
- params = {
80
- id: id,
81
- fields: 'players,publishers,genres,overview,last_updated,rating,platform,coop,youtube,os,processor,ram,hdd,video,sound,alternates',
82
- include: 'boxart,platform'
83
- }
84
- data = json_response(url, params)
85
- return [] if data['data']['count'] == 0
86
- symbolize_keys(data['data']['games'].first)
87
- end
26
+ def initialize(api_key)
27
+ @api_key = api_key
28
+ end
88
29
 
89
- # The GetGamesList API search returns a listing of games matched up
90
- # with loose search terms.
91
- # https://api.thegamesdb.net/#/operations/Games/GamesByGameName
92
- #
93
- # Parameters:
94
- # - name (required)
95
- # - platform (optional - platform id)
96
- # - page (optional)
97
- #
98
- # == Returns:
99
- # Hash with game info: id, name (not-unique), release_date,
100
- # platform, etc.
101
- #
102
- def self.games_by_name(name, platform: nil, page: 1)
103
- url = 'Games/ByGameName'
104
- params = {
105
- fields: 'players,publishers,genres,overview,last_updated,rating,platform,coop,youtube,os,processor,ram,hdd,video,sound,alternates',
106
- include: 'boxart',
107
- name: name,
108
- page: page
109
- }
110
- unless platform.nil?
111
- params.merge!("filter[platform]" => platform)
30
+ # Perform request
31
+ #
32
+ # Used by every API endpoint, but can also be used manually.
33
+ #
34
+ # @param url [String] Required
35
+ # @param params [Hash] optional
36
+ #
37
+ # @return [Hash] Parsed JSON response
38
+ def perform_request(url, params = {})
39
+ raise ArgumentError, 'You need to set the API KEY to use the GamesDB API' unless @api_key
40
+
41
+ params = params.merge({ apikey: @api_key })
42
+ uri = URI(BASE_URL + url)
43
+ uri.query = URI.encode_www_form(params)
44
+ response = JSON.parse(Net::HTTP.get_response(uri).body)
45
+ refresh_allowances(response)
46
+ response
47
+ rescue StandardError => e
48
+ # TODO: Handle errors
49
+ raise e
112
50
  end
113
51
 
114
- data = json_response(url, params)
115
- process_platform_games(data)
116
- end
52
+ private
117
53
 
118
- # This API feature returns a list of available artwork types and
119
- # locations specific to the requested game id in the database. It
120
- # also lists the resolution of any images available. Scrapers can be
121
- # set to use a minimum or maximum resolution for specific images
122
- # https://api.thegamesdb.net/#/operations/Games/GamesImages
123
- #
124
- # Parameters
125
- # - id - (integer) The numeric ID of the game in Gamesdb that you
126
- # like to fetch artwork details for
127
- #
128
- # == Returns:
129
- # Hash with game art info: fanart (array), boxart (Hash, :front,
130
- # :back), screenshots (array), fanart (array)
131
- #
132
- def self.game_images(id)
133
- url = 'Games/Images'
134
- data = json_response(url, games_id: id)
135
- return [] if data.dig('data', 'count') == (0 || nil)
136
-
137
- response = {}
138
- response[:base_url] = data['data']['base_url']['original']
139
- response[:logo] = process_logo(data['data'], id)
140
- response[:boxart] = process_covers(data['data'], id)
141
- response[:screenshot] = process_screenshots(data['data'], id)
142
- response[:fanart] = process_fanart(data['data'], id)
143
- response
144
- end
145
-
146
- def self.process_logo(data, id)
147
- logo = data['images'][id.to_s].select { |a| a['type'] == "clearlogo" }
148
- logo.empty? ? '' : logo.first['filename']
149
- end
54
+ def refresh_allowances(response)
55
+ @remaining_monthly_allowance = response['remaining_monthly_allowance']
56
+ @extra_allowance = response['extra_allowance']
57
+ @allowance_refresh_timer = response['allowance_refresh_timer']
58
+ end
150
59
 
151
- def self.process_fanart(data, id)
152
- fanarts = []
153
- fanart = data['images'][id.to_s].select do |a|
154
- a['type'] == 'fanart'
60
+ def process_logo(data, id)
61
+ logo = data['images'][id.to_s].select { |a| a['type'] == 'clearlogo' }
62
+ logo.empty? ? '' : logo.first['filename']
155
63
  end
156
- return [] if fanart.empty?
157
- fanart.each do |art|
158
- width, height = art['resolution'].split("x") unless art['resolution'].nil?
159
- fanarts << {
160
- url: art['filename'],
161
- resolution: art['resolution'],
162
- width: width,
163
- height: height
164
- }
64
+
65
+ def process_fanart(data, id)
66
+ fanart = select_images(data, id, 'fanart')
67
+ return [] if fanart.empty?
68
+
69
+ fanart.map { |art| build_individual_fanart(art) }
165
70
  end
166
- fanarts
167
- end
168
71
 
169
- def self.process_screenshots(data, id)
170
- data['images'][id.to_s].select do |a|
171
- a['type'] == 'screenshot'
172
- end.map { |b| symbolize_keys(b) }
173
- end
72
+ def process_covers(data, id)
73
+ covers = {}
74
+ boxart = select_images(data, id, 'boxart')
75
+ return [] if boxart.empty?
174
76
 
175
- def self.process_covers(data, id)
176
- covers = {}
177
- boxart = data['images'][id.to_s].select do |a|
178
- a['type'] == "boxart"
77
+ boxart.each do |art|
78
+ width, height = art['resolution'].split('x') unless art['resolution'].nil?
79
+ covers[art['side'].to_sym] = art_structure(art, width, height)
80
+ end
81
+ covers
179
82
  end
180
- return [] if boxart.empty?
181
- boxart.each do |art|
182
- width, height = art['resolution'].split("x") unless art['resolution'].nil?
183
- covers[art['side'].to_sym] = {
83
+
84
+ def build_individual_fanart(art)
85
+ width, height = art['resolution'].split('x') unless art['resolution'].nil?
86
+ art_structure(art, width, height)
87
+ end
88
+
89
+ def art_structure(art, width, height)
90
+ {
184
91
  url: art['filename'],
185
92
  resolution: art['resolution'],
186
93
  width: width,
187
94
  height: height
188
95
  }
189
96
  end
190
- covers
191
- end
192
97
 
193
- private
194
-
195
- def self.configuration
196
- @configuration ||= Config.new
197
- end
198
-
199
- # Api call and xml parsing
200
- def self.json_response(url, params = {})
201
- params = params.merge({apikey: configuration.api_key})
202
-
203
- uri = URI(BASE_URL + url)
204
- uri.query = URI.encode_www_form(params)
205
- request = Net::HTTP.get_response(uri)
206
- response = JSON.parse(request.body)
207
- response
208
- end
98
+ def process_screenshots(data, id)
99
+ select_images(data, id, 'screenshot').map { |b| symbolize_keys(b) }
100
+ end
209
101
 
210
- # Process games for platform_games
211
- def self.process_platform_games(data)
212
- games = []
213
-
214
- data['data']['games'].each do |elem|
215
- id = elem['id']
216
- games << {
217
- name: elem['game_title'],
218
- id: id,
219
- release_date: elem['release_date'],
220
- platform: elem['platform'],
221
- developers: elem['developers'],
222
- players: elem['players'],
223
- publishers: elem['publishers'],
224
- genres: elem['genres'],
225
- overview: elem['overview'],
226
- last_updated: elem['last_updated'],
227
- rating: elem['rating'],
228
- coop: elem['coop'],
229
- youtube: elem['youtube'],
230
- alternates: elem['alternates'],
231
- image: if boxart = data.dig('include', 'boxart', 'data', id.to_s)
232
- data['include']['boxart']['base_url']['original'] +
233
- boxart.select { |a| a['side'] == 'front' }.first['filename'] || ''
234
- end
235
- }
102
+ def select_images(data, id, image_type)
103
+ data['images'][id.to_s].select do |a|
104
+ a['type'] == image_type
105
+ end
236
106
  end
237
- games
238
- end
239
107
 
240
- def self.symbolize_keys(hash)
241
- hash.keys.each do |key|
242
- hash[key.to_sym] = hash.delete(key)
108
+ # Process games for platform_games
109
+ # rubocop:disable Metrics/AbcSize
110
+ # rubocop:disable Metrics/MethodLength
111
+ def process_platform_games(data)
112
+ data['data']['games'].map do |elem|
113
+ {
114
+ name: elem['game_title'],
115
+ id: elem['id'],
116
+ release_date: elem['release_date'],
117
+ platform: elem['platform'],
118
+ developers: elem['developers'],
119
+ players: elem['players'],
120
+ publishers: elem['publishers'],
121
+ genres: elem['genres'],
122
+ overview: elem['overview'],
123
+ last_updated: elem['last_updated'],
124
+ rating: elem['rating'],
125
+ coop: elem['coop'],
126
+ youtube: elem['youtube'],
127
+ alternates: elem['alternates'],
128
+ image: if (boxart = data.dig('include', 'boxart', 'data', elem['id'].to_s))
129
+ data['include']['boxart']['base_url']['original'] +
130
+ boxart.select { |a| a['side'] == 'front' }.first['filename'] || ''
131
+ end
132
+ }
133
+ end
134
+ end
135
+ # rubocop:enable Metrics/AbcSize
136
+ # rubocop:enable Metrics/MethodLength
137
+
138
+ def symbolize_keys(hash)
139
+ new_hash = {}
140
+ hash.each_key do |key|
141
+ new_hash[key.to_sym] = hash.delete(key)
142
+ end
143
+ new_hash
243
144
  end
244
- hash
245
145
  end
246
146
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamesdb
4
+ # Developers related API Endpoints
5
+ module Developers
6
+ # Fetches Developers list
7
+ #
8
+ # @see https://api.thegamesdb.net/#/Developers/Developers
9
+ #
10
+ # @return Array of Hashes with id and name as keys
11
+ def developers
12
+ url = 'Developers'
13
+ data = perform_request(url)
14
+ data['data']['developers'].map { |_id, developer| developer }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gamesdb
4
+ # Games related API Endpoints
5
+ module Games
6
+ # Method for listing platform's games
7
+ #
8
+ # @see https://api.thegamesdb.net/#/Games/GamesByPlatformID
9
+ #
10
+ # @param id [Integer]
11
+ # @param page [Integer]
12
+ #
13
+ # @return [Array] Array of Hashes with games info
14
+ #
15
+ def games_by_platform_id(platform_id, page = 1)
16
+ url = 'Games/ByPlatformID'
17
+ params = { id: platform_id, page: page, include: 'boxart' }
18
+ data = perform_request(url, params)
19
+ process_platform_games(data)
20
+ end
21
+
22
+ # Method for getting game info
23
+ #
24
+ # @see https://api.thegamesdb.net/#/Games/GamesByGameID
25
+ #
26
+ # @param id [Integer|String] Game id or string of ',' delimited list
27
+ #
28
+ # @return [Array|Hash] Hash with game info
29
+ #
30
+ # rubocop:disable Metrics/MethodLength
31
+ def games_by_id(id)
32
+ url = 'Games/ByGameID'
33
+ params = {
34
+ id: id,
35
+ fields:
36
+ 'players,publishers,genres,overview,last_updated,rating,platform,coop,youtube,os,processor,ram,hdd,'\
37
+ 'video,sound,alternates',
38
+ include: 'boxart,platform'
39
+ }
40
+ data = perform_request(url, params)
41
+ return [] if (data['data']['count']).zero?
42
+
43
+ games = data['data']['games']
44
+ return symbolize_keys(games.first) if games.count == 1
45
+
46
+ games.map { |game| symbolize_keys(game) }
47
+ end
48
+ # rubocop:enable Metrics/MethodLength
49
+
50
+ # The GetGamesList API search returns a listing of games matched up with loose search terms.
51
+ #
52
+ # @see https://api.thegamesdb.net/#/Games/GamesByGameName
53
+ #
54
+ # @param name [String] game name (required)
55
+ # @param platform [Integer] (optional - platform id)
56
+ # @param page [Integer] (optional)
57
+ #
58
+ # @return [Hash] Hash with game info: id, name (not-unique), release_date, platform, etc.
59
+ #
60
+ # rubocop:disable Metrics/MethodLength
61
+ def games_by_name(name, platform: nil, page: 1)
62
+ url = 'Games/ByGameName'
63
+ params = {
64
+ fields:
65
+ 'players,publishers,genres,overview,last_updated,rating,platform,coop,youtube,os,processor,ram,hdd'\
66
+ ',video,sound,alternates',
67
+ include: 'boxart',
68
+ name: name,
69
+ page: page
70
+ }
71
+ params.merge!('filter[platform]' => platform) unless platform.nil?
72
+
73
+ data = perform_request(url, params)
74
+ process_platform_games(data)
75
+ end
76
+ # rubocop:enable Metrics/MethodLength
77
+
78
+ # This API feature returns a list of available artwork types and
79
+ # locations specific to the requested game id in the database. It
80
+ # also lists the resolution of any images available. Scrapers can be
81
+ # set to use a minimum or maximum resolution for specific images
82
+ #
83
+ # @see https://api.thegamesdb.net/#/Games/GamesImages
84
+ #
85
+ # @param id [Integer] The numeric ID of the game in Gamesdb that you like to fetch artwork details for
86
+ #
87
+ # @return [Hash] Hash with game art info: fanart (array), boxart (Hash,
88
+ # :front, :back), screenshots (array), fanart (array)
89
+ #
90
+ # rubocop:disable Metrics/AbcSize
91
+ def games_images(id)
92
+ url = 'Games/Images'
93
+ data = perform_request(url, games_id: id)
94
+ return [] if data.dig('data', 'count') == (0 || nil)
95
+
96
+ response = {}
97
+ response[:base_url] = data['data']['base_url']['original']
98
+ response[:logo] = process_logo(data['data'], id)
99
+ response[:boxart] = process_covers(data['data'], id)
100
+ response[:screenshot] = process_screenshots(data['data'], id)
101
+ response[:fanart] = process_fanart(data['data'], id)
102
+ response
103
+ end
104
+
105
+ # Fetch games update
106
+ #
107
+ # @see https://api.thegamesdb.net/#/Games/GamesUpdates
108
+ #
109
+ # @param last_edit_id [Integer] Required
110
+ # @param time [Integer] (optional)
111
+ # @param page [Integer] results page offset to return (optional)
112
+ # rubocop:disable Metrics/CyclomaticComplexity
113
+ # rubocop:disable Metrics/PerceivedComplexity
114
+ def games_update(last_edit_id, arguments = {})
115
+ url = 'Games/Updates'
116
+ params = arguments.merge({ last_edit_id: last_edit_id })
117
+ data = perform_request(url, params)
118
+
119
+ regexp = /page=([0-9]+)/
120
+ response = {}
121
+ response[:updates] = data['data']['updates']
122
+ response[:previous_page] = data.dig('pages', 'previous')&.match(regexp)&.captures&.first&.to_i
123
+ response[:next_page] = data.dig('pages', 'next')&.match(regexp)&.captures&.first&.to_i
124
+ response
125
+ end
126
+ # rubocop:enable Metrics/AbcSize
127
+ # rubocop:enable Metrics/CyclomaticComplexity
128
+ # rubocop:enable Metrics/PerceivedComplexity
129
+ end
130
+ end