capiumbrowser 1.2.1__tar.gz → 1.2.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.
Files changed (46) hide show
  1. {capiumbrowser-1.2.1/capiumbrowser.egg-info → capiumbrowser-1.2.2}/PKG-INFO +1 -1
  2. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/_version.py +7 -6
  3. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/browser.py +19 -7
  4. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/browser_async.py +29 -0
  5. capiumbrowser-1.2.2/capiumbrowser/channels.json +40 -0
  6. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/config.py +10 -6
  7. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2/capiumbrowser.egg-info}/PKG-INFO +1 -1
  8. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/pyproject.toml +1 -1
  9. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_config_args.py +5 -2
  10. capiumbrowser-1.2.1/capiumbrowser/channels.json +0 -36
  11. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/LICENSE +0 -0
  12. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/MANIFEST.in +0 -0
  13. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/README.md +0 -0
  14. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/__init__.py +0 -0
  15. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/__main__.py +0 -0
  16. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/_driver.py +0 -0
  17. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/android-phones.json +0 -0
  18. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/browser-brands.json +0 -0
  19. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/desktop.py +0 -0
  20. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/errors.py +0 -0
  21. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/human/__init__.py +0 -0
  22. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/human/behavior.py +0 -0
  23. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/ios-phones.json +0 -0
  24. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/licensing/__init__.py +0 -0
  25. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/licensing/client.py +0 -0
  26. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/licensing/download.py +0 -0
  27. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/licensing/readiness.py +0 -0
  28. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/mobile.py +0 -0
  29. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/network/__init__.py +0 -0
  30. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser/network/proxy.py +0 -0
  31. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/SOURCES.txt +0 -0
  32. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/dependency_links.txt +0 -0
  33. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/entry_points.txt +0 -0
  34. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/requires.txt +0 -0
  35. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/top_level.txt +0 -0
  36. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/setup.cfg +0 -0
  37. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_channels_manifest.py +0 -0
  38. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_desktop_brands.py +0 -0
  39. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_download_contract.py +0 -0
  40. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_geoip_flags.py +0 -0
  41. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_ios_profiles.py +0 -0
  42. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_launch_viewport.py +0 -0
  43. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_license_readiness.py +0 -0
  44. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_mobile_profiles.py +0 -0
  45. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_proxy.py +0 -0
  46. {capiumbrowser-1.2.1 → capiumbrowser-1.2.2}/tests/test_version_tables_consistent.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capiumbrowser
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: Drop-in stealth browser SDK on top of Playwright (Capium Chromium).
5
5
  Author: Capzy
6
6
  License: Capzy capiumbrowser SDK License
@@ -17,17 +17,18 @@ import json
17
17
  import os
18
18
  import platform as _platform
19
19
 
20
- __version__ = "1.2.1"
20
+ __version__ = "1.2.2"
21
21
 
22
22
  # Used only if channels.json can't be read (corrupt/removed install). ensure_binary() raises a
23
23
  # clear error long before the "unsupported platform" case matters; this keeps lookups total.
24
+ # Reported version per platform: Windows .85, macOS/Linux .84 (engine 155.0.8059.84 on all).
24
25
  _FALLBACK_VERSIONS = {
25
- "windows-x64": "155.0.8059.39", # Chromium 155 release candidate on all supported hosts
26
- "macos-arm64": "155.0.8059.39",
27
- "linux-x64": "155.0.8059.39",
28
- "linux-arm64": "155.0.8059.39", # declared; not yet published (see channels.json)
26
+ "windows-x64": "155.0.8059.85",
27
+ "macos-arm64": "155.0.8059.84",
28
+ "linux-x64": "155.0.8059.84",
29
+ "linux-arm64": "155.0.8059.84", # declared; not yet published (see channels.json)
29
30
  }
30
- _DEFAULT_BINARY_VERSION = "155.0.8059.39"
31
+ _DEFAULT_BINARY_VERSION = "155.0.8059.84"
31
32
 
32
33
 
33
34
  def _load_stable():
@@ -171,6 +171,7 @@ def _fit_window_to_screen(context):
171
171
  (works for persistent contexts, which have no browser() object). This keeps screen~=viewport in
172
172
  headless/container (avoids the browserscan 'screen != viewport' VM tell) AND stays coherent in
