thegamesdb 1.1.2 → 2.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.
data/lib/thegamesdb.rb CHANGED
@@ -1,246 +1,98 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thegamesdb/version'
2
- require 'thegamesdb/config'
4
+ require 'thegamesdb/developers'
5
+ require 'thegamesdb/error'
6
+ require 'thegamesdb/games'
7
+ require 'thegamesdb/genres'
8
+ require 'thegamesdb/platforms'
9
+ require 'thegamesdb/publishers'
10
+ require 'thegamesdb/utils'
3
11
  require 'net/http'
4
12
  require 'json'
5
13
 
6
- # Client for TheGamesDB API (thegamesdb.net)
7
14
  module Gamesdb
8
- BASE_URL = 'https://api.thegamesdb.net/v1/'.freeze
9
- IMAGES_BASE_URL = 'https://legacy.thegamesdb.net/banners/'.freeze
15
+ # Client for TheGamesDB API (thegamesdb.net)
16
+ class Client
17
+ include Gamesdb::Developers
18
+ include Gamesdb::Games
19
+ include Gamesdb::Genres
20
+ include Gamesdb::Platforms
21
+ include Gamesdb::Publishers
10
22
 
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
23
+ BASE_URL = 'https://api.thegamesdb.net/v1/'
24
+ IMAGES_BASE_URL = 'https://legacy.thegamesdb.net/banners/'
26
25
 
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)
26
+ attr_reader :remaining_monthly_allowance, :extra_allowance, :allowance_refresh_timer
39
27
 
40
- data['data']['platforms'].map do |p|
41
- symbolize_keys(p.last)
28
+ def initialize(api_key)
29
+ @api_key = api_key
42
30
  end
43
- end
44
-
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
66
-
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
88
-
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)
112
- end
113
-
114
- data = json_response(url, params)
115
- process_platform_games(data)
116
- end
117
-
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
31
 
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
150
-
151
- def self.process_fanart(data, id)
152
- fanarts = []
153
- fanart = data['images'][id.to_s].select do |a|
154
- a['type'] == 'fanart'
32
+ # Perform request
33
+ #
34
+ # Used by every API endpoint, but can also be used manually.
35
+ #
36
+ # @param url [String] Required
37
+ # @param params [Hash] optional
38
+ #
39
+ # @return [Hash] Parsed JSON response
40
+ def perform_request(url, params = {})
41
+ raise ArgumentError, 'You need to set the API KEY to use the GamesDB API' unless @api_key
42
+
43
+ params = params.merge({ apikey: @api_key })
44
+ uri = URI(BASE_URL + url)
45
+ uri.query = URI.encode_www_form(params)
46
+ response = JSON.parse(Net::HTTP.get_response(uri).body)
47
+ http_error(response) if response['code'] >= 300
48
+
49
+ refresh_allowances(response)
50
+ response
51
+ rescue StandardError => e
52
+ raise e
155
53
  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
- }
165
- end
166
- fanarts
167
- end
168
54
 
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
55
+ private
174
56
 
175
- def self.process_covers(data, id)
176
- covers = {}
177
- boxart = data['images'][id.to_s].select do |a|
178
- a['type'] == "boxart"
179
- 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] = {
184
- url: art['filename'],
185
- resolution: art['resolution'],
186
- width: width,
187
- height: height
188
- }
57
+ def refresh_allowances(response)
58
+ @remaining_monthly_allowance = response['remaining_monthly_allowance']
59
+ @extra_allowance = response['extra_allowance']
60
+ @allowance_refresh_timer = response['allowance_refresh_timer']
189
61
  end
190
- covers
191
- end
192
-
193
- private
194
62
 
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
209
-
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
- }
63
+ # TODO: More granular errors
64
+ def http_error(response)
65
+ raise Gamesdb::Error.new(response['code'], response['status'])
236
66
  end
237
- games
238
- end
239
67
 
