capiumbrowser 1.2.0__tar.gz → 1.2.1__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.
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/MANIFEST.in +3 -0
- {capiumbrowser-1.2.0/capiumbrowser.egg-info → capiumbrowser-1.2.1}/PKG-INFO +44 -6
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/README.md +43 -5
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/_version.py +1 -1
- capiumbrowser-1.2.1/capiumbrowser/android-phones.json +99 -0
- capiumbrowser-1.2.1/capiumbrowser/browser-brands.json +47 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/browser.py +81 -12
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/browser_async.py +71 -7
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/channels.json +7 -7
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/config.py +27 -4
- capiumbrowser-1.2.1/capiumbrowser/desktop.py +81 -0
- capiumbrowser-1.2.1/capiumbrowser/ios-phones.json +19 -0
- capiumbrowser-1.2.1/capiumbrowser/mobile.py +113 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1/capiumbrowser.egg-info}/PKG-INFO +44 -6
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser.egg-info/SOURCES.txt +8 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/pyproject.toml +2 -2
- capiumbrowser-1.2.1/tests/test_desktop_brands.py +62 -0
- capiumbrowser-1.2.1/tests/test_ios_profiles.py +48 -0
- capiumbrowser-1.2.1/tests/test_mobile_profiles.py +157 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/LICENSE +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/__main__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/_driver.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/errors.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/human/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/human/behavior.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/licensing/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/licensing/client.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/licensing/download.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/licensing/readiness.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/network/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser/network/proxy.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser.egg-info/dependency_links.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser.egg-info/entry_points.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser.egg-info/requires.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/capiumbrowser.egg-info/top_level.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/setup.cfg +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_channels_manifest.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_config_args.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_download_contract.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_geoip_flags.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_launch_viewport.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_license_readiness.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/tests/test_proxy.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.1}/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.
|
|
3
|
+
Version: 1.2.1
|
|
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
|
|
@@ -128,6 +128,7 @@ browser.close() # also stops the Playwright driver — no leake
|
|
|
128
128
|
- [Install](#install) · [Why Capium](#why-capium) · [Licensing & tiers](#licensing)
|
|
129
129
|
- [What a persona reads](#what-a-persona-reads) · [Comparison](#comparison) · [How it works](#how-it-works)
|
|
130
130
|
- [API](#api) · [Parameters](#parameters) · [Personas & seeds](#personas--seeds) · [Fingerprint switches](#fingerprint-switches)
|
|
131
|
+
- [Browser and mobile profiles](#browser-and-mobile-profiles)
|
|
131
132
|
- [Human behavior](#human-behavior) · [Proxies & geo](#proxies--geo) · [Fonts](#fonts)
|
|
132
133
|
- [Framework integrations](#framework-integrations) · [Deployment](#deployment) · [Platforms](#platforms)
|
|
133
134
|
- [CLI](#cli--environment) · [Troubleshooting](#troubleshooting) · [FAQ](#faq) · [Security](#security) · [License](#license)
|
|
@@ -395,7 +396,9 @@ and `launch_persistent_context` forward everything to `launch`.
|
|
|
395
396
|
| Parameter | Type | Default | Description |
|
|
396
397
|
|---|---|---|---|
|
|
397
398
|
| `seed` | `int` \| `None` \| `"off"` | `None` | Persona seed → a complete coherent device. `None` = fresh random seed each launch. The range selects the OS family (see [Personas & seeds](#personas--seeds)). `"off"`/`"false"`/`"none"` = **passthrough** (host's real values; debugging). |
|
|
398
|
-
| `platform` | `str` | `"windows"` |
|
|
399
|
+
| `platform` | `str` | `"windows"` | OS profile: `"windows"` \| `"macos"` \| `"linux"` \| `"android"` \| `"ios"`. Mobile profiles are opt-in. |
|
|
400
|
+
| `browser_brand` | `str` | `"chrome"` | Desktop profile: `chrome`, `edge`, `opera`, `vivaldi`. |
|
|
401
|
+
| `mobile_device` | `str` | seed/default | Phone catalog ID; requires `android` or `ios`. |
|
|
399
402
|
| `headless` | `bool` | `False` | Headed is recommended (reads cleaner). On a server, run headed under Xvfb. |
|
|
400
403
|
| `stealth_args` | `bool` | `True` | Apply the default coherent fingerprint flags. Set `False` to build the whole command line yourself via `args`. |
|
|
401
404
|
| `args` | `list[str]` \| `None` | `None` | Extra raw Chrome flags appended verbatim (see [Fingerprint switches](#fingerprint-switches)). |
|
|
@@ -496,6 +499,42 @@ Full runnable version (personas, persistent profiles, a fleet, proxy+geo, full c
|
|
|
496
499
|
|
|
497
500
|
---
|
|
498
501
|
|
|
502
|
+
## Browser and mobile profiles
|
|
503
|
+
|
|
504
|
+
CapiumBrowser 1.2.1 supports desktop Chrome, Edge, Opera and Vivaldi identity
|
|
505
|
+
profiles, plus opt-in Android and iPhone profiles on Windows x64, macOS arm64
|
|
506
|
+
and Linux x64. Chrome remains the default.
|
|
507
|
+
|
|
508
|
+
| Profile | Python | Node.js |
|
|
509
|
+
| --- | --- | --- |
|
|
510
|
+
| Desktop browser | `browser_brand="edge"` (also `chrome`, `opera`, `vivaldi`) | `browserBrand: 'edge'` |
|
|
511
|
+
| Android phone | `platform="android", mobile_device="pixel-9"` | `platform: 'android', mobileDevice: 'pixel-9'` |
|
|
512
|
+
| iPhone 16 | `platform="ios", mobile_device="iphone-16"` | `platform: 'ios', mobileDevice: 'iphone-16'` |
|
|
513
|
+
|
|
514
|
+
Android includes Pixel 7/8/9 families and Samsung Galaxy A55. Omit the Android
|
|
515
|
+
device to retain the original seed selection; iOS defaults to iPhone 16. Mobile
|
|
516
|
+
profiles set the mobile layout and identity while leaving desktop defaults intact.
|
|
517
|
+
These are experimental desktop emulation profiles: they do not install Android
|
|
518
|
+
or iOS, reproduce WebKit, or certify physical-phone fingerprints. Android Chrome
|
|
519
|
+
and Chrome iOS are the supported mobile browser profiles; arbitrary app/WebView
|
|
520
|
+
UAs are outside these presets.
|
|
521
|
+
|
|
522
|
+
Desktop profiles use verified browser/Chromium version pairs: Edge 155/155,
|
|
523
|
+
Opera 137/153 and Vivaldi 8.2/152. Vivaldi follows its usual Chrome branding.
|
|
524
|
+
Capium's engine remains Chromium 155; vendor-specific features are not emulated.
|
|
525
|
+
A version catalog is not an older browser engine. Native compatibility checks
|
|
526
|
+
reject binaries that do not implement the selected profile.
|
|
527
|
+
|
|
528
|
+
```python
|
|
529
|
+
from capiumbrowser import launch_context
|
|
530
|
+
|
|
531
|
+
browser, context, page = launch_context(
|
|
532
|
+
seed=200123, platform="ios", mobile_device="iphone-16")
|
|
533
|
+
# Android: platform="android", mobile_device="pixel-9"
|
|
534
|
+
# Desktop Edge: platform="windows", browser_brand="edge"
|
|
535
|
+
browser.close()
|
|
536
|
+
```
|
|
537
|
+
|
|
499
538
|
## Fingerprint switches
|
|
500
539
|
|
|
501
540
|
The SDK sets the essentials from `seed`/`platform`. Everything below is available for precise control —
|
|
@@ -725,13 +764,12 @@ launches **headed**, which reads cleaner than headless.
|
|
|
725
764
|
| Linux arm64 | `linux-arm64` | Declared in the manifest; not yet published or covered by the three-host audit |
|
|
726
765
|
|
|
727
766
|
The SDK auto-detects your host and downloads the matching tag; macOS Intel and Windows ARM are **not**
|
|
728
|
-
published and raise a clear error. The **1.2.
|
|
729
|
-
supported hosts.
|
|
730
|
-
artifacts are published. Linux arm64 remains unpublished.
|
|
767
|
+
published and raise a clear error. The **1.2.1** manifest selects **Chromium 155.0.8059.39, revision 2** on the three
|
|
768
|
+
supported hosts. Browser downloads are available with a valid license. Linux arm64 remains unpublished.
|
|
731
769
|
|
|
732
770
|
---
|
|
733
771
|
|
|
734
|
-
The
|
|
772
|
+
The launch APIs wait for the packaged browser's licensing decision before
|
|
735
773
|
returning a handle. Invalid keys raise a typed error during launch.
|
|
736
774
|
|
|
737
775
|
## CLI & environment
|
|
@@ -65,6 +65,7 @@ browser.close() # also stops the Playwright driver — no leake
|
|
|
65
65
|
- [Install](#install) · [Why Capium](#why-capium) · [Licensing & tiers](#licensing)
|
|
66
66
|
- [What a persona reads](#what-a-persona-reads) · [Comparison](#comparison) · [How it works](#how-it-works)
|
|
67
67
|
- [API](#api) · [Parameters](#parameters) · [Personas & seeds](#personas--seeds) · [Fingerprint switches](#fingerprint-switches)
|
|
68
|
+
- [Browser and mobile profiles](#browser-and-mobile-profiles)
|
|
68
69
|
- [Human behavior](#human-behavior) · [Proxies & geo](#proxies--geo) · [Fonts](#fonts)
|
|
69
70
|
- [Framework integrations](#framework-integrations) · [Deployment](#deployment) · [Platforms](#platforms)
|
|
70
71
|
- [CLI](#cli--environment) · [Troubleshooting](#troubleshooting) · [FAQ](#faq) · [Security](#security) · [License](#license)
|
|
@@ -332,7 +333,9 @@ and `launch_persistent_context` forward everything to `launch`.
|
|
|
332
333
|
| Parameter | Type | Default | Description |
|
|
333
334
|
|---|---|---|---|
|
|
334
335
|
| `seed` | `int` \| `None` \| `"off"` | `None` | Persona seed → a complete coherent device. `None` = fresh random seed each launch. The range selects the OS family (see [Personas & seeds](#personas--seeds)). `"off"`/`"false"`/`"none"` = **passthrough** (host's real values; debugging). |
|
|
335
|
-
| `platform` | `str` | `"windows"` |
|
|
336
|
+
| `platform` | `str` | `"windows"` | OS profile: `"windows"` \| `"macos"` \| `"linux"` \| `"android"` \| `"ios"`. Mobile profiles are opt-in. |
|
|
337
|
+
| `browser_brand` | `str` | `"chrome"` | Desktop profile: `chrome`, `edge`, `opera`, `vivaldi`. |
|
|
338
|
+
| `mobile_device` | `str` | seed/default | Phone catalog ID; requires `android` or `ios`. |
|
|
336
339
|
| `headless` | `bool` | `False` | Headed is recommended (reads cleaner). On a server, run headed under Xvfb. |
|
|
337
340
|
| `stealth_args` | `bool` | `True` | Apply the default coherent fingerprint flags. Set `False` to build the whole command line yourself via `args`. |
|
|
338
341
|
| `args` | `list[str]` \| `None` | `None` | Extra raw Chrome flags appended verbatim (see [Fingerprint switches](#fingerprint-switches)). |
|
|
@@ -433,6 +436,42 @@ Full runnable version (personas, persistent profiles, a fleet, proxy+geo, full c
|
|
|
433
436
|
|
|
434
437
|
---
|
|
435
438
|
|
|
439
|
+
## Browser and mobile profiles
|
|
440
|
+
|
|
441
|
+
CapiumBrowser 1.2.1 supports desktop Chrome, Edge, Opera and Vivaldi identity
|
|
442
|
+
profiles, plus opt-in Android and iPhone profiles on Windows x64, macOS arm64
|
|
443
|
+
and Linux x64. Chrome remains the default.
|
|
444
|
+
|
|
445
|
+
| Profile | Python | Node.js |
|
|
446
|
+
| --- | --- | --- |
|
|
447
|
+
| Desktop browser | `browser_brand="edge"` (also `chrome`, `opera`, `vivaldi`) | `browserBrand: 'edge'` |
|
|
448
|
+
| Android phone | `platform="android", mobile_device="pixel-9"` | `platform: 'android', mobileDevice: 'pixel-9'` |
|
|
449
|
+
| iPhone 16 | `platform="ios", mobile_device="iphone-16"` | `platform: 'ios', mobileDevice: 'iphone-16'` |
|
|
450
|
+
|
|
451
|
+
Android includes Pixel 7/8/9 families and Samsung Galaxy A55. Omit the Android
|
|
452
|
+
device to retain the original seed selection; iOS defaults to iPhone 16. Mobile
|
|
453
|
+
profiles set the mobile layout and identity while leaving desktop defaults intact.
|
|
454
|
+
These are experimental desktop emulation profiles: they do not install Android
|
|
455
|
+
or iOS, reproduce WebKit, or certify physical-phone fingerprints. Android Chrome
|
|
456
|
+
and Chrome iOS are the supported mobile browser profiles; arbitrary app/WebView
|
|
457
|
+
UAs are outside these presets.
|
|
458
|
+
|
|
459
|
+
Desktop profiles use verified browser/Chromium version pairs: Edge 155/155,
|
|
460
|
+
Opera 137/153 and Vivaldi 8.2/152. Vivaldi follows its usual Chrome branding.
|
|
461
|
+
Capium's engine remains Chromium 155; vendor-specific features are not emulated.
|
|
462
|
+
A version catalog is not an older browser engine. Native compatibility checks
|
|
463
|
+
reject binaries that do not implement the selected profile.
|
|
464
|
+
|
|
465
|
+
```python
|
|
466
|
+
from capiumbrowser import launch_context
|
|
467
|
+
|
|
468
|
+
browser, context, page = launch_context(
|
|
469
|
+
seed=200123, platform="ios", mobile_device="iphone-16")
|
|
470
|
+
# Android: platform="android", mobile_device="pixel-9"
|
|
471
|
+
# Desktop Edge: platform="windows", browser_brand="edge"
|
|
472
|
+
browser.close()
|
|
473
|
+
```
|
|
474
|
+
|
|
436
475
|
## Fingerprint switches
|
|
437
476
|
|
|
438
477
|
The SDK sets the essentials from `seed`/`platform`. Everything below is available for precise control —
|
|
@@ -662,13 +701,12 @@ launches **headed**, which reads cleaner than headless.
|
|
|
662
701
|
| Linux arm64 | `linux-arm64` | Declared in the manifest; not yet published or covered by the three-host audit |
|
|
663
702
|
|
|
664
703
|
The SDK auto-detects your host and downloads the matching tag; macOS Intel and Windows ARM are **not**
|
|
665
|
-
published and raise a clear error. The **1.2.
|
|
666
|
-
supported hosts.
|
|
667
|
-
artifacts are published. Linux arm64 remains unpublished.
|
|
704
|
+
published and raise a clear error. The **1.2.1** manifest selects **Chromium 155.0.8059.39, revision 2** on the three
|
|
705
|
+
supported hosts. Browser downloads are available with a valid license. Linux arm64 remains unpublished.
|
|
668
706
|
|
|
669
707
|
---
|
|
670
708
|
|
|
671
|
-
The
|
|
709
|
+
The launch APIs wait for the packaged browser's licensing decision before
|
|
672
710
|
returning a handle. Invalid keys raise a typed error during launch.
|
|
673
711
|
|
|
674
712
|
## CLI & environment
|
|
@@ -17,7 +17,7 @@ import json
|
|
|
17
17
|
import os
|
|
18
18
|
import platform as _platform
|
|
19
19
|
|
|
20
|
-
__version__ = "1.2.
|
|
20
|
+
__version__ = "1.2.1"
|
|
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.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 2,
|
|
3
|
+
"minimum_release_year": 2022,
|
|
4
|
+
"browser": "chrome",
|
|
5
|
+
"status": "experimental-emulation",
|
|
6
|
+
"gpu_policy": "host",
|
|
7
|
+
"hardware_policy": "host",
|
|
8
|
+
"physical_device_validation": "pending",
|
|
9
|
+
"default_profile_ids": [
|
|
10
|
+
"pixel-7",
|
|
11
|
+
"pixel-8",
|
|
12
|
+
"pixel-8-pro",
|
|
13
|
+
"pixel-8a",
|
|
14
|
+
"pixel-9"
|
|
15
|
+
],
|
|
16
|
+
"sources": [
|
|
17
|
+
"https://support.google.com/pixelphone/answer/7158570?hl=en",
|
|
18
|
+
"https://news.samsung.com/global/samsung-galaxy-a55-5g-and-galaxy-a35-5g-awesome-innovations-and-security-engineered-for-everyone",
|
|
19
|
+
"https://www.samsung.com/ie/business/smartphones/galaxy-a/galaxy-a55-5g-awesome-navy-256gb-sm-a556bzkceub/",
|
|
20
|
+
"https://chromium.googlesource.com/devtools/devtools-frontend/+/cd7bfad9bba968ac23ba67e538f9e1d499991d25/front_end/models/emulation/EmulatedDevices.ts"
|
|
21
|
+
],
|
|
22
|
+
"profiles": [
|
|
23
|
+
{
|
|
24
|
+
"id": "pixel-7",
|
|
25
|
+
"model": "Pixel 7",
|
|
26
|
+
"release_year": 2022,
|
|
27
|
+
"platform_version": "13",
|
|
28
|
+
"width": 412,
|
|
29
|
+
"height": 915,
|
|
30
|
+
"dpr": 2.625
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "pixel-8",
|
|
34
|
+
"model": "Pixel 8",
|
|
35
|
+
"release_year": 2023,
|
|
36
|
+
"platform_version": "14",
|
|
37
|
+
"width": 412,
|
|
38
|
+
"height": 915,
|
|
39
|
+
"dpr": 2.625
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "pixel-8-pro",
|
|
43
|
+
"model": "Pixel 8 Pro",
|
|
44
|
+
"release_year": 2023,
|
|
45
|
+
"platform_version": "14",
|
|
46
|
+
"width": 448,
|
|
47
|
+
"height": 997,
|
|
48
|
+
"dpr": 3.0
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "pixel-8a",
|
|
52
|
+
"model": "Pixel 8a",
|
|
53
|
+
"release_year": 2024,
|
|
54
|
+
"platform_version": "14",
|
|
55
|
+
"width": 412,
|
|
56
|
+
"height": 915,
|
|
57
|
+
"dpr": 2.625
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "pixel-9",
|
|
61
|
+
"model": "Pixel 9",
|
|
62
|
+
"release_year": 2024,
|
|
63
|
+
"platform_version": "14",
|
|
64
|
+
"width": 412,
|
|
65
|
+
"height": 924,
|
|
66
|
+
"dpr": 2.625
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "samsung-galaxy-a55",
|
|
70
|
+
"model": "SM-A556B",
|
|
71
|
+
"display_name": "Samsung Galaxy A55",
|
|
72
|
+
"release_year": 2024,
|
|
73
|
+
"platform_version": "14",
|
|
74
|
+
"width": 360,
|
|
75
|
+
"height": 800,
|
|
76
|
+
"dpr": 2.25,
|
|
77
|
+
"geometry_status": "devtools-emulation-only",
|
|
78
|
+
"geometry_note": "These Chromium preset dimensions/DPR do not directly map to the published 1080x2340 panel; physical Chrome capture required."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "pixel-9-pro",
|
|
82
|
+
"model": "Pixel 9 Pro",
|
|
83
|
+
"release_year": 2024,
|
|
84
|
+
"platform_version": "14",
|
|
85
|
+
"width": 427,
|
|
86
|
+
"height": 952,
|
|
87
|
+
"dpr": 3.0
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "pixel-9-pro-xl",
|
|
91
|
+
"model": "Pixel 9 Pro XL",
|
|
92
|
+
"release_year": 2024,
|
|
93
|
+
"platform_version": "14",
|
|
94
|
+
"width": 448,
|
|
95
|
+
"height": 997,
|
|
96
|
+
"dpr": 3.0
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"verified_on": "2026-10-10",
|
|
4
|
+
"engine_version": "155.0.8059.39",
|
|
5
|
+
"scope": "Desktop UA and Client Hints profiles; does not install vendor engines or features.",
|
|
6
|
+
"profiles": [
|
|
7
|
+
{
|
|
8
|
+
"brand": "edge",
|
|
9
|
+
"product_version": "155.0.4283.45",
|
|
10
|
+
"chromium_version": "155.0.8059.40",
|
|
11
|
+
"hint_brand": "Microsoft Edge",
|
|
12
|
+
"ua_suffix": " Edg/",
|
|
13
|
+
"ua_version": "155.0.0.0",
|
|
14
|
+
"sources": [
|
|
15
|
+
"https://edgeupdates.microsoft.com/api/products?view=enterprise"
|
|
16
|
+
],
|
|
17
|
+
"measurement": "Stock Edge 155 Windows and Linux owned-origin captures"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"brand": "opera",
|
|
21
|
+
"product_version": "137.0.6036.39",
|
|
22
|
+
"chromium_version": "153.0.8010.55",
|
|
23
|
+
"hint_brand": "Opera",
|
|
24
|
+
"ua_suffix": " OPR/",
|
|
25
|
+
"ua_version": "137.0.0.0",
|
|
26
|
+
"sources": [
|
|
27
|
+
"https://blogs.opera.com/desktop/changelog-for-137/",
|
|
28
|
+
"https://blogs.opera.com/desktop/2026/10/opera-137-stable/"
|
|
29
|
+
],
|
|
30
|
+
"measurement": "Stock Opera 137 Linux owned-origin capture"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"brand": "vivaldi",
|
|
34
|
+
"product_version": "8.2.4133.84",
|
|
35
|
+
"chromium_version": "152.0.7977.160",
|
|
36
|
+
"hint_brand": "Google Chrome",
|
|
37
|
+
"ua_suffix": "",
|
|
38
|
+
"ua_version": "",
|
|
39
|
+
"sources": [
|
|
40
|
+
"https://vivaldi.com/blog/minor-update-nine-8-2/",
|
|
41
|
+
"https://vivaldi.com/blog/desktop/",
|
|
42
|
+
"https://help.vivaldi.com/developers/web/vivaldi-user-agent-and-client-hints-user-agent/"
|
|
43
|
+
],
|
|
44
|
+
"measurement": "Stock Vivaldi 8.2 Linux owned-origin capture; default branding only, partner-site exceptions excluded"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -117,13 +117,13 @@ def _has_screen_override(extra):
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
def _build_args(seed, platform, stealth_args, timezone, locale, extension_paths, extra,
|
|
120
|
-
headless=False):
|
|
120
|
+
headless=False, mobile_device=None, browser_brand=None):
|
|
121
121
|
args = []
|
|
122
122
|
if stealth_args:
|
|
123
|
-
args += config.get_default_stealth_args(seed, platform)
|
|
123
|
+
args += config.get_default_stealth_args(seed, platform, mobile_device=mobile_device, browser_brand=browser_brand)
|
|
124
124
|
# Keep the engine's seeded screen. Use initial window bounds only when
|
|
125
125
|
# the caller has not provided screen/window flags.
|
|
126
|
-
if headless and not _has_screen_override(extra):
|
|
126
|
+
if headless and platform not in ("android", "ios") and not _has_screen_override(extra):
|
|
127
127
|
args += config.get_headless_default_args()
|
|
128
128
|
if timezone:
|
|
129
129
|
args.append("--timezone=%s" % timezone) # capium in-binary tz spoof
|
|
@@ -209,10 +209,13 @@ def _wrap_close(obj, pw):
|
|
|
209
209
|
return obj
|
|
210
210
|
|
|
211
211
|
|
|
212
|
-
def _apply_viewport_defaults(browser, asynchronous=False):
|
|
212
|
+
def _apply_viewport_defaults(browser, asynchronous=False, mobile_seed=None, mobile_device=None, mobile_platform="android"):
|
|
213
213
|
"""Match the other launch APIs: use the window unless the caller chooses a viewport."""
|
|
214
214
|
def decorate(original):
|
|
215
215
|
def options(kwargs):
|
|
216
|
+
if mobile_seed is not None:
|
|
217
|
+
from .mobile import context_options
|
|
218
|
+
return context_options(mobile_seed, kwargs, mobile_device, mobile_platform)
|
|
216
219
|
if 'viewport' not in kwargs and 'no_viewport' not in kwargs:
|
|
217
220
|
kwargs = dict(kwargs, no_viewport=True)
|
|
218
221
|
return kwargs
|
|
@@ -234,13 +237,29 @@ def _apply_viewport_defaults(browser, asynchronous=False):
|
|
|
234
237
|
return browser
|
|
235
238
|
|
|
236
239
|
|
|
240
|
+
def _require_mobile_engine(context, mobile_device=None, seed=0, platform="android"):
|
|
241
|
+
"""Refuse incompatible binaries before a mobile case can visit a site."""
|
|
242
|
+
page = context.new_page()
|
|
243
|
+
try:
|
|
244
|
+
from .mobile import mobile_profile, engine_probe
|
|
245
|
+
expected = mobile_profile(seed, mobile_device, platform)
|
|
246
|
+
page.route("**/*", lambda route: route.fulfill(status=200, content_type="text/html", body="<!doctype html>"))
|
|
247
|
+
page.goto("https://capium-mobile-preview.invalid/", wait_until="domcontentloaded")
|
|
248
|
+
supported = page.evaluate(engine_probe(platform), expected)
|
|
249
|
+
if not supported:
|
|
250
|
+
raise ValueError("This binary does not support the requested mobile profile/device; use Capium 1.2.1 revision 2 or later")
|
|
251
|
+
finally:
|
|
252
|
+
page.close()
|
|
253
|
+
|
|
254
|
+
|
|
237
255
|
def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None, args=None,
|
|
238
256
|
stealth_args=True, timezone=None, locale=None, extension_paths=None, binary=None,
|
|
239
257
|
license_key=None, license_server=None, license_through_proxy=False,
|
|
240
|
-
license_preflight=True, driver=None, **kwargs):
|
|
258
|
+
license_preflight=True, driver=None, mobile_device=None, browser_brand=None, **kwargs):
|
|
241
259
|
"""Launch a Capium Browser (non-persistent; Playwright manages a temp profile).
|
|
242
260
|
|
|
243
261
|
seed/platform : identity. stealth_args=False drops the default fingerprint flags.
|
|
262
|
+
mobile_device : Android/iPhone catalog ID; requires the matching native release.
|
|
244
263
|
proxy : "dataimpulse" | "http://user:pass@host:port" | "socks5://host:port" | dict.
|
|
245
264
|
geoip : geo coherence, resolved inside the binary. None (default) = OFF: no lookup
|
|
246
265
|
runs, even with a proxy (a proxy alone no longer triggers it, so proxied
|
|
@@ -263,6 +282,10 @@ def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None
|
|
|
263
282
|
kwargs : forwarded to Playwright's chromium.launch (e.g. slow_mo=...).
|
|
264
283
|
Returns a Browser whose .close() also stops Playwright.
|
|
265
284
|
"""
|
|
285
|
+
from .mobile import validate_launch
|
|
286
|
+
from . import desktop
|
|
287
|
+
desktop.validate_launch(browser_brand, platform, binary, args, stealth_args)
|
|
288
|
+
validate_launch(platform, binary, args, stealth_args, mobile_device)
|
|
266
289
|
if license_preflight:
|
|
267
290
|
_license.preflight(license_key, license_server)
|
|
268
291
|
key, _ = _license.effective(license_key, license_server)
|
|
@@ -272,7 +295,7 @@ def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None
|
|
|
272
295
|
display = os.environ.get("DISPLAY")
|
|
273
296
|
proxy_kwargs, proxy_args = _proxy_and_geo_args(proxy, binpath, geoip, display)
|
|
274
297
|
launch_args = proxy_args + _build_args(
|
|
275
|
-
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless)
|
|
298
|
+
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless, mobile_device, browser_brand)
|
|
276
299
|
if license_through_proxy:
|
|
277
300
|
launch_args.append("--license-through-proxy")
|
|
278
301
|
env = _license.child_env(license_key, license_server)
|
|
@@ -304,7 +327,30 @@ def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None
|
|
|
304
327
|
raise
|
|
305
328
|
_clear_status_file(status_path)
|
|
306
329
|
browser._capium_seed = seed
|
|
307
|
-
|
|
330
|
+
browser._capium_mobile = platform in ("android", "ios") and stealth_args
|
|
331
|
+
browser = _wrap_close(_apply_viewport_defaults(
|
|
332
|
+
browser, mobile_seed=seed if browser._capium_mobile else None, mobile_device=mobile_device, mobile_platform=platform), pw)
|
|
333
|
+
if browser._capium_mobile:
|
|
334
|
+
try:
|
|
335
|
+
context = browser.new_context()
|
|
336
|
+
try:
|
|
337
|
+
_require_mobile_engine(context, mobile_device, seed, platform)
|
|
338
|
+
finally:
|
|
339
|
+
context.close()
|
|
340
|
+
except Exception:
|
|
341
|
+
browser.close()
|
|
342
|
+
raise
|
|
343
|
+
if desktop.normalize_brand(browser_brand) != "chrome":
|
|
344
|
+
try:
|
|
345
|
+
context = browser.new_context()
|
|
346
|
+
try:
|
|
347
|
+
desktop.require_engine(context, browser_brand)
|
|
348
|
+
finally:
|
|
349
|
+
context.close()
|
|
350
|
+
except Exception:
|
|
351
|
+
browser.close()
|
|
352
|
+
raise
|
|
353
|
+
return browser
|
|
308
354
|
|
|
309
355
|
|
|
310
356
|
def launch_persistent_context(user_data_dir, seed=None, platform="windows", headless=False,
|
|
@@ -312,11 +358,15 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
|
|
|
312
358
|
timezone=None, locale=None, extension_paths=None, binary=None,
|
|
313
359
|
license_key=None, license_server=None,
|
|
314
360
|
license_through_proxy=False,
|
|
315
|
-
license_preflight=True, driver=None, **kwargs):
|
|
361
|
+
license_preflight=True, driver=None, mobile_device=None, browser_brand=None, **kwargs):
|
|
316
362
|
"""Launch a persistent context (cookies/localStorage/state persist in user_data_dir).
|
|
317
363
|
|
|
318
364
|
Same options as launch(); returns a BrowserContext whose .close() also stops Playwright.
|
|
319
365
|
"""
|
|
366
|
+
from .mobile import context_options, validate_launch
|
|
367
|
+
from . import desktop
|
|
368
|
+
desktop.validate_launch(browser_brand, platform, binary, args, stealth_args)
|
|
369
|
+
validate_launch(platform, binary, args, stealth_args, mobile_device)
|
|
320
370
|
if license_preflight:
|
|
321
371
|
_license.preflight(license_key, license_server)
|
|
322
372
|
key, _ = _license.effective(license_key, license_server)
|
|
@@ -326,14 +376,16 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
|
|
|
326
376
|
display = os.environ.get("DISPLAY")
|
|
327
377
|
proxy_kwargs, proxy_args = _proxy_and_geo_args(proxy, binpath, geoip, display)
|
|
328
378
|
launch_args = proxy_args + _build_args(
|
|
329
|
-
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless)
|
|
379
|
+
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless, mobile_device, browser_brand)
|
|
330
380
|
if license_through_proxy:
|
|
331
381
|
launch_args.append("--license-through-proxy")
|
|
332
382
|
env = _license.child_env(license_key, license_server)
|
|
333
383
|
status_path = _new_status_file(env)
|
|
334
384
|
# Default to the page filling the window (see launch_context) unless the caller
|
|
335
385
|
# pinned a viewport themselves.
|
|
336
|
-
if
|
|
386
|
+
if platform in ("android", "ios") and stealth_args:
|
|
387
|
+
kwargs = context_options(seed, kwargs, mobile_device, platform)
|
|
388
|
+
elif "viewport" not in kwargs and "no_viewport" not in kwargs:
|
|
337
389
|
kwargs["no_viewport"] = True
|
|
338
390
|
# Strip Playwright's automation + forced-software-WebGL defaults;
|
|
339
391
|
# the caller can override by passing ignore_default_args explicitly.
|
|
@@ -363,6 +415,22 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
|
|
|
363
415
|
raise
|
|
364
416
|
_clear_status_file(status_path)
|
|
365
417
|
ctx._capium_seed = seed
|
|
418
|
+
if desktop.normalize_brand(browser_brand) != "chrome":
|
|
419
|
+
ctx = _wrap_close(ctx, pw)
|
|
420
|
+
try:
|
|
421
|
+
desktop.require_engine(ctx, browser_brand)
|
|
422
|
+
except Exception:
|
|
423
|
+
ctx.close()
|
|
424
|
+
raise
|
|
425
|
+
return ctx
|
|
426
|
+
if platform in ("android", "ios") and stealth_args:
|
|
427
|
+
ctx = _wrap_close(ctx, pw)
|
|
428
|
+
try:
|
|
429
|
+
_require_mobile_engine(ctx, mobile_device, seed, platform)
|
|
430
|
+
except Exception:
|
|
431
|
+
ctx.close()
|
|
432
|
+
raise
|
|
433
|
+
return ctx
|
|
366
434
|
# Headed persistent launches fit the window to the selected screen. Headless
|
|
367
435
|
# keeps its startup bounds (800x600 by default) independent of the seeded screen;
|
|
368
436
|
# callers can choose larger window bounds explicitly.
|
|
@@ -383,12 +451,13 @@ def launch_context(seed=None, platform="windows", headless=False, url=None,
|
|
|
383
451
|
# no_viewport: let the page fill the real window (fit_window sizes the window to
|
|
384
452
|
# the persona's screen). A fixed 1280x720 emulated viewport would leave the page
|
|
385
453
|
# smaller than the window AND mismatch window.screen — an incoherent fingerprint.
|
|
386
|
-
context = browser.contexts[0] if browser.contexts else browser.new_context(
|
|
454
|
+
context = browser.contexts[0] if browser.contexts else browser.new_context(
|
|
455
|
+
**({} if getattr(browser, "_capium_mobile", False) else {"no_viewport": True}))
|
|
387
456
|
if humanize:
|
|
388
457
|
from . import human as _human
|
|
389
458
|
_human.humanize(context, preset=human_preset)
|
|
390
459
|
page = context.pages[0] if context.pages else context.new_page()
|
|
391
|
-
if not headless:
|
|
460
|
+
if not headless and not getattr(browser, "_capium_mobile", False):
|
|
392
461
|
fit_window(browser, context, page) # modest window <= spoofed screen
|
|
393
462
|
if url:
|
|
394
463
|
page.goto(url, wait_until="domcontentloaded", timeout=60000)
|
|
@@ -72,13 +72,17 @@ async def launch_async(seed=None, platform="windows", headless=False, proxy=None
|
|
|
72
72
|
args=None, stealth_args=True, timezone=None, locale=None,
|
|
73
73
|
extension_paths=None, binary=None, license_key=None,
|
|
74
74
|
license_server=None, license_through_proxy=False,
|
|
75
|
-
license_preflight=True, driver=None, **kwargs):
|
|
75
|
+
license_preflight=True, driver=None, mobile_device=None, browser_brand=None, **kwargs):
|
|
76
76
|
"""Async launch -> Browser (await browser.close() also stops Playwright).
|
|
77
77
|
|
|
78
78
|
driver: "playwright" (default) or the installed "patchright" fork.
|
|
79
79
|
CAPIUM_DRIVER=patchright also works. Evaluation-world defaults and API
|
|
80
80
|
compatibility depend on the installed version. See capiumbrowser._driver.
|
|
81
81
|
"""
|
|
82
|
+
from .mobile import validate_launch
|
|
83
|
+
from . import desktop
|
|
84
|
+
desktop.validate_launch(browser_brand, platform, binary, args, stealth_args)
|
|
85
|
+
validate_launch(platform, binary, args, stealth_args, mobile_device)
|
|
82
86
|
await _license_preflight_async(license_key, license_server, license_preflight)
|
|
83
87
|
key, _ = _license.effective(license_key, license_server)
|
|
84
88
|
binpath = _resolve_binary(binary, key)
|
|
@@ -87,7 +91,7 @@ async def launch_async(seed=None, platform="windows", headless=False, proxy=None
|
|
|
87
91
|
display = os.environ.get("DISPLAY")
|
|
88
92
|
proxy_kwargs, proxy_args = await _proxy_and_geo_args_async(proxy, binpath, geoip, display)
|
|
89
93
|
launch_args = proxy_args + _build_args(
|
|
90
|
-
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless)
|
|
94
|
+
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless, mobile_device, browser_brand)
|
|
91
95
|
if license_through_proxy:
|
|
92
96
|
launch_args.append("--license-through-proxy")
|
|
93
97
|
env = _license.child_env(license_key, license_server)
|
|
@@ -119,7 +123,44 @@ async def launch_async(seed=None, platform="windows", headless=False, proxy=None
|
|
|
119
123
|
raise
|
|
120
124
|
_clear_status_file(status_path)
|
|
121
125
|
browser._capium_seed = seed
|
|
122
|
-
|
|
126
|
+
browser._capium_mobile = platform in ("android", "ios") and stealth_args
|
|
127
|
+
browser = _wrap_close_async(_apply_viewport_defaults(browser, asynchronous=True,
|
|
128
|
+
mobile_seed=seed if browser._capium_mobile else None, mobile_device=mobile_device, mobile_platform=platform), pw)
|
|
129
|
+
if browser._capium_mobile:
|
|
130
|
+
try:
|
|
131
|
+
context = await browser.new_context()
|
|
132
|
+
try:
|
|
133
|
+
await _require_mobile_engine_async(context, mobile_device, seed, platform)
|
|
134
|
+
finally:
|
|
135
|
+
await context.close()
|
|
136
|
+
except Exception:
|
|
137
|
+
await browser.close()
|
|
138
|
+
raise
|
|
139
|
+
if desktop.normalize_brand(browser_brand) != "chrome":
|
|
140
|
+
try:
|
|
141
|
+
context = await browser.new_context()
|
|
142
|
+
try:
|
|
143
|
+
await desktop.require_engine_async(context, browser_brand)
|
|
144
|
+
finally:
|
|
145
|
+
await context.close()
|
|
146
|
+
except Exception:
|
|
147
|
+
await browser.close()
|
|
148
|
+
raise
|
|
149
|
+
return browser
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def _require_mobile_engine_async(context, mobile_device=None, seed=0, platform="android"):
|
|
153
|
+
page = await context.new_page()
|
|
154
|
+
try:
|
|
155
|
+
from .mobile import mobile_profile, engine_probe
|
|
156
|
+
expected = mobile_profile(seed, mobile_device, platform)
|
|
157
|
+
await page.route("**/*", lambda route: route.fulfill(status=200, content_type="text/html", body="<!doctype html>"))
|
|
158
|
+
await page.goto("https://capium-mobile-preview.invalid/", wait_until="domcontentloaded")
|
|
159
|
+
supported = await page.evaluate(engine_probe(platform), expected)
|
|
160
|
+
if not supported:
|
|
161
|
+
raise ValueError("This binary does not support the requested mobile profile/device; use Capium 1.2.1 revision 2 or later")
|
|
162
|
+
finally:
|
|
163
|
+
await page.close()
|
|
123
164
|
|
|
124
165
|
|
|
125
166
|
async def launch_persistent_context_async(user_data_dir, seed=None, platform="windows",
|
|
@@ -127,9 +168,13 @@ async def launch_persistent_context_async(user_data_dir, seed=None, platform="wi
|
|
|
127
168
|
stealth_args=True, timezone=None, locale=None,
|
|
128
169
|
extension_paths=None, binary=None, license_key=None,
|
|
129
170
|
license_server=None, license_through_proxy=False,
|
|
130
|
-
license_preflight=True, driver=None,
|
|
171
|
+
license_preflight=True, driver=None, mobile_device=None, browser_brand=None,
|
|
131
172
|
**kwargs):
|
|
132
173
|
"""Async persistent context -> BrowserContext."""
|
|
174
|
+
from .mobile import context_options, validate_launch
|
|
175
|
+
from . import desktop
|
|
176
|
+
desktop.validate_launch(browser_brand, platform, binary, args, stealth_args)
|
|
177
|
+
validate_launch(platform, binary, args, stealth_args, mobile_device)
|
|
133
178
|
await _license_preflight_async(license_key, license_server, license_preflight)
|
|
134
179
|
key, _ = _license.effective(license_key, license_server)
|
|
135
180
|
binpath = _resolve_binary(binary, key)
|
|
@@ -138,12 +183,14 @@ async def launch_persistent_context_async(user_data_dir, seed=None, platform="wi
|
|
|
138
183
|
display = os.environ.get("DISPLAY")
|
|
139
184
|
proxy_kwargs, proxy_args = await _proxy_and_geo_args_async(proxy, binpath, geoip, display)
|
|
140
185
|
launch_args = proxy_args + _build_args(
|
|
141
|
-
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless)
|
|
186
|
+
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless, mobile_device, browser_brand)
|
|
142
187
|
if license_through_proxy:
|
|
143
188
|
launch_args.append("--license-through-proxy")
|
|
144
189
|
env = _license.child_env(license_key, license_server)
|
|
145
190
|
status_path = _new_status_file(env)
|
|
146
|
-
if
|
|
191
|
+
if platform in ("android", "ios") and stealth_args:
|
|
192
|
+
kwargs = context_options(seed, kwargs, mobile_device, platform)
|
|
193
|
+
elif "viewport" not in kwargs and "no_viewport" not in kwargs:
|
|
147
194
|
kwargs["no_viewport"] = True
|
|
148
195
|
# Strip Playwright's automation + forced-software-WebGL defaults;
|
|
149
196
|
# the caller can override by passing ignore_default_args explicitly.
|
|
@@ -173,6 +220,22 @@ async def launch_persistent_context_async(user_data_dir, seed=None, platform="wi
|
|
|
173
220
|
raise
|
|
174
221
|
_clear_status_file(status_path)
|
|
175
222
|
ctx._capium_seed = seed
|
|
223
|
+
if desktop.normalize_brand(browser_brand) != "chrome":
|
|
224
|
+
ctx = _wrap_close_async(ctx, pw)
|
|
225
|
+
try:
|
|
226
|
+
await desktop.require_engine_async(ctx, browser_brand)
|
|
227
|
+
except Exception:
|
|
228
|
+
await ctx.close()
|
|
229
|
+
raise
|
|
230
|
+
return ctx
|
|
231
|
+
if platform in ("android", "ios") and stealth_args:
|
|
232
|
+
ctx = _wrap_close_async(ctx, pw)
|
|
233
|
+
try:
|
|
234
|
+
await _require_mobile_engine_async(ctx, mobile_device, seed, platform)
|
|
235
|
+
except Exception:
|
|
236
|
+
await ctx.close()
|
|
237
|
+
raise
|
|
238
|
+
return ctx
|
|
176
239
|
return _wrap_close_async(ctx, pw)
|
|
177
240
|
|
|
178
241
|
|
|
@@ -182,7 +245,8 @@ async def launch_context_async(seed=None, platform="windows", headless=False, ur
|
|
|
182
245
|
browser = await launch_async(seed=seed, platform=platform, headless=headless, **kwargs)
|
|
183
246
|
# no_viewport: page fills the real window (see browser.launch_context).
|
|
184
247
|
context = (browser.contexts[0] if browser.contexts
|
|
185
|
-
else await browser.new_context(
|
|
248
|
+
else await browser.new_context(
|
|
249
|
+
**({} if getattr(browser, "_capium_mobile", False) else {"no_viewport": True})))
|
|
186
250
|
if humanize:
|
|
187
251
|
from . import human as _human
|
|
188
252
|
_human.humanize(context, preset=human_preset)
|