thegamesdb 1.1.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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