240
- def self.symbolize_keys(hash)
241
- hash.keys.each do |key|
242
- hash[key.to_sym] = hash.delete(key)
68
+ # Process games for platform_games
69
+ # rubocop:disable Metrics/AbcSize
70
+ # rubocop:disable Metrics/MethodLength
71
+ def process_platform_games(data)
72
+ data['data']['games'].map do |elem|
73
+ {
74
+ name: elem['game_title'],
75
+ id: elem['id'],
76
+ release_date: elem['release_date'],
77
+ platform: elem['platform'],
78
+ developers: elem['developers'],
79
+ players: elem['players'],
80
+ publishers: elem['publishers'],
81
+ genres: elem['genres'],
82
+ overview: elem['overview'],
83
+ last_updated: elem['last_updated'],
84
+ rating: elem['rating'],
85
+ coop: elem['coop'],
86
+ youtube: elem['youtube'],
87
+ alternates: elem['alternates'],
88
+ image: if (boxart = data.dig('include', 'boxart', 'data', elem['id'].to_s))
89
+ data['include']['boxart']['base_url']['original'] +
90
+ boxart.select { |a| a['side'] == 'front' }.first['filename'] || ''
91
+ end
92
+ }
93
+ end
243
94
  end
244
- hash
95
+ # rubocop:enable Metrics/AbcSize
96
+ # rubocop:enable Metrics/MethodLength
245
97
  end
246
98
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './test_helper'
4
+
5
+ describe 'Gamesdb - client', :vcr do
6
+ let(:client) { Gamesdb::Client.new(ENV['GAMESDB_API_KEY']) }
7
+
8
+ describe 'client' do
9
+ it 'should update allowances' do
10
+ client.games_by_id(1904)
11
+ monthly = client.remaining_monthly_allowance
12
+ client.games_by_id(1527)
13
+ expect(client.remaining_monthly_allowance).must_equal(monthly - 1)
14
+ end
15
+
16
+ describe 'errors' do
17
+ let(:client) { Gamesdb::Client.new('invalid_api_key') }
18
+
19
+ it 'should raise http error' do
20
+ assert_raises Gamesdb::Error do
21
+ client.games_by_id(1904)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './test_helper'
4
+
5
+ describe 'Gamesdb - developers', :vcr do
6
+ let(:client) { Gamesdb::Client.new(ENV['GAMESDB_API_KEY']) }
7
+
8
+ describe 'developers' do
9
+ it 'should return the developers' do
10
+ @developers = client.developers
11
+
12
+ expect(@developers.count.positive?)
13
+ expect(@developers.first.keys).must_equal(['id', 'name'])
14
+ end
15
+ end
16
+ end
data/test/games_test.rb CHANGED
@@ -1,75 +1,89 @@
1
- require_relative './test_helper.rb'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './test_helper'
2
4
 
3
5
  describe 'Gamesdb - games', :vcr do
6
+ let(:client) { Gamesdb::Client.new(ENV['GAMESDB_API_KEY']) }
7
+
4
8
  describe 'game' do
5
9
  before do
6
- @game = Gamesdb.game_by_id(109)
10
+ @game = client.games_by_id(109)
7
11
  end
8
12
 
9
13
  it 'should have a valid id' do
10
- @game[:id].must_be_kind_of Integer
11
- @game[:id].must_equal 109
14
+ expect(@game[:id]).must_be_kind_of Integer
15
+ expect(@game[:id]).must_equal 109
12
16
  end
13
17
 
14
18
  it 'should have valid fields' do
15
- @game[:game_title].must_be_kind_of String
16
- @game[:game_title].length.wont_be :<, 0
19
+ expect(@game[:game_title]).must_be_kind_of String
20
+ expect(@game[:game_title].length).wont_be :<, 0
21
+ end
22
+ end
23
+
24
+ describe 'game by several ids' do
25
+ it 'should return games for a String of ids' do
26
+ @games = client.games_by_id('109,108').sort_by { |g| g[:id] }
27
+
28
+ expect(@games.count).must_equal 2
29
+ expect(@games.first[:id]).must_equal 108
30
+ expect(@games.last[:id]).must_equal 109
17
31
  end
