capiumbrowser 1.2.0__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.
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/MANIFEST.in +3 -0
- {capiumbrowser-1.2.0/capiumbrowser.egg-info → capiumbrowser-1.2.2}/PKG-INFO +44 -6
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/README.md +43 -5
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/_version.py +7 -6
- capiumbrowser-1.2.2/capiumbrowser/android-phones.json +99 -0
- capiumbrowser-1.2.2/capiumbrowser/browser-brands.json +47 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/browser.py +98 -17
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/browser_async.py +100 -7
- capiumbrowser-1.2.2/capiumbrowser/channels.json +40 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/config.py +37 -10
- capiumbrowser-1.2.2/capiumbrowser/desktop.py +81 -0
- capiumbrowser-1.2.2/capiumbrowser/ios-phones.json +19 -0
- capiumbrowser-1.2.2/capiumbrowser/mobile.py +113 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2/capiumbrowser.egg-info}/PKG-INFO +44 -6
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/SOURCES.txt +8 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/pyproject.toml +2 -2
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_config_args.py +5 -2
- capiumbrowser-1.2.2/tests/test_desktop_brands.py +62 -0
- capiumbrowser-1.2.2/tests/test_ios_profiles.py +48 -0
- capiumbrowser-1.2.2/tests/test_mobile_profiles.py +157 -0
- capiumbrowser-1.2.0/capiumbrowser/channels.json +0 -36
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/LICENSE +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/__main__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/_driver.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/errors.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/human/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/human/behavior.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/licensing/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/licensing/client.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/licensing/download.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/licensing/readiness.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/network/__init__.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser/network/proxy.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/dependency_links.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/entry_points.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/requires.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/capiumbrowser.egg-info/top_level.txt +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/setup.cfg +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_channels_manifest.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_download_contract.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_geoip_flags.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_launch_viewport.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_license_readiness.py +0 -0
- {capiumbrowser-1.2.0 → capiumbrowser-1.2.2}/tests/test_proxy.py +0 -0
- {capiumbrowser-1.2.0 → 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.
|
|
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
|
|
@@ -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,17 +17,18 @@ import json
|
|
|
17
17
|
import os
|
|
18
18
|
import platform as _platform
|
|
19
19
|
|
|
20
|
-
__version__ = "1.2.
|
|
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.
|
|
26
|
-
"macos-arm64": "155.0.8059.
|
|
27
|
-
"linux-x64": "155.0.8059.
|
|
28
|
-
"linux-arm64": "155.0.8059.
|
|
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.
|
|
31
|
+
_DEFAULT_BINARY_VERSION = "155.0.8059.84"
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def _load_stable():
|
|
@@ -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
|
|
@@ -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):
|
|
@@ -209,10 +216,13 @@ def _wrap_close(obj, pw):
|
|
|
209
216
|
return obj
|
|
210
217
|
|
|
211
218
|
|
|
212
|
-
def _apply_viewport_defaults(browser, asynchronous=False):
|
|
219
|
+
def _apply_viewport_defaults(browser, asynchronous=False, mobile_seed=None, mobile_device=None, mobile_platform="android"):
|
|
213
220
|
"""Match the other launch APIs: use the window unless the caller chooses a viewport."""
|
|
214
221
|
def decorate(original):
|
|
215
222
|
def options(kwargs):
|
|
223
|
+
if mobile_seed is not None:
|
|
224
|
+
from .mobile import context_options
|
|
225
|
+
return context_options(mobile_seed, kwargs, mobile_device, mobile_platform)
|
|
216
226
|
if 'viewport' not in kwargs and 'no_viewport' not in kwargs:
|
|
217
227
|
kwargs = dict(kwargs, no_viewport=True)
|
|
218
228
|
return kwargs
|
|
@@ -234,13 +244,29 @@ def _apply_viewport_defaults(browser, asynchronous=False):
|
|
|
234
244
|
return browser
|
|
235
245
|
|
|
236
246
|
|
|
247
|
+
def _require_mobile_engine(context, mobile_device=None, seed=0, platform="android"):
|
|
248
|
+
"""Refuse incompatible binaries before a mobile case can visit a site."""
|
|
249
|
+
page = context.new_page()
|
|
250
|
+
try:
|
|
251
|
+
from .mobile import mobile_profile, engine_probe
|
|
252
|
+
expected = mobile_profile(seed, mobile_device, platform)
|
|
253
|
+
page.route("**/*", lambda route: route.fulfill(status=200, content_type="text/html", body="<!doctype html>"))
|
|
254
|
+
page.goto("https://capium-mobile-preview.invalid/", wait_until="domcontentloaded")
|
|
255
|
+
supported = page.evaluate(engine_probe(platform), expected)
|
|
256
|
+
if not supported:
|
|
257
|
+
raise ValueError("This binary does not support the requested mobile profile/device; use Capium 1.2.1 revision 2 or later")
|
|
258
|
+
finally:
|
|
259
|
+
page.close()
|
|
260
|
+
|
|
261
|
+
|
|
237
262
|
def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None, args=None,
|
|
238
263
|
stealth_args=True, timezone=None, locale=None, extension_paths=None, binary=None,
|
|
239
264
|
license_key=None, license_server=None, license_through_proxy=False,
|
|
240
|
-
license_preflight=True, driver=None, **kwargs):
|
|
265
|
+
license_preflight=True, driver=None, mobile_device=None, browser_brand=None, **kwargs):
|
|
241
266
|
"""Launch a Capium Browser (non-persistent; Playwright manages a temp profile).
|
|
242
267
|
|
|
243
268
|
seed/platform : identity. stealth_args=False drops the default fingerprint flags.
|
|
269
|
+
mobile_device : Android/iPhone catalog ID; requires the matching native release.
|
|
244
270
|
proxy : "dataimpulse" | "http://user:pass@host:port" | "socks5://host:port" | dict.
|
|
245
271
|
geoip : geo coherence, resolved inside the binary. None (default) = OFF: no lookup
|
|
246
272
|
runs, even with a proxy (a proxy alone no longer triggers it, so proxied
|
|
@@ -263,6 +289,10 @@ def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None
|
|
|
263
289
|
kwargs : forwarded to Playwright's chromium.launch (e.g. slow_mo=...).
|
|
264
290
|
Returns a Browser whose .close() also stops Playwright.
|
|
265
291
|
"""
|
|
292
|
+
from .mobile import validate_launch
|
|
293
|
+
from . import desktop
|
|
294
|
+
desktop.validate_launch(browser_brand, platform, binary, args, stealth_args)
|
|
295
|
+
validate_launch(platform, binary, args, stealth_args, mobile_device)
|
|
266
296
|
if license_preflight:
|
|
267
297
|
_license.preflight(license_key, license_server)
|
|
268
298
|
key, _ = _license.effective(license_key, license_server)
|
|
@@ -272,7 +302,7 @@ def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None
|
|
|
272
302
|
display = os.environ.get("DISPLAY")
|
|
273
303
|
proxy_kwargs, proxy_args = _proxy_and_geo_args(proxy, binpath, geoip, display)
|
|
274
304
|
launch_args = proxy_args + _build_args(
|
|
275
|
-
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless)
|
|
305
|
+
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless, mobile_device, browser_brand)
|
|
276
306
|
if license_through_proxy:
|
|
277
307
|
launch_args.append("--license-through-proxy")
|
|
278
308
|
env = _license.child_env(license_key, license_server)
|
|
@@ -304,7 +334,30 @@ def launch(seed=None, platform="windows", headless=False, proxy=None, geoip=None
|
|
|
304
334
|
raise
|
|
305
335
|
_clear_status_file(status_path)
|
|
306
336
|
browser._capium_seed = seed
|
|
307
|
-
|
|
337
|
+
browser._capium_mobile = platform in ("android", "ios") and stealth_args
|
|
338
|
+
browser = _wrap_close(_apply_viewport_defaults(
|
|
339
|
+
browser, mobile_seed=seed if browser._capium_mobile else None, mobile_device=mobile_device, mobile_platform=platform), pw)
|
|
340
|
+
if browser._capium_mobile:
|
|
341
|
+
try:
|
|
342
|
+
context = browser.new_context()
|
|
343
|
+
try:
|
|
344
|
+
_require_mobile_engine(context, mobile_device, seed, platform)
|
|
345
|
+
finally:
|
|
346
|
+
context.close()
|
|
347
|
+
except Exception:
|
|
348
|
+
browser.close()
|
|
349
|
+
raise
|
|
350
|
+
if desktop.normalize_brand(browser_brand) != "chrome":
|
|
351
|
+
try:
|
|
352
|
+
context = browser.new_context()
|
|
353
|
+
try:
|
|
354
|
+
desktop.require_engine(context, browser_brand)
|
|
355
|
+
finally:
|
|
356
|
+
context.close()
|
|
357
|
+
except Exception:
|
|
358
|
+
browser.close()
|
|
359
|
+
raise
|
|
360
|
+
return browser
|
|
308
361
|
|
|
309
362
|
|
|
310
363
|
def launch_persistent_context(user_data_dir, seed=None, platform="windows", headless=False,
|
|
@@ -312,11 +365,15 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
|
|
|
312
365
|
timezone=None, locale=None, extension_paths=None, binary=None,
|
|
313
366
|
license_key=None, license_server=None,
|
|
314
367
|
license_through_proxy=False,
|
|
315
|
-
license_preflight=True, driver=None, **kwargs):
|
|
368
|
+
license_preflight=True, driver=None, mobile_device=None, browser_brand=None, **kwargs):
|
|
316
369
|
"""Launch a persistent context (cookies/localStorage/state persist in user_data_dir).
|
|
317
370
|
|
|
318
371
|
Same options as launch(); returns a BrowserContext whose .close() also stops Playwright.
|
|
319
372
|
"""
|
|
373
|
+
from .mobile import context_options, validate_launch
|
|
374
|
+
from . import desktop
|
|
375
|
+
desktop.validate_launch(browser_brand, platform, binary, args, stealth_args)
|
|
376
|
+
validate_launch(platform, binary, args, stealth_args, mobile_device)
|
|
320
377
|
if license_preflight:
|
|
321
378
|
_license.preflight(license_key, license_server)
|
|
322
379
|
key, _ = _license.effective(license_key, license_server)
|
|
@@ -326,14 +383,16 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
|
|
|
326
383
|
display = os.environ.get("DISPLAY")
|
|
327
384
|
proxy_kwargs, proxy_args = _proxy_and_geo_args(proxy, binpath, geoip, display)
|
|
328
385
|
launch_args = proxy_args + _build_args(
|
|
329
|
-
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless)
|
|
386
|
+
seed, platform, stealth_args, timezone, locale, extension_paths, args, headless, mobile_device, browser_brand)
|
|
330
387
|
if license_through_proxy:
|
|
331
388
|
launch_args.append("--license-through-proxy")
|
|
332
389
|
env = _license.child_env(license_key, license_server)
|
|
333
390
|
status_path = _new_status_file(env)
|
|
334
391
|
# Default to the page filling the window (see launch_context) unless the caller
|
|
335
392
|
# pinned a viewport themselves.
|
|
336
|
-
if
|
|
393
|
+
if platform in ("android", "ios") and stealth_args:
|
|
394
|
+
kwargs = context_options(seed, kwargs, mobile_device, platform)
|
|
395
|
+
elif "viewport" not in kwargs and "no_viewport" not in kwargs:
|
|
337
396
|
kwargs["no_viewport"] = True
|
|
338
397
|
# Strip Playwright's automation + forced-software-WebGL defaults;
|
|
339
398
|
# the caller can override by passing ignore_default_args explicitly.
|
|
@@ -363,11 +422,29 @@ def launch_persistent_context(user_data_dir, seed=None, platform="windows", head
|
|
|
363
422
|
raise
|
|
364
423
|
_clear_status_file(status_path)
|
|
365
424
|
ctx._capium_seed = seed
|
|
366
|
-
#
|
|
367
|
-
#
|
|
368
|
-
#
|
|
369
|
-
|
|
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):
|
|
370
431
|
_fit_window_to_screen(ctx)
|
|
432
|
+
if desktop.normalize_brand(browser_brand) != "chrome":
|
|
433
|
+
ctx = _wrap_close(ctx, pw)
|
|
434
|
+
try:
|
|
435
|
+
desktop.require_engine(ctx, browser_brand)
|
|
436
|
+
except Exception:
|
|
437
|
+
ctx.close()
|
|
438
|
+
raise
|
|
439
|
+
return ctx
|
|
440
|
+
if platform in ("android", "ios") and stealth_args:
|
|
441
|
+
ctx = _wrap_close(ctx, pw)
|
|
442
|
+
try:
|
|
443
|
+
_require_mobile_engine(ctx, mobile_device, seed, platform)
|
|
444
|
+
except Exception:
|
|
445
|
+
ctx.close()
|
|
446
|
+
raise
|
|
447
|
+
return ctx
|
|
371
448
|
return _wrap_close(ctx, pw)
|
|
372
449
|
|
|
373
450
|
|
|
@@ -383,13 +460,17 @@ def launch_context(seed=None, platform="windows", headless=False, url=None,
|
|
|
383
460
|
# no_viewport: let the page fill the real window (fit_window sizes the window to
|
|
384
461
|
# the persona's screen). A fixed 1280x720 emulated viewport would leave the page
|
|
385
462
|
# smaller than the window AND mismatch window.screen — an incoherent fingerprint.
|
|
386
|
-
context = browser.contexts[0] if browser.contexts else browser.new_context(
|
|
463
|
+
context = browser.contexts[0] if browser.contexts else browser.new_context(
|
|
464
|
+
**({} if getattr(browser, "_capium_mobile", False) else {"no_viewport": True}))
|
|
387
465
|
if humanize:
|
|
388
466
|
from . import human as _human
|
|
389
467
|
_human.humanize(context, preset=human_preset)
|
|
390
468
|
page = context.pages[0] if context.pages else context.new_page()
|
|
391
|
-
if not
|
|
392
|
-
|
|
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
|
|
393
474
|
if url:
|
|
394
475
|
page.goto(url, wait_until="domcontentloaded", timeout=60000)
|
|
395
476
|
return browser, context, page
|