173
173
  headed (Chrome clamps to the real display -> a normal viewport<screen window). Best-effort."""
174
+ s = None
174
175
  try:
175
176
  page = context.pages[0] if context.pages else None
176
177
  if not page:
@@ -185,6 +186,12 @@ def _fit_window_to_screen(context):
185
186
  "left": 0, "top": 0, "width": aw, "height": ah, "windowState": "normal"}})
186
187
  except Exception:
187
188
  pass
189
+ finally:
190
+ if s is not None:
191
+ try:
192
+ s.detach()
193
+ except Exception:
194
+ pass
188
195
 
189
196
 
190
197
  def _wrap_close(obj, pw):
@@ -415,6 +422,13 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
415
422
  raise
416
423
  _clear_status_file(status_path)
417
424
  ctx._capium_seed = seed
425
+ # Fit every headed desktop brand before its engine check returns the context.
426
+ # Mobile profiles retain their catalog viewport and startup bounds.
427
+ # Size the window to the seed's screen for desktop personas -- in HEADLESS too, so the
428
+ # viewport is a coherent per-seed maximized size (e.g. 1920x1080 screen -> ~1904x911)
429
+ # instead of the tiny startup window. Mobile keeps its device viewport.
430
+ if not (platform in ("android", "ios") and stealth_args):
431
+ _fit_window_to_screen(ctx)
418
432
  if desktop.normalize_brand(browser_brand) != "chrome":
419
433
  ctx = _wrap_close(ctx, pw)
420
434
  try:
@@ -431,11 +445,6 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
431
445
  ctx.close()
432
446
  raise
433
447
  return ctx
434
- # Headed persistent launches fit the window to the selected screen. Headless
435
- # keeps its startup bounds (800x600 by default) independent of the seeded screen;
436
- # callers can choose larger window bounds explicitly.
437
- if not headless:
438
- _fit_window_to_screen(ctx)
439
448
  return _wrap_close(ctx, pw)
440
449
 
441
450
 
@@ -457,8 +466,11 @@ def launch_context(seed=None, platform="windows", headless=False, url=None,
457
466
  from . import human as _human
458
467
  _human.humanize(context, preset=human_preset)
459
468
  page = context.pages[0] if context.pages else context.new_page()
460
- if not headless and not getattr(browser, "_capium_mobile", False):
461
- fit_window(browser, context, page) # modest window <= spoofed screen
469
+ if not getattr(browser, "_capium_mobile", False):
470
+ if headless:
471
+ _fit_window_to_screen(context) # per-seed screen fit (coherent viewport, not the tiny startup window)
472
+ else:
473
+ fit_window(browser, context, page) # modest window <= spoofed screen
462
474
  if url:
463
475
  page.goto(url, wait_until="domcontentloaded", timeout=60000)
464
476
  return browser, context, page
@@ -163,6 +163,29 @@ async def _require_mobile_engine_async(context, mobile_device=None, seed=0, plat
163
163
  await page.close()
164
164
 
165
165
 
166
+ async def _fit_window_to_screen_async(context):
167
+ """Fit a headed desktop window to its available screen without emulation."""
168
+ session = None
169
+ try:
170
+ page = context.pages[0] if context.pages else await context.new_page()
171
+ screen = await page.evaluate("()=>({w:screen.availWidth,h:screen.availHeight})")
172
+ if not (screen and screen.get("w") and screen.get("h")):
173
+ return
174
+ session = await context.new_cdp_session(page)
175
+ window_id = (await session.send("Browser.getWindowForTarget"))["windowId"]
176
+ await session.send("Browser.setWindowBounds", {"windowId": window_id, "bounds": {
177
+ "left": 0, "top": 0, "width": int(screen["w"]),
178
+ "height": int(screen["h"]), "windowState": "normal"}})
179
+ except Exception:
180
+ pass
181
+ finally:
182
+ if session is not None:
183
+ try:
184
+ await session.detach()
185
+ except Exception:
186
+ pass
187
+
188
+
166
189
  async def launch_persistent_context_async(user_data_dir, seed=None, platform="windows",
167
190
  headless=False, proxy=None, geoip=None, args=None,
168
191
  stealth_args=True, timezone=None, locale=None,
@@ -220,6 +243,10 @@ async def launch_persistent_context_async(user_data_dir, seed=None, platform="wi
220
243
  raise
221
244
  _clear_status_file(status_path)
222
245
  ctx._capium_seed = seed
246
+ # Desktop personas: fit to the seed's screen in HEADLESS too (coherent per-seed viewport,
247
+ # not the tiny startup window). Mobile keeps its device viewport.
248
+ if not (platform in ("android", "ios") and stealth_args):
249
+ await _fit_window_to_screen_async(ctx)
223
250
  if desktop.normalize_brand(browser_brand) != "chrome":
224
251
  ctx = _wrap_close_async(ctx, pw)
225
252
  try:
@@ -251,6 +278,8 @@ async def launch_context_async(seed=None, platform="windows", headless=False, ur
251
278
  from . import human as _human
252
279
  _human.humanize(context, preset=human_preset)
253
280
  page = context.pages[0] if context.pages else await context.new_page()
281
+ if not getattr(browser, "_capium_mobile", False):
282
+ await _fit_window_to_screen_async(context)
254
283
  if url:
255
284
  await page.goto(url, wait_until="domcontentloaded", timeout=60000)
256
285
  return browser, context, page
@@ -0,0 +1,40 @@
1
+ {
2
+ "_comment": "SINGLE SOURCE OF TRUTH for the current stable Capium browser build per OS/arch. Each OS reaches stable independently -- edit one entry without touching the others. This file ships inside the wheel (the SDK reads it to pick each host's stable build) AND is published to the download server by the release pipeline. `version` is the reported Chromium version (what the browser reports to the customer, names the R2 folder); `engine_version` is the actual built Chromium engine (box_package verifies the binary against it) -- it differs from `version` when a platform reports a spoofed patch (Windows reports .85 on the .84 engine). `revision` is our packaging counter -- the served R2 folder is chromium-v<version>.<revision>/, ALWAYS dotted-suffixed starting at 1. A Chromium version bump resets revision to 1. `published: false` means the tag is declared but no artifact exists yet.",
3
+ "schema": 1,
4
+ "updated": "2026-10-21",
5
+ "channels": {
6
+ "stable": {
7
+ "windows-x64": {
8
+ "version": "155.0.8059.85",
9
+ "engine_version": "155.0.8059.84",
10
+ "revision": 1,
11
+ "sha256": "e10c944fc23637adb8d2f9206dbd69c595d51ba9de0cfd8140c9a2b5dc3510fc",
12
+ "published": true,
13
+ "license_status_protocol": 1
14
+ },
15
+ "macos-arm64": {
16
+ "version": "155.0.8059.84",
17
+ "engine_version": "155.0.8059.84",
18
+ "revision": 1,
19
+ "sha256": "273bdd754b06538f633d2830f6fb8e3f02df226f093cb42acd052c0fece74127",
20
+ "published": true,
21
+ "license_status_protocol": 1
22
+ },
23
+ "linux-x64": {
24
+ "version": "155.0.8059.84",
25
+ "engine_version": "155.0.8059.84",
26
+ "revision": 1,
27
+ "sha256": "e01b9b2179d82594e552f17aa0fad620785b4dfbf368c10c0ea146b5ce31c4e4",
28
+ "published": true,
29
+ "license_status_protocol": 1
30
+ },
31
+ "linux-arm64": {
32
+ "version": "155.0.8059.84",
33
+ "engine_version": "155.0.8059.84",
34
+ "revision": 1,
35
+ "sha256": null,
36
+ "published": false
37
+ }
38
+ }
39
+ }
40
+ }
@@ -24,16 +24,20 @@ import shutil
24
24
  IGNORE_DEFAULT_ARGS = ["--enable-automation", "--enable-unsafe-swiftshader"]
25
25
 
26
26
 
27
- # The 155 engine selects a seeded screen within these default bounds. Start
28
- # headless windows small enough to fit its modes without replacing the screen.
29
- # Explicit screen/window flags take
30
- # precedence. HEADLESS_SCREEN remains the upper bound for existing consumers.
27
+ # The 155 engine selects a seeded screen within these default bounds. The headless
28
+ # window is sized PER SEED after launch (browser._fit_window_to_screen -> the seed's
29
+ # screen.availWidth/Height), giving a coherent maximized viewport < screen for every
30
+ # profile. HEADLESS_VIEWPORT is only the brief STARTUP fallback before that fit runs
31
+ # (and the value raw launch() callers get if they build their own contexts); it must
32
+ # fit the smallest default seeded screen (win/linux min is 1280x720), so it is NOT the
33
+ # 800x600 headless-Chrome default tell. Explicit screen/window flags take precedence.
31
34
  HEADLESS_SCREEN = (1920, 1080)
32
- HEADLESS_VIEWPORT = (800, 600)
35
+ HEADLESS_VIEWPORT = (1280, 720)
33
36
 
34
37
 
35
38
  def get_headless_default_args():
36
- """Choose startup bounds without replacing the engine's seeded screen."""
39
+ """Startup window bounds (a safe, common fallback); the per-seed fit resizes to the
40
+ seeded screen's available area after launch without replacing the engine's screen."""
37
41
  vw, vh = HEADLESS_VIEWPORT