18
32
  end
19
33
 
20
34
  describe 'empty game' do
21
35
  before do
22
- @game = Gamesdb.game_by_id(3)
36
+ @game = client.games_by_id(3)
23
37
  end
24
38
 
25
39
  it 'should return an empty array' do
26
- @game.must_equal []
40
+ expect(@game).must_equal []
27
41
  end
28
42
  end
29
43
 
30
44
  describe 'games by name' do
31
45
  before do
32
- @games_list = Gamesdb.games_by_name('turrican')
46
+ @games_list = client.games_by_name('turrican')
33
47
  end
34
48
 
35
49
  it 'should return a list' do
36
50
  game = @games_list.first
37
- game[:id].must_be_kind_of Integer
38
- game[:name].must_be_kind_of String
39
- game[:platform].must_be_kind_of Integer
40
- game[:release_date].must_be_kind_of String
51
+ expect(game[:id]).must_be_kind_of Integer
52
+ expect(game[:name]).must_be_kind_of String
53
+ expect(game[:platform]).must_be_kind_of Integer
54
+ expect(game[:release_date]).must_be_kind_of String
41
55
  end
42
56
  end
43
57
 
44
58
  describe 'games by name pages' do
45
59
  before do
46
- @first_page = Gamesdb.games_by_name('mario', page: 1)
47
- @second_page = Gamesdb.games_by_name('mario', page: 2)
60
+ @first_page = client.games_by_name('mario', page: 1)
61
+ @second_page = client.games_by_name('mario', page: 2)
48
62
  end
49
63
 
50
64
  it 'should return games in platform by id' do
51
- @first_page.count.wont_be :<, 0
52
- @first_page.count.must_equal 20
65
+ expect(@first_page.count).wont_be :<, 0
66
+ expect(@first_page.count).must_equal 20
53
67
  end
54
68
 
55
69
  it 'should return games in the platform for the second page' do
56
- @second_page.count.wont_be :<, 0
57
- @second_page.count.must_equal 20
58
- (@first_page & @second_page).must_equal []
70
+ expect(@second_page.count).wont_be :<, 0
71
+ expect(@second_page.count).must_equal 20
72
+ expect(@first_page & @second_page).must_equal []
59
73
  end
60
74
  end
61
75
 
62
76
  describe 'games by name and platform' do
63
77
  before do
64
- @games_list = Gamesdb.games_by_name('mario', platform: 7)
78
+ @games_list = client.games_by_name('mario', platform: 7)
65
79
  end
66
80
 
67
81
  it 'should return a list' do
68
82
  @games_list.each do |game|
69
- game[:id].must_be_kind_of Integer
70
- game[:id].must_be_kind_of Integer
71
- game[:name].must_be_kind_of String
72
- game[:platform].must_equal 7
83
+ expect(game[:id]).must_be_kind_of Integer
84
+ expect(game[:id]).must_be_kind_of Integer
85
+ expect(game[:name]).must_be_kind_of String
86
+ expect(game[:platform]).must_equal 7
73
87
  end
74
88
  end
75
89
  end
@@ -77,40 +91,76 @@ describe 'Gamesdb - games', :vcr do
77
91
  describe 'games art', :vcr do
78
92
  describe 'when most of the art is available' do
79
93
  before do
80
- @images = Gamesdb.game_images('216')
94
+ @images = client.games_images('218')
81
95
  end
82
96
 
83
97
  it 'should return logo and boxart' do
84
- @images[:boxart].count.wont_be :<, 0
85
- @images[:logo].must_be_kind_of String
86
- @images[:boxart][:front][:url].must_be_kind_of String
87
- @images[:boxart][:front][:width].must_be_kind_of String
88
- @images[:boxart][:front][:height].must_be_kind_of String
89
- @images[:boxart][:front][:resolution].must_be_kind_of String
98
+ expect(@images[:boxart].count).wont_be :<, 0
99
+ expect(@images[:logo]).must_be_kind_of String
100
+ expect(@images[:boxart][:front][:url]).must_be_kind_of String
101
+ expect(@images[:boxart][:front][:width]).must_be_kind_of String
102
+ expect(@images[:boxart][:front][:height]).must_be_kind_of String
103
+ expect(@images[:boxart][:front][:resolution]).must_be_kind_of String
90
104
  end
