VibesAI-api 1.3.2__tar.gz → 1.4.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: VibesAI-api
3
- Version: 1.3.2
3
+ Version: 1.4.2
4
4
  Summary: Unofficial Python API client for vibes.ai
5
5
  Author-email: Ashiq Hussain Mir <imsrenpsycho@gmail.com>
6
6
  License: MIT
@@ -29,7 +29,7 @@ This package was built by reverse-engineering the vibes.ai Next.js bundles and o
29
29
 
30
30
  ```bash
31
31
  # 1) Install
32
- pip install -e /home/z/my-project/download/vibes-api
32
+ pip install VibesAI-api
33
33
 
34
34
  # 2) Set your cookie (grab from DevTools → Application → Cookies → vibes.ai)
35
35
  export VIBES_META_SESSION="e60e910a-242a-...-K54E"
@@ -555,7 +555,7 @@ python /home/z/my-project/download/vibes-api/examples/01_generate_video.py
555
555
 
556
556
  ```bash
557
557
  # Install in editable mode
558
- pip install -e /home/z/my-project/download/vibes-api
558
+ pip install VibesAI-api
559
559
 
560
560
  # Run tests (requires VIBES_META_SESSION env var)
561
561
  pytest
@@ -10,7 +10,7 @@ This package was built by reverse-engineering the vibes.ai Next.js bundles and o
10
10
 
11
11
  ```bash
12
12
  # 1) Install
13
- pip install -e /home/z/my-project/download/vibes-api
13
+ pip install VibesAI-api
14
14
 
15
15
  # 2) Set your cookie (grab from DevTools → Application → Cookies → vibes.ai)
16
16
  export VIBES_META_SESSION="e60e910a-242a-...-K54E"
@@ -536,7 +536,7 @@ python /home/z/my-project/download/vibes-api/examples/01_generate_video.py
536
536
 
537
537
  ```bash
538
538
  # Install in editable mode
539
- pip install -e /home/z/my-project/download/vibes-api
539
+ pip install VibesAI-api
540
540
 
541
541
  # Run tests (requires VIBES_META_SESSION env var)
542
542
  pytest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: VibesAI-api
3
- Version: 1.3.2
3
+ Version: 1.4.2
4
4
  Summary: Unofficial Python API client for vibes.ai
5
5
  Author-email: Ashiq Hussain Mir <imsrenpsycho@gmail.com>
6
6
  License: MIT
@@ -29,7 +29,7 @@ This package was built by reverse-engineering the vibes.ai Next.js bundles and o
29
29
 
30
30
  ```bash
31
31
  # 1) Install
32
- pip install -e /home/z/my-project/download/vibes-api
32
+ pip install VibesAI-api
33
33
 
34
34
  # 2) Set your cookie (grab from DevTools → Application → Cookies → vibes.ai)
35
35
  export VIBES_META_SESSION="e60e910a-242a-...-K54E"
@@ -555,7 +555,7 @@ python /home/z/my-project/download/vibes-api/examples/01_generate_video.py
555
555
 
556
556
  ```bash
557
557
  # Install in editable mode
558
- pip install -e /home/z/my-project/download/vibes-api
558
+ pip install VibesAI-api
559
559
 
560
560
  # Run tests (requires VIBES_META_SESSION env var)
561
561
  pytest
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "VibesAI-api"
3
- version = "1.3.2"
3
+ version = "1.4.2"
4
4
  description = "Unofficial Python API client for vibes.ai"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
File without changes
@@ -42,9 +42,9 @@ class TestImports(unittest.TestCase):
42
42
  for name in vibes_api.__all__:
43
43
  self.assertTrue(hasattr(vibes_api, name), f"Missing export: {name}")
44
44
 
45
- def test_version_is_1_3_0(self):
45
+ def test_version_is_1_4_2(self):
46
46
  import vibes_api
47
- self.assertEqual(vibes_api.__version__, "1.3.0")
47
+ self.assertEqual(vibes_api.__version__, "1.4.2")
48
48
 
49
49
  def test_no_syntax_errors(self):
50
50
  import vibes_api.client
@@ -94,7 +94,7 @@ from .ingredients import (
94
94
  )
95
95
  from .composition import Composition
96
96
 
97
- __version__ = "1.3.0"
97
+ __version__ = "1.4.2"
98
98
  __all__ = [
99
99
  # Client
100
100
  "VibesClient",
@@ -182,6 +182,7 @@ class VibesClient:
182
182
  on_cookie_refresh: Optional[callable] = None,
183
183
  on_session_expired: Optional[callable] = None,
184
184
  auto_relogin: bool = False,
185
+ auth_meta_cookies: Optional[str] = None,
185
186
  ):
186
187
  self.base_url = base_url.rstrip("/")
187
188
  self.timeout = timeout
@@ -191,6 +192,7 @@ class VibesClient:
191
192
  self.on_cookie_refresh = on_cookie_refresh
192
193
  self.on_session_expired = on_session_expired
193
194
  self.auto_relogin = auto_relogin
195
+ self.auth_meta_cookies = auth_meta_cookies
194
196
  self._background_thread = None
