Skip to content

Commit 55864ed

Browse files
committed
Fix Search discovery regex fails against current site (Turbopack bundling)
Fixes #59 - now get all scripts and fix regex endpoint parse
1 parent e650bf5 commit 55864ed

1 file changed

Lines changed: 25 additions & 43 deletions

File tree

‎howlongtobeatpy/howlongtobeatpy/HTMLRequests.py‎

Lines changed: 25 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -36,41 +36,27 @@ def __init__(self, script_content: str):
3636

3737
def __extract_search_url_script(self, script_content: str):
3838
"""
39-
Function that finds the 'fetch' call using 'method: "POST"',
40-
extracts the base endpoint path, and returns the full '/api/path'
41-
string (e.g., "/api/search" or "/api/finder").
42-
43-
This avoids relying on the exact string by confirming
39+
Function that finds the 'fetch' call using 'method: "POST"',
40+
extracts the endpoint path, and returns the full '/api/...'
41+
string (e.g., "/api/search/site" or "/api/finder/v2").
42+
43+
This avoids relying on the exact string by confirming
4444
the use of the POST method, which identifies the actual search endpoint.
45-
46-
@return: The full API endpoint string (e.g., "/api/search") or None.
45+
46+
@return: The full API endpoint string (e.g., "/api/search/site") or None.
4747
"""
48-
# Pattern explanation:
49-
# 1. Capture Group 1: Matches the path suffix (e.g., "search", "find", "finder").
50-
# 2. Ensures the request options contain 'method: "POST"' to filter out the GET init call.
5148
pattern = re.compile(
52-
# Capture Group 1: The path suffix after /api/ (e.g., "search", "find", "finder")
5349
r'fetch\s*\(\s*["\']/api/([a-zA-Z0-9_/]+)[^"\']*["\']\s*,\s*{[^}]*method:\s*["\']POST["\'][^}]*}',
5450
re.DOTALL | re.IGNORECASE
5551
)
56-
52+
5753
match = pattern.search(script_content)
58-
54+
5955
if match:
60-
# Example captured string: "search", "find", "finder", or "find/v2"
61-
path_suffix = match.group(1)
62-
63-
# Determine the root path (e.g., "search" from "search/v2")
64-
# This ensures we get the base endpoint name even if sub-paths are used.
65-
if '/' in path_suffix:
66-
base_path = path_suffix.split('/')[0]
67-
else:
68-
base_path = path_suffix
69-
70-
# Accept any endpoint variant (search, find, finder, etc.)
71-
full_endpoint = f"/api/{base_path}"
72-
return full_endpoint
73-
56+
# e.g. "search/site", "find", "finder/v2"
57+
path_suffix = match.group(1).rstrip('/')
58+
return f"/api/{path_suffix}"
59+
7460
return None
7561

7662

@@ -199,9 +185,7 @@ def send_web_request(game_name: str, search_modifiers: SearchModifiers = SearchM
199185
ua = UserAgent()
200186
request_user_agent = ua.random.strip()
201187
# Retrieve the updated URL
202-
search_info_data = HTMLRequests.send_website_request_getcode(False, request_user_agent)
203-
if search_info_data is None or search_info_data.search_url is None:
204-
search_info_data = HTMLRequests.send_website_request_getcode(True, request_user_agent)
188+
search_info_data = HTMLRequests.send_website_request_getcode(request_user_agent)
205189
# Retrieve the request auth token
206190
auth_struct = None
207191
if search_info_data is not None and search_info_data.search_url is not None:
@@ -232,9 +216,7 @@ async def send_async_web_request(game_name: str, search_modifiers: SearchModifie
232216
ua = UserAgent()
233217
request_user_agent = ua.random.strip()
234218
# Retrieve the updated URL
235-
search_info_data = await HTMLRequests.async_send_website_request_getcode(False, request_user_agent)
236-
if search_info_data is None or search_info_data.search_url is None:
237-
search_info_data = await HTMLRequests.async_send_website_request_getcode(True, request_user_agent)
219+
search_info_data = await HTMLRequests.async_send_website_request_getcode(request_user_agent)
238220
# Retrieve the request auth token
239221
auth_struct = None
240222
if search_info_data is not None and search_info_data.search_url is not None:
@@ -343,7 +325,7 @@ async def async_get_game_title(game_id: int):
343325
return None
344326

345327
@staticmethod
346-
def send_website_request_getcode(parse_all_scripts: bool, user_agent):
328+
def send_website_request_getcode(user_agent):
347329
"""
348330
Function that send a request to howlongtobeat to scrape the correct search url
349331
@return: The search informations to use in the request
@@ -356,10 +338,10 @@ def send_website_request_getcode(parse_all_scripts: bool, user_agent):
356338
soup = BeautifulSoup(resp.text, HTMLRequests.HTML_PARSER)
357339
# Find all <script> tags with a src attribute containing the substring
358340
scripts = soup.find_all('script', src=True)
359-
if parse_all_scripts:
360-
matching_scripts = [script['src'] for script in scripts]
361-
else:
362-
matching_scripts = [script['src'] for script in scripts if '_app-' in script['src']]
341+
matching_scripts = [
342+
script['src'] for script in scripts
343+
if '/_next/static/chunks/' in script['src']
344+
]
363345
for script_url in matching_scripts:
364346
script_url = HTMLRequests.BASE_URL + script_url
365347
script_resp = requests.get(script_url, headers=headers, timeout=60)
@@ -370,7 +352,7 @@ def send_website_request_getcode(parse_all_scripts: bool, user_agent):
370352
return None
371353

372354
@staticmethod
373-
async def async_send_website_request_getcode(parse_all_scripts: bool, user_agent):
355+
async def async_send_website_request_getcode(user_agent):
374356
"""
375357
Function that send a request to howlongtobeat to scrape the correct search url
376358
@return: The search informations to use in the request
@@ -386,10 +368,10 @@ async def async_send_website_request_getcode(parse_all_scripts: bool, user_agent
386368
soup = BeautifulSoup(resp_text, HTMLRequests.HTML_PARSER)
387369
# Find all <script> tags with a src attribute containing the substring
388370
scripts = soup.find_all('script', src=True)
389-
if parse_all_scripts:
390-
matching_scripts = [script['src'] for script in scripts]
391-
else:
392-
matching_scripts = [script['src'] for script in scripts if '_app-' in script['src']]
371+
matching_scripts = [
372+
script['src'] for script in scripts
373+
if '/_next/static/chunks/' in script['src']
374+
]
393375
for script_url in matching_scripts:
394376
script_url = HTMLRequests.BASE_URL + script_url
395377
async with aiohttp.ClientSession() as session:

0 commit comments

Comments
 (0)