@@ -65,6 +65,11 @@ def get_search_request_data(game_name: str, search_modifiers: SearchModifiers, p
6565 'min' : 0 ,
6666 'max' : 0
6767 },
68+ 'rangeYear' :
69+ {
70+ 'max' : "" ,
71+ 'min' : ""
72+ },
6873 'gameplay' : {
6974 'perspective' : "" ,
7075 'flow' : "" ,
@@ -73,14 +78,18 @@ def get_search_request_data(game_name: str, search_modifiers: SearchModifiers, p
7378 'modifier' : search_modifiers .value ,
7479 },
7580 'users' : {
76- 'id' : api_key ,
7781 'sortCategory' : "postcount"
7882 },
7983 'filter' : "" ,
8084 'sort' : 0 ,
8185 'randomizer' : 0
8286 }
8387 }
88+
89+ # If api_key is passed add it to the dict
90+ if api_key is not None :
91+ payload ['searchOptions' ]['users' ]['id' ] = api_key
92+
8493 return json .dumps (payload )
8594
8695 @staticmethod
@@ -97,10 +106,17 @@ def send_web_request(game_name: str, search_modifiers: SearchModifiers = SearchM
97106 api_key_result = HTMLRequests .send_website_request_getcode (False )
98107 if api_key_result is None :
99108 api_key_result = HTMLRequests .send_website_request_getcode (True )
100- payload = HTMLRequests .get_search_request_data (game_name , search_modifiers , page , api_key_result )
101- # Make the post request and return the result if is valid
102- search_url_with_key = HTMLRequests .SEARCH_URL
109+ # Make the request
110+ # The main method currently is the call to api/search/key
111+ search_url_with_key = HTMLRequests .SEARCH_URL + "/" + api_key_result
112+ payload = HTMLRequests .get_search_request_data (game_name , search_modifiers , page , None )
103113 resp = requests .post (search_url_with_key , headers = headers , data = payload , timeout = 60 )
114+ if resp .status_code == 200 :
115+ return resp .text
116+ # Try to call with the standard url adding the api key to the user
117+ search_url = HTMLRequests .SEARCH_URL
118+ payload = HTMLRequests .get_search_request_data (game_name , search_modifiers , page , api_key_result )
119+ resp = requests .post (search_url , headers = headers , data = payload , timeout = 60 )
104120 if resp .status_code == 200 :
105121 return resp .text
106122 return None
@@ -119,6 +135,22 @@ async def send_async_web_request(game_name: str, search_modifiers: SearchModifie
119135 api_key_result = await HTMLRequests .async_send_website_request_getcode (False )
120136 if api_key_result is None :
121137 api_key_result = await HTMLRequests .async_send_website_request_getcode (True )
138+ # Make the request
139+ # The main method currently is the call to api/search/key
140+ search_url_with_key = HTMLRequests .SEARCH_URL + "/" + api_key_result
141+ payload = HTMLRequests .get_search_request_data (game_name , search_modifiers , page , None )
142+ async with aiohttp .ClientSession () as session :
143+ async with session .post (search_url_with_key , headers = headers , data = payload ) as resp_with_key :
144+ if resp_with_key is not None and resp_with_key .status == 200 :
145+ return await resp_with_key .text ()
146+ else :
147+ search_url = HTMLRequests .SEARCH_URL
148+ payload = HTMLRequests .get_search_request_data (game_name , search_modifiers , page , api_key_result )
149+ async with session .post (search_url , headers = headers , data = payload ) as resp_user_id :
150+ if resp_user_id is not None and resp_user_id .status == 200 :
151+ return await resp_user_id .text ()
152+ return None
153+
122154 payload = HTMLRequests .get_search_request_data (game_name , search_modifiers , page , api_key_result )
123155 # Make the post request and return the result if is valid
124156 search_url_with_key = HTMLRequests .SEARCH_URL + "/" + api_key_result
@@ -208,11 +240,35 @@ async def async_get_game_title(game_id: int):
208240 return HTMLRequests .__cut_game_title (text )
209241 return None
210242
243+ @staticmethod
244+ def extract_api_from_script (script_content : str ):
245+ """
246+ Function that extract the htlb code to use in the request from the given script
247+ @return: the string of the api key found
248+ """
249+ # Try multiple find one after the other as hltb keep changing format
250+ # Test 1 - The API Key is in the user id in the request json
251+ user_id_api_key_pattern = r'users\s*:\s*{\s*id\s*:\s*"([^"]+)"'
252+ matches = re .findall (user_id_api_key_pattern , script_content )
253+ if matches :
254+ key = '' .join (matches )
255+ return key
256+ # Test 2 - The API Key is in format fetch("/api/search/".concat("X").concat("Y")...
257+ concat_api_key_pattern = r'\/api\/search\/"(?:\.concat\("[^"]*"\))*'
258+ matches = re .findall (concat_api_key_pattern , script_content )
259+ if matches :
260+ matches = str (matches ).split ('.concat' )
261+ matches = [re .sub (r'["\(\)\[\]\']' , '' , match ) for match in matches [1 :]]
262+ key = '' .join (matches )
263+ return key
264+ # Unable to find :(
265+ return None
266+
211267 @staticmethod
212268 def send_website_request_getcode (parse_all_scripts : bool ):
213269 """
214270 Function that send a request to howlongtobeat to scrape the /api/search key
215- @return: The string key to use on /api/search
271+ @return: The string key to use
216272 """
217273 # Make the post request and return the result if is valid
218274 headers = HTMLRequests .get_title_request_headers ()
@@ -230,23 +286,22 @@ def send_website_request_getcode(parse_all_scripts: bool):
230286 script_url = HTMLRequests .BASE_URL + script_url
231287 script_resp = requests .get (script_url , headers = headers , timeout = 60 )
232288 if script_resp .status_code == 200 and script_resp .text is not None :
233- pattern = r'users\s*:\s*{\s*id\s*:\s*"([^"]+)"'
234- matches = re .findall (pattern , script_resp .text )
235- for match in matches :
236- return match
289+ api_key_result = HTMLRequests .extract_api_from_script (script_resp .text )
290+ if api_key_result is not None :
291+ return api_key_result
237292 return None
238293
239294 @staticmethod
240295 async def async_send_website_request_getcode (parse_all_scripts : bool ):
241296 """
242297 Function that send a request to howlongtobeat to scrape the /api/search key
243- @return: The string key to use on /api/search
298+ @return: The string key to use
244299 """
245300 # Make the post request and return the result if is valid
246301 headers = HTMLRequests .get_title_request_headers ()
247302 async with aiohttp .ClientSession () as session :
248303 async with session .get (HTMLRequests .BASE_URL , headers = headers ) as resp :
249- if resp is not None and str ( resp .status ) == " 200" :
304+ if resp is not None and resp .status == 200 :
250305 resp_text = await resp .text ()
251306 # Parse the HTML content using BeautifulSoup
252307 soup = BeautifulSoup (resp_text , 'html.parser' )
@@ -260,12 +315,11 @@ async def async_send_website_request_getcode(parse_all_scripts: bool):
260315 script_url = HTMLRequests .BASE_URL + script_url
261316 async with aiohttp .ClientSession () as session :
262317 async with session .get (script_url , headers = headers ) as script_resp :
263- if script_resp is not None and str ( resp .status ) == " 200" :
318+ if script_resp is not None and resp .status == 200 :
264319 script_resp_text = await script_resp .text ()
265- pattern = r'users\s*:\s*{\s*id\s*:\s*"([^"]+)"'
266- matches = re .findall (pattern , script_resp_text )
267- for match in matches :
268- return match
320+ api_key_result = HTMLRequests .extract_api_from_script (script_resp_text )
321+ if api_key_result is not None :
322+ return api_key_result
269323 else :
270324 return None
271325 else :
0 commit comments