gemini-webapi 1.17.3__py3-none-any.whl → 1.18.1__py3-none-any.whl

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.
@@ -5,7 +5,7 @@ import orjson as json
5
5
  from ..constants import GRPC
6
6
  from ..exceptions import APIError
7
7
  from ..types import Gem, GemJar, RPCData
8
- from ..utils import running, logger
8
+ from ..utils import extract_json_from_response, get_nested_value, logger
9
9
 
10
10
 
11
11
  class GemMixin:
@@ -41,8 +41,9 @@ class GemMixin:
41
41
 
42
42
  return self._gems
43
43
 
44
- @running(retry=2)
45
- async def fetch_gems(self, include_hidden: bool = False, **kwargs) -> GemJar:
44
+ async def fetch_gems(
45
+ self, include_hidden: bool = False, language: str = "en", **kwargs
46
+ ) -> GemJar:
46
47
  """
47
48
  Get a list of available gems from gemini, including system predefined gems and user-created custom gems.
48
49
 
@@ -54,6 +55,8 @@ class GemMixin:
54
55
  include_hidden: `bool`, optional
55
56
  There are some predefined gems that by default are not shown to users (and therefore may not work properly).
56
57
  Set this parameter to `True` to include them in the fetched gem list.
58
+ language: `str`, optional
59
+ Language code for the gems to fetch. Default is 'en'.
57
60
 
58
61
  Returns
59
62
  -------
@@ -65,12 +68,16 @@ class GemMixin:
65
68
  [
66
69
  RPCData(
67
70
  rpcid=GRPC.LIST_GEMS,
68
- payload="[4]" if include_hidden else "[3]",
71
+ payload=(
72
+ f"[4,['{language}'],0]"
73
+ if include_hidden
74
+ else f"[3,['{language}'],0]"
75
+ ),
69
76
  identifier="system",
70
77
  ),
71
78
  RPCData(
72
79
  rpcid=GRPC.LIST_GEMS,
73
- payload="[2]",
80
+ payload=f"[2,['{language}'],0]",
74
81
  identifier="custom",
75
82
  ),
76
83
  ],
@@ -78,24 +85,32 @@ class GemMixin:
78
85
  )
79
86
 
80
87
  try:
81
- response_json = json.loads(response.text.split("\n")[2])
88
+ response_json = extract_json_from_response(response.text)
82
89
 
83
90
  predefined_gems, custom_gems = [], []
84
91
 
85
92
  for part in response_json:
86
- if part[-1] == "system":
87
- predefined_gems = json.loads(part[2])[2]
88
- elif part[-1] == "custom":
89
- if custom_gems_container := json.loads(part[2]):
90
- custom_gems = custom_gems_container[2]
93
+ try:
94
+ identifier = get_nested_value(part, [-1])
95
+ part_body_str = get_nested_value(part, [2])
96
+ if not part_body_str:
97
+ continue
98
+
99
+ part_body = json.loads(part_body_str)
100
+ if identifier == "system":
101
+ predefined_gems = get_nested_value(part_body, [2], [])
102
+ elif identifier == "custom":
103
+ custom_gems = get_nested_value(part_body, [2], [])
104
+ except json.JSONDecodeError:
105
+ continue
91
106
 
92
107
  if not predefined_gems and not custom_gems:
93
108
  raise Exception
94
109
  except Exception:
95
110
  await self.close()
96
- logger.debug(f"Invalid response: {response.text}")
111
+ logger.debug(f"Unexpected response data structure: {response.text}")
97
112
  raise APIError(
98
- "Failed to fetch gems. Invalid response data received. Client will try to re-initialize on next request."
113
+ "Failed to fetch gems. Unexpected response data structure. Client will try to re-initialize on next request."
99
114
  )
100
115
 
101
116
  self._gems = GemJar(
@@ -131,7 +146,6 @@ class GemMixin:
131
146
 
132
147
  return self._gems
133
148
 
134
- @running(retry=2)
135
149
  async def create_gem(self, name: str, prompt: str, description: str = "") -> Gem:
136
150
  """
137
151
  Create a new custom gem.
@@ -175,19 +189,26 @@ class GemMixin:
175
189
  [],
176
190
  ]
177
191
  ]
178
- ).decode(),
192
+ ).decode("utf-8"),
179
193
  )