91
105
 
92
106
  it 'should return screenshots' do
93
- @images[:screenshot].count.wont_be :<, 0
94
- @images[:screenshot].first.must_be_kind_of Hash
95
- @images[:screenshot].first[:filename].must_be_kind_of String
107
+ expect(@images[:screenshot].count).wont_be :<, 0
108
+ expect(@images[:screenshot].first).must_be_kind_of Hash
109
+ expect(@images[:screenshot].first[:filename]).must_be_kind_of String
96
110
  end
97
111
 
98
112
  it 'should return fanart' do
99
- @images[:fanart].count.wont_be :<, 0
100
- @images[:fanart].first[:url].must_be_kind_of String
101
- @images[:fanart].first[:width].must_be_kind_of String
102
- @images[:fanart].first[:height].must_be_kind_of String
103
- @images[:fanart].first[:resolution].must_be_kind_of String
113
+ expect(@images[:fanart].count).wont_be :<, 0
114
+ expect(@images[:fanart].first[:url]).must_be_kind_of String
115
+ expect(@images[:fanart].first[:width]).must_be_kind_of String
116
+ expect(@images[:fanart].first[:height]).must_be_kind_of String
117
+ expect(@images[:fanart].first[:resolution]).must_be_kind_of String
104
118
  end
105
119
  end
106
120
 
107
121
  describe 'when some art is missing' do
108
122
  before do
109
- @images = Gamesdb.game_images(65238)
123
+ @images = client.games_images(65_238)
110
124
  end
111
125
 
112
126
  it 'should return an empty array' do
113
- @images.must_be_kind_of Hash
127
+ expect(@images).must_be_kind_of Hash
128
+ end
129
+ end
130
+
131
+ describe 'update games' do
132
+ it 'should return updates' do
133
+ updates = client.games_update(1)
134
+
135
+ expect(updates[:updates].count).must_equal 100
136
+ expect(updates[:updates].first).must_equal(
137
+ {
138
+ 'edit_id' => 2,
139
+ 'game_id' => 38_113,
140
+ 'timestamp' => '2018-06-28 15:20:54',
141
+ 'type' => 'boxart',
142
+ 'value' => 'boxart/front/38113-1.jpg'
143
+ }
144
+ )
145
+ expect(updates[:updates].last).must_equal(
146
+ {
147
+ 'edit_id' => 101,
148
+ 'game_id' => 22_208,
149
+ 'timestamp' => '2018-06-29 01:36:38',
150
+ 'type' => 'rating',
151
+ 'value' => 'E - Everyone'
152
+ }
153
+ )
154
+ expect(updates[:previous_page]).must_be_nil
155
+ expect(updates[:next_page]).must_equal 2
156
+ end
157
+
158
+ it 'should return updates when using a page parameter' do
159
+ updates = client.games_update(1, { page: 1_000 })
160
+
161
+ expect(updates[:updates].count).must_equal 100
162
+ expect(updates[:previous_page]).must_equal 999
163
+ expect(updates[:next_page]).must_equal 1001
114
164
  end
115
165
  end
116
166
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './test_helper'
4
+
5
+ describe 'Gamesdb - genres', :vcr do
6
+ let(:client) { Gamesdb::Client.new(ENV['GAMESDB_API_KEY']) }
7
+
8
+ describe 'genres' do
9
+ it 'should return the genres' do
10
+ @genres = client.genres
11
+
12
+ expect(@genres.count).must_equal 29
13
+ expect(@genres.min_by { |g| g['id'] }).must_equal({ 'id' => 1, 'name' => 'Action' })
14
+ end
15
+ end
16
+ end