Spaces:
Sleeping
Sleeping
feat: dynamically extract gemini_bl from homepage on startup
Browse files- gemini_web2api.py +72 -0
gemini_web2api.py
CHANGED
|
@@ -298,6 +298,55 @@ class GeminiHTTPClient:
|
|
| 298 |
else:
|
| 299 |
log("HTTP transport: urllib (no TLS fingerprinting - less stealthy)")
|
| 300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
def post(self, url: str, data: bytes, headers: dict, cookies: dict = None) -> str:
|
| 302 |
"""POST request with Chrome fingerprint. Returns response text."""
|
| 303 |
if self._session:
|
|
@@ -942,6 +991,28 @@ def load_config(path: str):
|
|
| 942 |
log(f"Config loaded: {path}")
|
| 943 |
|
| 944 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 945 |
def main():
|
| 946 |
parser = argparse.ArgumentParser(description="Gemini Web to OpenAI API")
|
| 947 |
parser.add_argument("--port", type=int, default=None)
|
|
@@ -968,6 +1039,7 @@ def main():
|
|
| 968 |
|
| 969 |
# Initialize HTTP client
|
| 970 |
get_http_client()
|
|
|
|
| 971 |
|
| 972 |
class ThreadedServer(ThreadingMixIn, HTTPServer):
|
| 973 |
daemon_threads = True
|
|
|
|
| 298 |
else:
|
| 299 |
log("HTTP transport: urllib (no TLS fingerprinting - less stealthy)")
|
| 300 |
|
| 301 |
+
def get(self, url: str, headers: dict = None, cookies: dict = None) -> str:
|
| 302 |
+
"""GET request with Chrome fingerprint. Returns response text."""
|
| 303 |
+
if self._session:
|
| 304 |
+
return self._get_curl(url, headers, cookies)
|
| 305 |
+
else:
|
| 306 |
+
return self._get_urllib(url, headers, cookies)
|
| 307 |
+
|
| 308 |
+
def _get_curl(self, url: str, headers: dict = None, cookies: dict = None) -> str:
|
| 309 |
+
self._session.cookies.clear()
|
| 310 |
+
proxy = CONFIG.get("proxy")
|
| 311 |
+
proxies = {"http": proxy, "https": proxy} if proxy else None
|
| 312 |
+
resp = self._session.get(
|
| 313 |
+
url,
|
| 314 |
+
headers=dict(headers) if headers else {},
|
| 315 |
+
cookies=cookies or {},
|
| 316 |
+
proxies=proxies,
|
| 317 |
+
allow_redirects=True,
|
| 318 |
+
)
|
| 319 |
+
if resp.status_code != 200:
|
| 320 |
+
raise Exception(f"HTTP {resp.status_code}: {resp.text[:200]}")
|
| 321 |
+
return resp.text
|
| 322 |
+
|
| 323 |
+
def _get_urllib(self, url: str, headers: dict = None, cookies: dict = None) -> str:
|
| 324 |
+
all_headers = dict(headers) if headers else {}
|
| 325 |
+
if cookies:
|
| 326 |
+
cookie_str = "; ".join(f"{k}={v}" for k, v in cookies.items())
|
| 327 |
+
existing = all_headers.get("Cookie", "")
|
| 328 |
+
if existing:
|
| 329 |
+
all_headers["Cookie"] = existing + "; " + cookie_str
|
| 330 |
+
else:
|
| 331 |
+
all_headers["Cookie"] = cookie_str
|
| 332 |
+
req = urllib.request.Request(url, headers=all_headers, method="GET")
|
| 333 |
+
ctx = ssl.create_default_context()
|
| 334 |
+
proxy = CONFIG.get("proxy")
|
| 335 |
+
if proxy:
|
| 336 |
+
opener = urllib.request.build_opener(
|
| 337 |
+
urllib.request.ProxyHandler({"http": proxy, "https": proxy}),
|
| 338 |
+
urllib.request.HTTPSHandler(context=ctx)
|
| 339 |
+
)
|
| 340 |
+
urllib.request.install_opener(opener)
|
| 341 |
+
else:
|
| 342 |
+
opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
|
| 343 |
+
urllib.request.install_opener(opener)
|
| 344 |
+
try:
|
| 345 |
+
with urllib.request.urlopen(req, timeout=CONFIG["request_timeout_sec"]) as resp:
|
| 346 |
+
return resp.read().decode("utf-8")
|
| 347 |
+
except urllib.error.HTTPError as e:
|
| 348 |
+
raise Exception(f"HTTP {e.code}: {e.read().decode('utf-8')[:200]}")
|
| 349 |
+
|
| 350 |
def post(self, url: str, data: bytes, headers: dict, cookies: dict = None) -> str:
|
| 351 |
"""POST request with Chrome fingerprint. Returns response text."""
|
| 352 |
if self._session:
|
|
|
|
| 991 |
log(f"Config loaded: {path}")
|
| 992 |
|
| 993 |
|
| 994 |
+
def update_gemini_bl():
|
| 995 |
+
"""Scrape the gemini.google.com homepage to extract the latest gemini_bl parameter."""
|
| 996 |
+
try:
|
| 997 |
+
log("Fetching latest gemini_bl parameter from gemini.google.com...")
|
| 998 |
+
client = get_http_client()
|
| 999 |
+
headers = build_chrome_headers(method="GET")
|
| 1000 |
+
html = client.get("https://gemini.google.com/app", headers=headers)
|
| 1001 |
+
|
| 1002 |
+
# Look for the bl string in the HTML (usually under cfb2h or SNlM0e)
|
| 1003 |
+
match = re.search(r'"cfb2h":"([^"]+)"', html)
|
| 1004 |
+
if not match:
|
| 1005 |
+
match = re.search(r'"SNlM0e":"([^"]+)"', html)
|
| 1006 |
+
|
| 1007 |
+
if match:
|
| 1008 |
+
CONFIG["gemini_bl"] = match.group(1)
|
| 1009 |
+
log(f"Successfully updated gemini_bl to: {CONFIG['gemini_bl']}")
|
| 1010 |
+
else:
|
| 1011 |
+
log("Warning: Could not extract gemini_bl from homepage. Using fallback.")
|
| 1012 |
+
except Exception as e:
|
| 1013 |
+
log(f"Error fetching gemini_bl: {e}. Using fallback.")
|
| 1014 |
+
|
| 1015 |
+
|
| 1016 |
def main():
|
| 1017 |
parser = argparse.ArgumentParser(description="Gemini Web to OpenAI API")
|
| 1018 |
parser.add_argument("--port", type=int, default=None)
|
|
|
|
| 1039 |
|
| 1040 |
# Initialize HTTP client
|
| 1041 |
get_http_client()
|
| 1042 |
+
update_gemini_bl()
|
| 1043 |
|
| 1044 |
class ThreadedServer(ThreadingMixIn, HTTPServer):
|
| 1045 |
daemon_threads = True
|