38
42
  return [
39
43
  "--window-size=%d,%d" % (vw, vh),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capiumbrowser
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: Drop-in stealth browser SDK on top of Playwright (Capium Chromium).
5
5
  Author: Capzy
6
6
  License: Capzy capiumbrowser SDK License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "capiumbrowser"
7
- version = "1.2.1"
7
+ version = "1.2.2"
8
8
  description = "Drop-in stealth browser SDK on top of Playwright (Capium Chromium)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -57,16 +57,19 @@ def test_third_party_cookies_allowed():
57
57
 
58
58
 
59
59
  def test_headless_default_keeps_seeded_screen():
60
+ # Startup fallback is a safe common size (not the 800x600 headless-default tell) and
61
+ # does NOT pin the screen -- the per-seed window fit resizes to the seeded screen.
60
62
  a = config.get_headless_default_args()
61
63
  assert not _has(a, "--fingerprint-screen-width")
62
64
  assert not _has(a, "--fingerprint-screen-height")
63
- assert "--window-size=800,600" in a
65
+ assert "--window-size=1280,720" in a
66
+ assert "--window-size=800,600" not in a
64
67
 
65
68
 
66
69
  def test_build_args_adds_headless_default_only_headless():
67
70
  from capiumbrowser import browser
68
71
  headless = browser._build_args(1, "windows", True, None, None, None, None, headless=True)
69
- assert "--window-size=800,600" in headless
72
+ assert "--window-size=1280,720" in headless
70
73
  assert not _has(headless, "--fingerprint-screen-width")
71
74
  assert not _has(headless, "--fingerprint-screen-height")
72
75
  headed = browser._build_args(1, "windows", True, None, None, None, None, headless=False)
@@ -1,36 +0,0 @@
1
- {
2
- "_comment": "SINGLE SOURCE OF TRUTH for the current stable Capium browser build per OS/arch. Each OS reaches stable independently -- edit one entry without touching the others. This file ships inside the wheel (the SDK reads it to pick each host's stable build) AND is published to the download server by the release pipeline. `version` is the Chromium version (what the browser reports to the customer -- unchanged by the revision); `revision` is our packaging counter -- the served R2 folder is chromium-v<version>.<revision>/, ALWAYS dotted-suffixed starting at 1 (chromium-v<version>.1/ is the first build, .2/ a same-engine re-spin), so publishing a re-spin never overwrites the artifact older SDKs still fetch. `published: false` means the tag is declared but no artifact exists yet -- the SDK raises a clear 'not published yet' error instead of a raw 404.",
3
- "schema": 1,
4
- "updated": "2026-10-10",
5
- "channels": {
6
- "stable": {
7
- "windows-x64": {
8
- "version": "155.0.8059.39",
9
- "revision": 2,
10
- "sha256": "25ef98b8713c2c9fb62615b4c9aa0d7a3bd20c76320ec3d99e848775b1d4fdd6",
11
- "published": true,
12
- "license_status_protocol": 1
13
- },
14
- "macos-arm64": {
15
- "version": "155.0.8059.39",
16
- "revision": 2,
17
- "sha256": "2c39538576a8c5f63b39ac1f09cdb1cc5f7d2b6899ed7625752c6c4c2737a90f",
18
- "published": true,
19
- "license_status_protocol": 1
20
- },
21
- "linux-x64": {
22
- "version": "155.0.8059.39",
23
- "revision": 2,
24
- "sha256": "87889050d6a1e46a8aabe4800f8fff5e474a07cd23e22a66f79b830f5544487c",
25
- "published": true,
26
- "license_status_protocol": 1
27
- },
28
- "linux-arm64": {
29
- "version": "155.0.8059.39",
30
- "revision": 2,
31
- "sha256": null,
32
- "published": false
33
- }
34
- }
35
- }
36
- }
File without changes
File without changes
File without changes
File without changes