180
194
  ]
181
195
  )
182
196
 
183
197
  try:
184
- response_json = json.loads(response.text.split("\n")[2])
185
- gem_id = json.loads(response_json[0][2])[0]
198
+ response_json = extract_json_from_response(response.text)
199
+ part_body_str = get_nested_value(response_json, [0, 2], verbose=True)
200
+ if not part_body_str:
201
+ raise Exception
202
+
203
+ part_body = json.loads(part_body_str)
204
+ gem_id = get_nested_value(part_body, [0], verbose=True)
205
+ if not gem_id:
206
+ raise Exception
186
207
  except Exception:
187
208
  await self.close()
188
- logger.debug(f"Invalid response: {response.text}")
209
+ logger.debug(f"Unexpected response data structure: {response.text}")
189
210
  raise APIError(
190
- "Failed to create gem. Invalid response data received. Client will try to re-initialize on next request."
211
+ "Failed to create gem. Unexpected response data structure. Client will try to re-initialize on next request."
191
212
  )
192
213
 
193
214
  return Gem(
@@ -198,7 +219,6 @@ class GemMixin:
198
219
  predefined=False,
199
220
  )
200
221
 
201
- @running(retry=2)
202
222
  async def update_gem(
203
223
  self, gem: Gem | str, name: str, prompt: str, description: str = ""
204
224
  ) -> Gem:
@@ -253,7 +273,7 @@ class GemMixin:
253
273
  0,
254
274
  ],
255
275
  ]
256
- ).decode(),
276
+ ).decode("utf-8"),
257
277
  )
258
278
  ]
259
279
  )
@@ -266,7 +286,6 @@ class GemMixin:
266
286
  predefined=False,
267
287
  )
268
288
 
269
- @running(retry=2)
270
289
  async def delete_gem(self, gem: Gem | str, **kwargs) -> None:
271
290
  """
272
291
  Delete a custom gem.
@@ -283,6 +302,10 @@ class GemMixin:
283
302
  gem_id = gem
284
303
 
285
304
  await self._batch_execute(
286
- [RPCData(rpcid=GRPC.DELETE_GEM, payload=json.dumps([gem_id]).decode())],
305
+ [
306
+ RPCData(
307
+ rpcid=GRPC.DELETE_GEM, payload=json.dumps([gem_id]).decode("utf-8")
308
+ )
309
+ ],
287
310
  **kwargs,
288
311
  )
@@ -18,6 +18,7 @@ class GRPC(StrEnum):
18
18
  # Chat methods
19
19
  LIST_CHATS = "MaZiqc"
20
20
  READ_CHAT = "hNvQHb"
21
+ DELETE_CHAT = "GzXR5e"
21
22
 
22
23
  # Gem methods
23
24
  LIST_GEMS = "CNgdBe"
@@ -25,6 +26,9 @@ class GRPC(StrEnum):
25
26
  UPDATE_GEM = "kHv0Vd"
26
27
  DELETE_GEM = "UXcSJb"
27
28
 
29
+ # Activity methods
30
+ BARD_ACTIVITY = "ESY5D"
31
+
28
32
 
29
33
  class Headers(Enum):
30
34
  GEMINI = {
@@ -32,7 +36,7 @@ class Headers(Enum):
32
36
  "Host": "gemini.google.com",
33
37
  "Origin": "https://gemini.google.com",
34
38
  "Referer": "https://gemini.google.com/",
35
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
39
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
36
40
  "X-Same-Domain": "1",
37
41
  }
38
42
  ROTATE_COOKIES = {
@@ -46,21 +50,21 @@ class Model(Enum):
46
50
  G_3_0_PRO = (
47
51
  "gemini-3.0-pro",
48
52
  {
49
- "x-goog-ext-525001261-jspb": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4]]'
53
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4],null,null,1]'
50
54
  },
51
55
  False,
52
56
  )
53
- G_2_5_PRO = (
54
- "gemini-2.5-pro",
57
+ G_3_0_FLASH = (
58
+ "gemini-3.0-flash",
55
59
  {
56
- "x-goog-ext-525001261-jspb": '[1,null,null,null,"4af6c7f5da75d65d",null,null,0,[4]]'
60
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"fbb127bbb056c959",null,null,0,[4],null,null,1]'
57
61
  },
58
62
  False,
59
63
  )
60
- G_2_5_FLASH = (
61
- "gemini-2.5-flash",
64
+ G_3_0_FLASH_THINKING = (
65
+ "gemini-3.0-flash-thinking",
62
66
  {
63
- "x-goog-ext-525001261-jspb": '[1,null,null,null,"9ec249fc9ad08861",null,null,0,[4]]'
67
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"5bf011840784117a",null,null,0,[4],null,null,1]'
64
68
  },
65
69
  False,
66
70
  )
@@ -24,7 +24,9 @@ class Candidate(BaseModel):
24
24
 
25
25
  rcid: str
26
26
  text: str
27
+ text_delta: str | None = None
27
28
  thoughts: str | None = None
29
+ thoughts_delta: str | None = None
28
30
  web_images: list[WebImage] = []
29
31
  generated_images: list[GeneratedImage] = []
30
32
 
@@ -1,8 +1,9 @@
1
1
  import re
2
2
  from pathlib import Path
3
3
  from datetime import datetime
4
+ from typing import Any
4
5
 
5
- from httpx import AsyncClient, HTTPError
6
+ from httpx import AsyncClient, Cookies, HTTPError
6
7
  from pydantic import BaseModel, field_validator
7
8
 
8
9
  from ..utils import logger
@@ -39,7 +40,7 @@ class Image(BaseModel):
39
40
  self,
40
41
  path: str = "temp",
41
42
  filename: str | None = None,
42
- cookies: dict | None = None,
43
+ cookies: dict | Cookies | None = None,
43
44
  verbose: bool = False,
44
45
  skip_invalid_filename: bool = False,
45
46
  ) -> str | None:
@@ -81,7 +82,7 @@ class Image(BaseModel):
81
82
  return None
82
83
 
83
84
  async with AsyncClient(
84
- follow_redirects=True, cookies=cookies, proxy=self.proxy
85
+ http2=True, follow_redirects=True, cookies=cookies, proxy=self.proxy
85
86
  ) as client:
86
87
  response = await client.get(self.url)
87
88
  if response.status_code == 200:
@@ -121,16 +122,16 @@ class GeneratedImage(Image):
121
122
 
122
123
  Parameters
123
124
  ----------
124
- cookies: `dict`
125
+ cookies: `dict | httpx.Cookies`
125
126
  Cookies used for requesting the content of the generated image, inherit from GeminiClient object or manually set.
126
127
  Should contain valid "__Secure-1PSID" and "__Secure-1PSIDTS" values.
127
128
  """
128
129
 
129
- cookies: dict[str, str]
130
+ cookies: Any
130
131
 
131
132
  @field_validator("cookies")
132
133
  @classmethod
133
- def validate_cookies(cls, v: dict) -> dict:
134
+ def validate_cookies(cls, v: Any) -> Any:
134
135
  if len(v) == 0:
135
136
  raise ValueError(
136
137
  "GeneratedImage is designed to be initialized with same cookies as GeminiClient."
@@ -142,7 +143,7 @@ class GeneratedImage(Image):
142
143
  self,
143
144
  path: str = "temp",
144
145
  filename: str | None = None,
145
- cookies: dict | None = None,
146
+ cookies: dict | Cookies | None = None,
146
147
  verbose: bool = False,
147
148
  skip_invalid_filename: bool = False,
148
149
  full_size: bool = True,
@@ -32,10 +32,18 @@ class ModelOutput(BaseModel):
32
32
  def text(self) -> str:
33
33
  return self.candidates[self.chosen].text
34
34
 
35
+ @property
36
+ def text_delta(self) -> str:
37
+ return self.candidates[self.chosen].text_delta or ""
38
+
35
39
  @property
36
40
  def thoughts(self) -> str | None:
37
41
  return self.candidates[self.chosen].thoughts
38
42
 
43
+ @property
44
+ def thoughts_delta(self) -> str:
45
+ return self.candidates[self.chosen].thoughts_delta or ""
46
+
39
47
  @property
40
48
  def images(self) -> list[Image]:
41
49
  return self.candidates[self.chosen].images
@@ -1,14 +1,9 @@
1
1
  # flake8: noqa
2
2
 
3
- from asyncio import Task
4
-
5
3
  from .decorators import running
6
4
  from .get_access_token import get_access_token
7
5
  from .load_browser_cookies import load_browser_cookies
8
6
  from .logger import logger, set_log_level
9
- from .parsing import extract_json_from_response, get_nested_value
7
+ from .parsing import extract_json_from_response, get_nested_value, parse_stream_frames
10
8
  from .rotate_1psidts import rotate_1psidts
11
9
  from .upload_file import upload_file, parse_file_name
12
-
13
-
14
- rotate_tasks: dict[str, Task] = {}
@@ -1,13 +1,18 @@
1
1
  import asyncio
2
2
  import functools
3
+ import inspect
3
4
  from collections.abc import Callable
4
5
 
5
- from ..exceptions import APIError, ImageGenerationError
6
+ from ..exceptions import APIError
7
+
8
+
9
+ DELAY_FACTOR = 5
6
10
 
7
11
 
8
12
  def running(retry: int = 0) -> Callable:
9
13
  """
10
14
  Decorator to check if GeminiClient is running before making a request.
15
+ Supports both regular async functions and async generators.
11
16
 
12
17
  Parameters
13
18
  ----------
@@ -16,38 +21,78 @@ def running(retry: int = 0) -> Callable:
16
21
  """
17
22
 
18
23
  def decorator(func):
19
- @functools.wraps(func)
20
- async def wrapper(client, *args, retry=retry, **kwargs):
21
- try:
22
- if not client._running:
23
- await client.init(
24
- timeout=client.timeout,
25
- auto_close=client.auto_close,
26
- close_delay=client.close_delay,
27
- auto_refresh=client.auto_refresh,
28
- refresh_interval=client.refresh_interval,
29
- verbose=False,
30
- )
31
- if client._running:
32
- return await func(client, *args, **kwargs)
33
-
34
- # Should not reach here
35
- raise APIError(
36
- f"Invalid function call: GeminiClient.{func.__name__}. Client initialization failed."
37
- )
38
- else:
24
+ if inspect.isasyncgenfunction(func):
25
+
26
+ @functools.wraps(func)
27
+ async def wrapper(client, *args, current_retry=None, **kwargs):
28
+ if current_retry is None:
29
+ current_retry = retry
30
+
31
+ try:
32
+ if not client._running:
33
+ await client.init(
34
+ timeout=client.timeout,
35
+ auto_close=client.auto_close,
36
+ close_delay=client.close_delay,
37
+ auto_refresh=client.auto_refresh,
38
+ refresh_interval=client.refresh_interval,
39
+ verbose=client.verbose,
40
+ )
41
+
42
+ if not client._running:
43
+ raise APIError(
44
+ f"Invalid function call: GeminiClient.{func.__name__}. Client initialization failed."
45
+ )
46
+
47
+ async for item in func(client, *args, **kwargs):
48
+ yield item
49
+ except APIError:
50
+ if current_retry > 0:
51
+ delay = (retry - current_retry + 1) * DELAY_FACTOR
52
+ await asyncio.sleep(delay)
53
+ async for item in wrapper(
54
+ client, *args, current_retry=current_retry - 1, **kwargs
55
+ ):
56
+ yield item
57
+ else:
58
+ raise
59
+
60
+ return wrapper
61
+ else:
62
+
63
+ @functools.wraps(func)
64
+ async def wrapper(client, *args, current_retry=None, **kwargs):
65
+ if current_retry is None:
66
+ current_retry = retry
67
+
68
+ try:
69
+ if not client._running:
70
+ await client.init(
71
+ timeout=client.timeout,
72
+ auto_close=client.auto_close,
73
+ close_delay=client.close_delay,
74
+ auto_refresh=client.auto_refresh,
75
+ refresh_interval=client.refresh_interval,
76
+ verbose=client.verbose,
77
+ )
78
+
79
+ if not client._running:
80
+ raise APIError(
81
+ f"Invalid function call: GeminiClient.{func.__name__}. Client initialization failed."
82
+ )
83
+
39
84
  return await func(client, *args, **kwargs)
40
- except APIError as e:
41
- # Image generation takes too long, only retry once
42
- if isinstance(e, ImageGenerationError):
43
- retry = min(1, retry)
85
+ except APIError:
86
+ if current_retry > 0:
87
+ delay = (retry - current_retry + 1) * DELAY_FACTOR
88
+ await asyncio.sleep(delay)
44
89
 
45
- if retry > 0:
46
- await asyncio.sleep(1)
47
- return await wrapper(client, *args, retry=retry - 1, **kwargs)
90
+ return await wrapper(
91
+ client, *args, current_retry=current_retry - 1, **kwargs
92
+ )
48
93
 
49
- raise
94
+ raise
50
95
 
51
- return wrapper
96
+ return wrapper
52
97
 
53
98
  return decorator
@@ -4,7 +4,7 @@ import asyncio
4
4
  from asyncio import Task
5
5
  from pathlib import Path
6
6
 
7
- from httpx import AsyncClient, Response
7
+ from httpx import AsyncClient, Cookies, Response
8
8
 
9
9
  from ..constants import Endpoint, Headers
10
10
  from ..exceptions import AuthError
@@ -13,27 +13,30 @@ from .logger import logger
13
13
 
14
14
 
15
15
  async def send_request(
16
- cookies: dict, proxy: str | None = None
17
- ) -> tuple[Response | None, dict]:
16
+ cookies: dict | Cookies, proxy: str | None = None
17
+ ) -> tuple[Response | None, Cookies]:
18
18
  """
19
19
  Send http request with provided cookies.
20
20
  """