195
197
  self._background_stop = None
196
198
  self._session_expired = False
@@ -213,11 +215,7 @@ class VibesClient:
213
215
  self._session_expired = False
214
216
 
215
217
  def update_cookie(self, meta_session: str) -> None:
216
- """Update the session cookie at runtime.
217
-
218
- Use this when the ``on_session_expired`` callback fires and
219
- you've obtained a fresh cookie (e.g., from the browser).
220
- """
218
+ """Update the session cookie at runtime."""
221
219
  self._set_cookie(meta_session)
222
220
  if self.on_cookie_refresh:
223
221
  try:
@@ -225,16 +223,24 @@ class VibesClient:
225
223
  except Exception:
226
224
  pass
227
225
 
226
+ def set_auth_meta_cookies(self, cookies: str) -> None:
227
+ """Set the auth.meta.com cookies for automatic re-login.
228
+
229
+ Parameters
230
+ ----------
231
+ cookies : str
232
+ Cookie string from auth.meta.com (e.g.,
233
+ ``"ps_n=1;datr=...;fs=...;locale=en_GB;ps_l=1"``).
234
+ Get these from DevTools → Application → Cookies → auth.meta.com.
235
+ """
236
+ self.auth_meta_cookies = cookies
237
+ self.auto_relogin = True # Enable auto re-login
238
+
228
239
  def get_current_cookie(self) -> str:
229
240
  return self._current_meta_session
230
241
 
231
242
  def _maybe_refresh_cookie(self, resp: requests.Response) -> None:
232
- """Intercept Set-Cookie headers and update the session cookie.
233
-
234
- Note: vibes.ai does NOT send Set-Cookie on regular API responses.
235
- This is kept for completeness — if the server ever adds sliding
236
- session behavior, this will automatically pick it up.
237
- """
243
+ """Intercept Set-Cookie headers and update the session cookie."""
238
244
  if not self.auto_refresh:
239
245
  return
240
246
  set_cookie = resp.headers.get("Set-Cookie") or resp.headers.get("set-cookie")
@@ -258,19 +264,22 @@ class VibesClient:
258
264
 
259
265
  Returns True if the session was successfully restored.
260
266
  """
267
+ # Don't retry re-login if we're already in the middle of one
268
+ if self._session_expired:
269
+ return False
270
+
261
271
  self._session_expired = True
262
272
 
263
- # Call the session expired callback
264
273
  if self.on_session_expired:
265
274
  try:
266
275
  self.on_session_expired(self._current_meta_session)
267
276
  except Exception:
268
277
  pass
269
278
 
270
- # Attempt auto re-login via headless browser
271
- if self.auto_relogin:
279
+ if self.auto_relogin and self.auth_meta_cookies:
272
280
  new_cookie = self._attempt_oidc_relogin()
273
281
  if new_cookie:
282
+ # _set_cookie resets _session_expired to False
274
283
  self._set_cookie(new_cookie)
275
284
  if self.on_cookie_refresh:
276
285
  try:
@@ -279,60 +288,117 @@ class VibesClient:
279
288
  pass
280
289
  return True
281
290
 
291
+ # Reset the flag so future 401s can attempt re-login again
292
+ self._session_expired = False
282
293
  return False
283
294
 
284
295
  def _attempt_oidc_relogin(self) -> Optional[str]:
285
- """Attempt to re-login via the OIDC flow using a headless browser.
296
+ """Attempt to re-login via the OIDC flow using auth.meta.com cookies.
286
297
 
287
- This navigates to ``/api/meta-oidc/start`` which redirects to
288
- Meta's OIDC login. If the browser has an active Meta session
289
- (from facebook.com/meta.com cookies), the flow completes
290
- automatically and a new ``meta_session`` cookie is set.
298
+ Follows the full OIDC redirect chain using requests.get() (not Session)
299
+ to avoid cookie/header interference between steps.
291
300
 
292
- Returns the new cookie value, or None if re-login failed.
301
+ Returns the new meta_session cookie value, or None if re-login failed.
293
302
  """
294
- import subprocess, json as _json
303
+ if not self.auth_meta_cookies:
304
+ return None
295
305
 
296
- try:
297
- # Use agent-browser CLI to navigate to the OIDC start URL
298
- # The browser must have Meta cookies for this to work
299
- result = subprocess.run(
300
- ["agent-browser", "open", f"{self.base_url}/api/meta-oidc/start"],
301
- capture_output=True, text=True, timeout=30,
302
- )
303
- if result.returncode != 0:
304
- return None
306
+ ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
305
307
 
306
- # Wait for redirects to complete
307
- import time as _time
308
- _time.sleep(3)
308
+ for attempt in range(3):
309
+ try:
310
+ # Step 1: Start OIDC flow at vibes.ai
311
+ r1 = requests.get(
312
+ f"{self.base_url}/api/meta-oidc/start",
313
+ headers={"User-Agent": ua, "Cookie": "cookie_ack=true"},
314
+ allow_redirects=False, timeout=15,
315
+ )
316
+ if r1.status_code not in (302, 307):
317
+ if attempt < 2:
318
+ time.sleep(5)
319
+ continue
320
+ return None
321
+ redirect_url = r1.headers.get("Location", "")
322
+ if not redirect_url:
323
+ return None
324
+
325
+ # Extract oauth_csrf_token from Set-Cookie
326
+ csrf_token = None
327
+ for part in r1.headers.get("Set-Cookie", "").split(";"):
328
+ if part.strip().startswith("oauth_csrf_token="):
329
+ csrf_token = part.strip()[len("oauth_csrf_token="):]
330
+ break
331
+
332
+ # Step 2: auth.meta.com (with auth.meta.com cookies only)
333
+ r2 = requests.get(
334
+ redirect_url,
335
+ headers={"User-Agent": ua, "Cookie": self.auth_meta_cookies},
336
+ allow_redirects=False, timeout=15,
337
+ )
338
+ if r2.status_code not in (302, 307):
339
+ # 400 = auth.meta.com rate limited or cookies expired
340
+ # Don't retry — makes rate limiting worse
341
+ return None
342
+ redirect2 = r2.headers.get("Location", "")
343
+ if not redirect2 or "code=" not in redirect2:
344
+ return None
345
+
346
+ # Step 3: auth.meta.ai/ecto (code exchange)
347
+ r3 = requests.get(
348
+ redirect2,
349
+ headers={"User-Agent": ua},
350
+ allow_redirects=False, timeout=15,
351
+ )
352
+ if r3.status_code not in (302, 307):
353
+ return None
354
+ redirect3 = r3.headers.get("Location", "")
355
+ if not redirect3 or "vibes.ai" not in redirect3:
356
+ return None
357
+
358
+ # Step 4: vibes.ai callback (CSRF token only, no old session)
359
+ callback_cookie = "cookie_ack=true"
360
+ if csrf_token:
361
+ callback_cookie += f";oauth_csrf_token={csrf_token}"
362
+ r4 = requests.get(
363
+ redirect3,
364
+ headers={"User-Agent": ua, "Cookie": callback_cookie},
365
+ allow_redirects=False, timeout=15,
366
+ )
309
367
 
310
- # Get the current URL (should be back on vibes.ai)
311
- result = subprocess.run(
312
- ["agent-browser", "get", "url"],
313
- capture_output=True, text=True, timeout=10,
314
- )
315
- current_url = result.stdout.strip()
368
+ # Check Set-Cookie for new meta_session
369
+ set_cookie = r4.headers.get("Set-Cookie", "").replace(",", ";")
370
+ for part in set_cookie.split(";"):
371
+ part = part.strip()
372
+ if part.startswith("meta_session="):
373
+ val = part[len("meta_session="):]
374
+ if val and val != "deleted" and val != self._current_meta_session:
375
+ return val
376
+
377
+ # Step 5: Follow final redirect if no cookie in step 4
378
+ final_redirect = r4.headers.get("Location", "")
379
+ if final_redirect and "error=" not in final_redirect:
380
+ if not final_redirect.startswith("http"):
381
+ final_redirect = f"{self.base_url}{final_redirect}"
382
+ r5 = requests.get(
383
+ final_redirect,
384
+ headers={"User-Agent": ua, "Cookie": "cookie_ack=true"},
385
+ allow_redirects=True, timeout=15,
386
+ )
387
+ set_cookie_5 = r5.headers.get("Set-Cookie", "").replace(",", ";")
388
+ for part in set_cookie_5.split(";"):
389
+ part = part.strip()
390
+ if part.startswith("meta_session="):
391
+ val = part[len("meta_session="):]
392
+ if val and val != "deleted" and val != self._current_meta_session:
393
+ return val
316
394
 
317
- # Check if we're back on vibes.ai (OIDC callback completed)
318
- if "vibes.ai" not in current_url:
319
395
  return None
320
396
 
321
- # Extract the new meta_session cookie
322
- result = subprocess.run(
323
- ["agent-browser", "cookies"],
324
- capture_output=True, text=True, timeout=10,
325
- )
326
- cookies_text = result.stdout.strip()
327
- for line in cookies_text.split("\n"):
328
- line = line.strip()
329
- if line.startswith("meta_session="):
330
- new_cookie = line[len("meta_session="):]
331
- if new_cookie and new_cookie != "deleted":
332
- return new_cookie
333
-
334
- except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
335
- pass
397
+ except Exception:
398
+ if attempt < 2:
399
+ time.sleep(5)
400
+ continue
401
+ pass
336
402
 
337
403
  return None
338
404
 
@@ -449,9 +515,10 @@ class VibesClient:
449
515
  except VibesAPIError as e:
450
516
  if e.status == 401 and not self._session_expired:
451
517
  if self._handle_401():
452
- # Session was restored — retry the request
518
+ # Session was restored — retry the request ONCE
453
519
  resp = self.session.get(self._url(path), params=params, timeout=self.timeout, **kw)
454
520
  return self._check(resp)
521
+ # Re-login failed or not enabled — raise the original error
455
522
  raise
456
523
 
457
524
  def _post(self, path: str, json_body: Optional[dict] = None, **kw) -> dict:
File without changes
File without changes