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.
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,180 +1,146 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thegamesdb/version'
2
- require 'ox'
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
- BASE_URL = 'http://legacy.thegamesdb.net/api/'
8
- IMAGES_BASE_URL = 'http://legacy.thegamesdb.net/banners/'
9
-
10
- # Method for listing platform's games
11
- # http://wiki.thegamesdb.net/index.php?title=GetPlatformGames
12
- #
13
- # Parameters: platform id (int) || platform slug (string)
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
- # Method for listing platforms
25
- # http://wiki.thegamesdb.net/index.php?title=GetPlatformsList
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
- # This API feature returns a set of metadata and artwork data for a
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
- # Method for getting game info
61
- # TODO: name and platform parameters (for search)
62
- # http://wiki.thegamesdb.net/index.php?title=GetGame
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
- # The GetGamesList API search returns a listing of games matched up
78
- # with loose search terms.
79
- # http://wiki.thegamesdb.net/index.php/GetGamesList
80
- #
81
- # Parameters:
82
- # - name (required)
83
- # - TODO: platform (optional): filters results by platform (not implemented)
84
- # - TODO: genre (optional): filters results by genre (not
85
- # implemented)
86
- #
87
- # == Returns:
88
- # Hash with game info: id, name (not-unique), release_date,
89
- # platform
90
- #
91
- def self.games_list(name)
92
- url = 'GetGamesList.php'
93
- data = xml_response(url, name: name)
94
- data[:Game].map { |game| process_game(game) }
95
- end
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
- # This API feature returns a list of available artwork types and
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
- def self.process_covers(boxart)
120
- boxart = boxart.flatten
121
-
122
- covers = {}
123
- boxart.each do |art|
124
- next unless art.is_a?(Hash)
125
- covers[art[:side].to_sym] = {
126
- url: art[:thumb].gsub('thumb/',''),
127
- width: art[:width],
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
- private
65
+ def process_fanart(data, id)
66
+ fanart = select_images(data, id, 'fanart')
67
+ return [] if fanart.empty?
136
68
 
137
- # Api call and xml parsing
138
- def self.xml_response(url, params = {})
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
- # Process games for platform_games
146
- def self.process_platform_games(data)
147
- games = []
72
+ def process_covers(data, id)
73
+ covers = {}
74
+ boxart = select_images(data, id, 'boxart')
75
+ return [] if boxart.empty?
148
76
 
149
- data.first.last.each do |elem|
150
- name = elem[:GameTitle]
151
- id = elem[:id].to_i
152
- date = elem.dig(:ReleaseDate)
153
- games << { name: name, id: id, release_date: date }
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
- def self.process_game(game)
159
- game[:id] = game[:id].to_i
160
- game[:name] = game.delete(:GameTitle)
161
- game[:title] = game[:name]
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
- # Method for processing the fan art and screenshots into a uniform
167
- # collection with url, width, height and thumb url
168
- def self.images_from_nodes(data)
169
- images = []
170
- data.each do |art|
171
- images << {
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
- images
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