howlongtobeat 0.2.2 → 0.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c202be25ee5614c52d8334494939e9dbfb220a74e6f1fb1791b927bbf4ad1ba
4
- data.tar.gz: '00268aa34aa51cd06ce4d0484f7bc380218e52ae881f5b967c03204379c48080'
3
+ metadata.gz: b94e6610de0b221a6a9913bcddc28648dc7fb7bbb79991e6e512dee9a7359cbc
4
+ data.tar.gz: 5daf1faf915be4b7f33ccb1542d801b079cf6386bcb1d8ae56dbb988a81bc4a9
5
5
  SHA512:
6
- metadata.gz: 83c98f7ed7ef66cc500fd3f0005330371711a1b3111df321933fa7bc7b99909e720be9e95a2be354705fe674d8115065513fbe9f6cfa7644cbab01a6698c1065
7
- data.tar.gz: dc27b8455d7c0d99015e9f57a3e5fe72e7efc837e6b3f7d7e528df8e15a7098d18406505064d5228b3e52784553a9ed75f6d2a68cee5ef2c8dd9c8392771076e
6
+ metadata.gz: b7e115db1b5c3ba6444d1fd5e2bf76082f830a6bb55c0c9a55da132b05dc33d364ded99036ef448872221aa99aaea84b2ac17e3d7ee37dfc97ee529f813898f6
7
+ data.tar.gz: dfaf4acae0ed822a1c59900a8c6a78131e4548aff26b0d59f2baaf91505fa533ff23f708d5627de6a84c7b04c2d27679bcfc3164ce31fab0e300e0f03b38f82b
data/CHANGELOG.md CHANGED
@@ -25,6 +25,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
25
25
  ### Security
26
26
  - None
27
27
 
28
+ ## [0.2.3] - 2026-04-15
29
+
30
+ ### Fixed
31
+ - Send `x-hp-key` and `x-hp-val` headers extracted from the `/init` response, matching HLTB's updated API authentication (mirrors Python package v1.0.21)
32
+ - Inject the dynamic key/value pair from `/init` into the search request payload
33
+
28
34
  ## [0.2.2] - 2026-03-24
29
35
 
30
36
  ### Fixed
@@ -70,18 +70,28 @@ module HowLongToBeat
70
70
  end
71
71
  end
72
72
 
73
+ AuthStruct = Struct.new(:auth_token, :auth_key, :auth_value)
74
+
73
75
  class << self
74
- def get_search_request_headers
75
- {
76
+ def get_search_request_headers(auth_struct = nil)
77
+ headers = {
76
78
  'content-type' => 'application/json',
77
79
  'accept' => '*/*',
78
80
  'User-Agent' => random_user_agent,
79
- 'referer' => REFERER_HEADER,
80
- 'origin' => BASE_URL
81
+ 'Referer' => REFERER_HEADER,
82
+ 'Origin' => BASE_URL
81
83
  }
84
+
85
+ if auth_struct
86
+ headers['x-auth-token'] = auth_struct.auth_token.to_s if auth_struct.auth_token
87
+ headers['x-hp-key'] = auth_struct.auth_key.to_s if auth_struct.auth_key
88
+ headers['x-hp-val'] = auth_struct.auth_value.to_s if auth_struct.auth_value
89
+ end
90
+
91
+ headers
82
92
  end
83
93
 
84
- def get_search_request_data(game_name, search_modifiers = SearchModifiers::NONE, page = 1, search_info = nil)
94
+ def get_search_request_data(game_name, search_modifiers = SearchModifiers::NONE, page = 1, search_info = nil, auth_struct = nil)
85
95
  payload = {
86
96
  searchType: 'games',
87
97
  searchTerms: game_name.split,
@@ -123,6 +133,10 @@ module HowLongToBeat
123
133
  payload[:searchOptions][:users][:id] = search_info.api_key
124
134
  end
125
135
 
136
+ if auth_struct&.auth_key && auth_struct&.auth_value
137
+ payload[auth_struct.auth_key] = auth_struct.auth_value
138
+ end
139
+
126
140
  payload.to_json
127
141
  end
128
142
 
@@ -135,11 +149,11 @@ module HowLongToBeat
135
149
  endpoint_candidates = build_endpoint_candidates(search_info&.search_url)
136
150
 
137
151
  endpoint_candidates.each do |endpoint|
138
- token = fetch_search_token(endpoint)
139
- next unless token
152
+ auth_struct = fetch_search_token(endpoint)
153
+ next unless auth_struct
140
154
 
141
- headers = get_search_request_headers.merge('x-auth-token' => token)
142
- payload = get_search_request_data(game_name, search_modifiers, page, search_info)
155
+ headers = get_search_request_headers(auth_struct)
156
+ payload = get_search_request_data(game_name, search_modifiers, page, search_info, auth_struct)
143
157
  search_url = "#{BASE_URL}#{endpoint}"
144
158
  response = make_request(search_url, headers, payload)
145
159
  return response if response
@@ -180,7 +194,20 @@ module HowLongToBeat
180
194
  json = JSON.parse(response) rescue nil
181
195
  return nil unless json.is_a?(Hash)
182
196
 
183
- json['token'] || json.dig('data', 'token') || json['auth_token'] || json['authToken']
197
+ token = json['token'] || json.dig('data', 'token') || json['auth_token'] || json['authToken']
198
+
199
+ auth_key = nil
200
+ auth_value = nil
201
+ json.each do |field_name, field_value|
202
+ lower = field_name.downcase
203
+ if lower.match?(/key/)
204
+ auth_key = field_value
205
+ elsif lower.match?(/val/)
206
+ auth_value = field_value
207
+ end
208
+ end
209
+
210
+ AuthStruct.new(token, auth_key, auth_value)
184
211
  rescue StandardError
185
212
  nil
186
213
  end
@@ -1,3 +1,3 @@
1
1
  module HowLongToBeat
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howlongtobeat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitrii Pashutskii
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-24 00:00:00.000000000 Z
11
+ date: 2026-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri