thegamesdb 0.2.0 → 2.0.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 +4 -4
- data/.github/workflows/master.yml +26 -0
- data/.github/workflows/rubocop.yml +20 -0
- data/.gitignore +2 -1
- data/.rubocop.yml +39 -0
- data/CHANGELOG.md +74 -0
- data/Gemfile +2 -0
- data/README.md +339 -191
- data/Rakefile +3 -0
- data/lib/thegamesdb.rb +124 -158
- data/lib/thegamesdb/developers.rb +17 -0
- data/lib/thegamesdb/games.rb +130 -0
- data/lib/thegamesdb/genres.rb +13 -0
- data/lib/thegamesdb/images.rb +7 -0
- data/lib/thegamesdb/platforms.rb +106 -0
- data/lib/thegamesdb/publishers.rb +15 -0
- data/lib/thegamesdb/version.rb +3 -1
- data/test/client_test.rb +16 -0
- data/test/developers_test.rb +16 -0
- data/test/games_test.rb +137 -33
- data/test/genres_test.rb +16 -0
- data/test/platform_test.rb +102 -49
- data/test/publishers_test.rb +16 -0
- data/test/test_helper.rb +4 -3
- data/thegamesdb.gemspec +16 -5
- metadata +34 -27
- data/.travis.yml +0 -9
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'
|
data/lib/thegamesdb.rb
CHANGED
@@ -1,180 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thegamesdb/version'
|
2
|
-
require '
|
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'
|
10
|
+
require 'json'
|
4
11
|
|
5
|
-
# Client for TheGamesDB API (thegamesdb.net)
|
6
12
|
module Gamesdb
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# For information on how to attain a valid platform slug see `platform`
|
15
|
-
#
|
16
|
-
# == Returns:
|
17
|
-
# Array of Hashes with games info
|
18
|
-
def self.platform_games(platform)
|
19
|
-
url = platform.is_a?(Numeric) ? 'GetPlatformGames.php' : 'PlatformGames.php'
|
20
|
-
data = xml_response(url, platform: platform)
|
21
|
-
process_platform_games(data)
|
22
|
-
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
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
# Parameters: none
|
28
|
-
#
|
29
|
-
# == Returns:
|
30
|
-
# Array of Hashes with platforms info
|
31
|
-
#
|
32
|
-
def self.platforms
|
33
|
-
url = 'GetPlatformsList.php'
|
34
|
-
data = xml_response(url)
|
35
|
-
platforms = []
|
36
|
-
|
37
|
-
data[:Platforms].first.last.each do |platform|
|
38
|
-
platforms << { name: platform[:name], id: platform[:id].to_i, slug: platform[:alias] }
|
39
|
-
end
|
40
|
-
platforms
|
41
|
-
end
|
21
|
+
BASE_URL = 'https://api.thegamesdb.net/v1/'
|
22
|
+
IMAGES_BASE_URL = 'https://legacy.thegamesdb.net/banners/'
|
42
23
|
|
43
|
-
|
44
|
-
# specified Platform ID.
|
45
|
-
# http://wiki.thegamesdb.net/index.php/GetPlatform
|
46
|
-
#
|
47
|
-
# Parameters:
|
48
|
-
# - id - (int) The numeric ID of the platform in the GamesDB database
|
49
|
-
#
|
50
|
-
# == Returns:
|
51
|
-
# Hash with platform info
|
52
|
-
#
|
53
|
-
def self.platform(id)
|
54
|
-
url = 'GetPlatform.php'
|
55
|
-
data = xml_response(url, id: id)[:Platform]
|
56
|
-
data[:name] = data.delete(:Platform)
|
57
|
-
data
|
58
|
-
end
|
24
|
+
attr_reader :remaining_monthly_allowance, :extra_allowance, :allowance_refresh_timer
|
59
25
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#
|
64
|
-
# Parameters:
|
65
|
-
# - id - (int) Game id
|
66
|
-
#
|
67
|
-
# == Returns:
|
68
|
-
# Hash with game info
|
69
|
-
#
|
70
|
-
def self.game(id)
|
71
|
-
url = 'GetGame.php'
|
72
|
-
data = xml_response(url, id: id)
|
73
|
-
game = process_game(data[:Game])
|
74
|
-
game
|
75
|
-
end
|
26
|
+
def initialize(api_key)
|
27
|
+
@api_key = api_key
|
28
|
+
end
|
76
29
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
50
|
+
end
|
96
51
|
|
97
|
-
|
98
|
-
# locations specific to the requested game id in the database. It
|
99
|
-
# also lists the resolution of any images available. Scrapers can be
|
100
|
-
# set to use a minimum or maximum resolution for specific images
|
101
|
-
# http://wiki.thegamesdb.net/index.php/GetArt
|
102
|
-
#
|
103
|
-
# Parameters
|
104
|
-
# - id - (integer) The numeric ID of the game in Gamesdb that you
|
105
|
-
# like to fetch artwork details for
|
106
|
-
#
|
107
|
-
# == Returns:
|
108
|
-
# Hash with game art info: fanart (array), boxart (Hash, :front,
|
109
|
-
# :back), screenshots (array), fanart (array)
|
110
|
-
#
|
111
|
-
def self.art(id)
|
112
|
-
url = 'GetArt.php'
|
113
|
-
data = xml_response(url, id: id)[:Images]
|
114
|
-
data[:logo] = data[:clearlogo].last
|
115
|
-
data[:boxart] = process_covers(data[:boxart])
|
116
|
-
data
|
117
|
-
end
|
52
|
+
private
|
118
53
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
height: art[:height],
|
129
|
-
thumb: art[:thumb]
|
130
|
-
}
|
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
|
59
|
+
|
60
|
+
def process_logo(data, id)
|
61
|
+
logo = data['images'][id.to_s].select { |a| a['type'] == 'clearlogo' }
|
62
|
+
logo.empty? ? '' : logo.first['filename']
|
131
63
|
end
|
132
|
-
covers
|
133
|
-
end
|
134
64
|
|
135
|
-
|
65
|
+
def process_fanart(data, id)
|
66
|
+
fanart = select_images(data, id, 'fanart')
|
67
|
+
return [] if fanart.empty?
|
136
68
|
|
137
|
-
|
138
|
-
|
139
|
-
uri = URI(BASE_URL + url)
|
140
|
-
uri.query = URI.encode_www_form(params)
|
141
|
-
request = Net::HTTP.get_response(uri)
|
142
|
-
Ox.load(request.body, mode: :hash)[:Data]
|
143
|
-
end
|
69
|
+
fanart.map { |art| build_individual_fanart(art) }
|
70
|
+
end
|
144
71
|
|
145
|
-
|
146
|
-
|
147
|
-
|
72
|
+
def process_covers(data, id)
|
73
|
+
covers = {}
|
74
|
+
boxart = select_images(data, id, 'boxart')
|
75
|
+
return [] if boxart.empty?
|
148
76
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
154
82
|
end
|
155
|
-
games
|
156
|
-
end
|
157
83
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
game[:platform] = game.delete(:Platform)
|
163
|
-
game
|
164
|
-
end
|
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
|
165
88
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
url: art.original.nodes[0],
|
173
|
-
width: art.nodes.first.attributes[:width],
|
174
|
-
height: art.nodes.first.attributes[:height],
|
175
|
-
thumb: art.thumb.text
|
89
|
+
def art_structure(art, width, height)
|
90
|
+
{
|
91
|
+
url: art['filename'],
|
92
|
+
resolution: art['resolution'],
|
93
|
+
width: width,
|
94
|
+
height: height
|
176
95
|
}
|
177
96
|
end
|
178
|
-
|
97
|
+
|
98
|
+
def process_screenshots(data, id)
|
99
|
+
select_images(data, id, 'screenshot').map { |b| symbolize_keys(b) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def select_images(data, id, image_type)
|
103
|
+
data['images'][id.to_s].select do |a|
|
104
|
+
a['type'] == image_type
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
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
|
144
|
+
end
|
179
145
|
end
|
180
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
|