howlongtobeat 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef5a11f2d4eb82426ed1c0e6312a824381798af62a77425fa3bd39671a9d7080
4
- data.tar.gz: b16931671e18bfd16a02a4072dd124a089549fa9dbd0af7bcddb76e693bbe388
3
+ metadata.gz: dade03f5e29eecc43b780f4721d6843fee4033a1a5d374ad65adbfcf9510cd21
4
+ data.tar.gz: 51ce4b254c4f8c8923ba56b5dacb97e8981437b449de4285c7e29f1856aa111e
5
5
  SHA512:
6
- metadata.gz: b39fcf8b55b15bac316188010468528576b2c0abbec0ca69c9a29eed9fa3be32cfc38fdfb5c03425705522a9807f87da76170821cb0adbb7eb2c0bca0835e8cc
7
- data.tar.gz: 77bfcbc196915c9c253db36313ed97ed404d627d14232fd2ca1439307d46a367b7dc8a29865d6deff8a68bf2b4662bc720487dfd41e72958a7f317231f82475d
6
+ metadata.gz: 9fb70b3b35d2a6465f37a063f2c34338dd82284ccc1e0aa684361d0a28d8028a129db8b1ea653f66fd24f9f4206284f835c5dce3f02d32e47ff3b2158b1f50ec
7
+ data.tar.gz: b90d809192fd9b481c837f75c53684dca44b788dcf6fbe2aaa4558203e891fb958e3e018d1e5a69b2c521cd62ede9af314539714f27c39de4f837222b0231d15
data/CHANGELOG.md CHANGED
@@ -8,11 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
  ### Added
11
- - Initial release
12
- - Basic search functionality
13
- - Search by ID functionality
14
- - Search modifiers support
15
- - Similarity filtering
11
+ - None
16
12
 
17
13
  ### Changed
18
14
  - None
@@ -29,10 +25,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
29
25
  ### Security
30
26
  - None
31
27
 
32
- ## [0.1.0] - 2025-03-06
28
+ ## [0.2.0] - 2025-12-13
33
29
 
34
- - Initial release
30
+ ### Changed
31
+ - Updated authentication to use token-based API (`/api/search/init`)
32
+ - Improved request headers to match API requirements
33
+
34
+ ### Fixed
35
+ - Fixed search functionality to work with HowLongToBeat.com's updated API
35
36
 
36
37
  ## [0.1.2] - 2025-03-06
37
38
 
38
39
  - Update Ruby requirement version
40
+
41
+ ## [0.1.0] - 2025-03-06
42
+
43
+ - Initial release
44
+ - Basic search functionality
45
+ - Search by ID functionality
46
+ - Search modifiers support
47
+ - Similarity filtering
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # HowLongToBeat Ruby API
2
2
 