21
21
 
22
22
  async with AsyncClient(
23
+ http2=True,
23
24
  proxy=proxy,
24
25
  headers=Headers.GEMINI.value,
25
26
  cookies=cookies,
26
27
  follow_redirects=True,
27
- verify=False,
28
28
  ) as client:
29
- response = await client.get(Endpoint.INIT.value)
29
+ response = await client.get(Endpoint.INIT)
30
30
  response.raise_for_status()
31
- return response, cookies
31
+ return response, client.cookies
32
32
 
33
33
 
34
34
  async def get_access_token(
35
- base_cookies: dict, proxy: str | None = None, verbose: bool = False
36
- ) -> tuple[str, dict]:
35
+ base_cookies: dict | Cookies,
36
+ proxy: str | None = None,
37
+ verbose: bool = False,
38
+ verify: bool = True,
39
+ ) -> tuple[str, Cookies, str | None, str | None]:
37
40
  """
38
41
  Send a get request to gemini.google.com for each group of available cookies and return
39
42
  the value of "SNlM0e" as access token on the first successful request.
@@ -45,19 +48,19 @@ async def get_access_token(
45
48
 
46
49
  Parameters
47
50
  ----------
48
- base_cookies : `dict`
51
+ base_cookies : `dict | httpx.Cookies`
49
52
  Base cookies to be used in the request.
50
53
  proxy: `str`, optional
51
54
  Proxy URL.
52
55
  verbose: `bool`, optional
53
56
  If `True`, will print more infomation in logs.
57
+ verify: `bool`, optional
58
+ Whether to verify SSL certificates.
54
59
 
55
60
  Returns
56
61
  -------
57
- `str`
58
- Access token.
59
- `dict`
60
- Cookies of the successful request.
62
+ `tuple[str, str | None, str | None, Cookies]`
63
+ By order: access token; build label; session id; cookies of the successful request.
61
64
 
62
65
  Raises
63
66
  ------
@@ -65,18 +68,23 @@ async def get_access_token(
65
68
  If all requests failed.
66
69
  """
67
70
 
68
- async with AsyncClient(proxy=proxy, follow_redirects=True, verify=False) as client:
69
- response = await client.get(Endpoint.GOOGLE.value)
71
+ async with AsyncClient(
72
+ http2=True, proxy=proxy, follow_redirects=True, verify=verify
73
+ ) as client:
74
+ response = await client.get(Endpoint.GOOGLE)
70
75
 
71
- extra_cookies = {}
76
+ extra_cookies = Cookies()
72
77
  if response.status_code == 200:
73
78
  extra_cookies = response.cookies
74
79
 
75
80
  tasks = []
76
81
 
77
82
  # Base cookies passed directly on initializing client
83
+ # We use a Jar to merge extra_cookies and base_cookies safely (preserving domains)
78
84
  if "__Secure-1PSID" in base_cookies and "__Secure-1PSIDTS" in base_cookies:
79
- tasks.append(Task(send_request({**extra_cookies, **base_cookies}, proxy=proxy)))
85
+ jar = Cookies(extra_cookies)
86
+ jar.update(base_cookies)
87
+ tasks.append(Task(send_request(jar, proxy=proxy)))
80
88
  elif verbose:
81
89
  logger.debug(
82
90
  "Skipping loading base cookies. Either __Secure-1PSID or __Secure-1PSIDTS is not provided."
@@ -88,18 +96,25 @@ async def get_access_token(
88
96
  and Path(GEMINI_COOKIE_PATH)
89
97
  or (Path(__file__).parent / "temp")
90
98
  )
91
- if "__Secure-1PSID" in base_cookies:
92
- filename = f".cached_1psidts_{base_cookies['__Secure-1PSID']}.txt"
99
+
100
+ # Safely get __Secure-1PSID value
101
+ if isinstance(base_cookies, Cookies):
102
+ secure_1psid = base_cookies.get(
103
+ "__Secure-1PSID", domain=".google.com"
104
+ ) or base_cookies.get("__Secure-1PSID")
105
+ else:
106
+ secure_1psid = base_cookies.get("__Secure-1PSID")
107
+
108
+ if secure_1psid:
109
+ filename = f".cached_1psidts_{secure_1psid}.txt"
93
110
  cache_file = cache_dir / filename
94
111
  if cache_file.is_file():
95
112
  cached_1psidts = cache_file.read_text()
96
113
  if cached_1psidts:
97
- cached_cookies = {
98
- **extra_cookies,
99
- **base_cookies,
100
- "__Secure-1PSIDTS": cached_1psidts,
101
- }
102
- tasks.append(Task(send_request(cached_cookies, proxy=proxy)))
114
+ jar = Cookies(extra_cookies)
115
+ jar.update(base_cookies)
116
+ jar.set("__Secure-1PSIDTS", cached_1psidts, domain=".google.com")
117
+ tasks.append(Task(send_request(jar, proxy=proxy)))
103
118
  elif verbose:
104
119
  logger.debug("Skipping loading cached cookies. Cache file is empty.")
105
120
  elif verbose:
@@ -110,12 +125,11 @@ async def get_access_token(
110
125
  for cache_file in cache_files:
111
126
  cached_1psidts = cache_file.read_text()
112
127
  if cached_1psidts:
113
- cached_cookies = {
114
- **extra_cookies,
115
- "__Secure-1PSID": cache_file.stem[16:],
116
- "__Secure-1PSIDTS": cached_1psidts,
117
- }
118
- tasks.append(Task(send_request(cached_cookies, proxy=proxy)))
128
+ jar = Cookies(extra_cookies)
129
+ psid = cache_file.stem[16:]
130
+ jar.set("__Secure-1PSID", psid, domain=".google.com")
131
+ jar.set("__Secure-1PSIDTS", cached_1psidts, domain=".google.com")
132
+ tasks.append(Task(send_request(jar, proxy=proxy)))
119
133
  valid_caches += 1
120
134
 
121
135
  if valid_caches == 0 and verbose:
@@ -174,13 +188,20 @@ async def get_access_token(
174
188
  for i, future in enumerate(asyncio.as_completed(tasks)):
175
189
  try:
176
190
  response, request_cookies = await future
177
- match = re.search(r'"SNlM0e":"(.*?)"', response.text)
178
- if match:
191
+ snlm0e = re.search(r'"SNlM0e":\s*"(.*?)"', response.text)
192
+ if snlm0e:
193
+ cfb2h = re.search(r'"cfb2h":\s*"(.*?)"', response.text)
194
+ fdrfje = re.search(r'"FdrFJe":\s*"(.*?)"', response.text)
179
195
  if verbose:
180
196
  logger.debug(
181
197
  f"Init attempt ({i + 1}/{len(tasks)}) succeeded. Initializing client..."
182
198
  )
183
- return match.group(1), request_cookies
199
+ return (
200
+ snlm0e.group(1),
201
+ cfb2h.group(1) if cfb2h else None,
202
+ fdrfje.group(1) if fdrfje else None,
203
+ request_cookies,
204
+ )
184
205
  elif verbose:
185
206
  logger.debug(
186
207
  f"Init attempt ({i + 1}/{len(tasks)}) failed. Cookies invalid."