3
+ ![CI](https://github.com/dpashutskii/howlongtobeat/actions/workflows/ci.yml/badge.svg)
4
+
3
5
  A simple Ruby API to read data from howlongtobeat.com.
4
6
 
5
7
  It is inspired by [ScrappyCocco's HowLongToBeat Python API](https://github.com/ScrappyCocco/HowLongToBeat-PythonAPI).
@@ -35,15 +37,37 @@ results = hltb.search("The Witcher 3")
35
37
 
36
38
  The `search` method returns an array of possible games, or `nil` if no results were found or there was an error in the request.
37
39
 
38
- Each result is a `HowLongToBeat::Entry` object containing:
39
- - `id`: The game's ID on HowLongToBeat
40
- - `name`: The game's name
41
- - `description`: Game description
42
- - `main_story`: Main story completion time
43
- - `main_plus_sides`: Main story + side quests completion time
44
- - `completionist`: 100% completion time
45
- - `all_styles`: Average time across all playstyles
46
- - `similarity`: How closely the game name matches the search query (0.0 to 1.0)
40
+ Each result is an [`HowLongToBeatEntry`](https://github.com/dpashutskii/howlongtobeat/blob/main/lib/howlongtobeat/how_long_to_beat_entry.rb) object containing:
41
+
42
+ #### Base Game Details
43
+ - `game_id`: The game's ID on HowLongToBeat (Integer)
44
+ - `game_name`: The game's name (String)
45
+ - `game_alias`: Alternative names for the game (String)
46
+ - `game_type`: Type of content (e.g., "game", "dlc") (String)
47
+ - `game_image_url`: URL to the game's cover image (String)
48
+ - `game_web_link`: URL to the game's HowLongToBeat page (String)
49
+ - `review_score`: User review score (Integer, 0-100)
50
+ - `profile_dev`: Developer information (String)
51
+ - `profile_platforms`: Available platforms (Array of Strings)
52
+ - `release_world`: Release year (Integer)
53
+
54
+ #### Completion Times (all in hours)
55
+ - `main_story`: Main story completion time (Float)
56
+ - `main_extra`: Main story + side quests completion time (Float)
57
+ - `completionist`: 100% completion time (Float)
58
+ - `all_styles`: Average time across all playstyles (Float)
59
+ - `coop_time`: Co-op gameplay time (Float)
60
+ - `mp_time`: Multiplayer gameplay time (Float)
61
+
62
+ #### Complexity Flags
63
+ - `complexity_lvl_combine`: Combined gameplay complexity (Boolean)
64
+ - `complexity_lvl_sp`: Single-player complexity (Boolean)
65
+ - `complexity_lvl_co`: Co-op complexity (Boolean)
66
+ - `complexity_lvl_mp`: Multiplayer complexity (Boolean)
67
+
68
+ #### Other
69
+ - `similarity`: How closely the game name matches the search query (Float, 0.0 to 1.0)
70
+ - `json_content`: Raw JSON data from the API (Hash)
47
71
 
48
72
  ### Search by ID
49
73
 
@@ -53,7 +77,7 @@ You can also search for a game using its HowLongToBeat ID:
53
77
  result = hltb.search_from_id(10270) # The Witcher 3: Wild Hunt
54
78
  ```
55
79
 
56
- This returns a single `Entry` object or `nil` if not found.
80
+ This returns a single [`HowLongToBeatEntry`](https://github.com/dpashutskii/howlongtobeat/blob/main/lib/howlongtobeat/how_long_to_beat_entry.rb) object or `nil` if not found.
57
81
 
58
82
  ### Search Modifiers
59
83
 
@@ -85,6 +109,10 @@ hltb = HowLongToBeat::HowLongToBeat.new(0.7)
85
109
  results = hltb.search("The Witcher 3")
86
110
  ```
87
111
 
112
+ ## Who's Using It
113
+
114
+ This gem was originally created for and is being used by [SearchToPlay](https://searchtoplay.com), a platform that helps gamers discover and track their gaming journey.
115
+
88
116
  ## Development
89
117
 
90
118
  After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests.
@@ -66,7 +66,8 @@ module HowLongToBeat
66
66
  'content-type' => 'application/json',
67
67
  'accept' => '*/*',
68
68
  'User-Agent' => random_user_agent,
69
- 'referer' => REFERER_HEADER
69
+ 'referer' => REFERER_HEADER,
70
+ 'origin' => BASE_URL
70
71
  }
71
72
  end
72
73
 
@@ -116,27 +117,12 @@ module HowLongToBeat
116
117
  end
117
118
 
118
119
  def send_web_request(game_name, search_modifiers = SearchModifiers::NONE, page = 1)
119
- headers = get_search_request_headers
120
- search_info = send_website_request_getcode(false)
121
- search_info ||= send_website_request_getcode(true)
122
-
123
- return nil unless search_info&.api_key
124
-
125
- if search_info.search_url
126
- search_url = "#{BASE_URL}/#{search_info.search_url}"
127
- else
128
- search_url = SEARCH_URL
129
- end
120
+ token = fetch_search_token
121
+ return nil unless token
130
122
 
131
- # Try with API key in URL
132
- search_url_with_key = "#{search_url}/#{search_info.api_key}"
123
+ headers = get_search_request_headers.merge('x-auth-token' => token)
133
124
  payload = get_search_request_data(game_name, search_modifiers, page)
134
- response = make_request(search_url_with_key, headers, payload)
135
- return response if response
136
-
137
- # Fallback to standard search with API key in payload
138
- payload = get_search_request_data(game_name, search_modifiers, page, search_info)
139
- make_request(search_url, headers, payload)
125
+ make_request(SEARCH_URL, headers, payload)
140
126
  end
141
127
 
142
128
  def get_game_title(game_id)
@@ -156,6 +142,18 @@ module HowLongToBeat
156
142
 
157
143
  private
158
144
 
145
+ def fetch_search_token
146
+ url = "#{BASE_URL}/api/search/init?t=#{Time.now.to_i}"
147
+ headers = get_title_request_headers
148
+ response = make_get_request(url, headers)
149
+ return nil unless response
150
+
151
+ json = JSON.parse(response) rescue nil
152
+ json.is_a?(Hash) ? json['token'] : nil
153
+ rescue StandardError
154
+ nil
155
+ end
156
+
159
157
  def send_website_request_getcode(parse_all_scripts)
160
158
  headers = get_title_request_headers
161
159
  response = make_get_request(BASE_URL, headers)
@@ -1,3 +1,3 @@
1
1
  module HowLongToBeat
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
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.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitrii Pashutskii
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-06 00:00:00.000000000 Z
11
+ date: 2025-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -120,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  - !ruby/object:Gem::Version
121
121
  version: '0'
122
122
  requirements: []
123
- rubygems_version: 3.5.16
123
+ rubygems_version: 3.5.22
124
124
  signing_key:
125
125
  specification_version: 4
126
126
  summary: Ruby client for HowLongToBeat.com