Metadata-Version: 2.4
Name: gecko-web-runtime-client
Version: 1.4.6
Summary: Thin Python client for the Firefox-derived WPR Gecko Worker protocol
Author: WPR project
License: MPL-2.0-compatible client; see the runtime bundle notices
Project-URL: Repository, https://github.com/childclm/gecko-web-runtime-client
Project-URL: Documentation, https://github.com/childclm/gecko-web-runtime-client/blob/main/README.md
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: wpr-runtime-core<0.3,>=0.2.4
Requires-Dist: wpr-http<0.3,>=0.2.0
Provides-Extra: diagnostics
Requires-Dist: loguru>=0.7.2; extra == "diagnostics"
Provides-Extra: wechat
Requires-Dist: wpr-http<0.3,>=0.2.1; extra == "wechat"
Requires-Dist: wpr-runtime-core<0.3,>=0.2.3; extra == "wechat"
Requires-Dist: Pillow<13,>=11; extra == "wechat"
Requires-Dist: numpy<3,>=2; extra == "wechat"
Requires-Dist: opencv-python-headless<6,>=4.10; extra == "wechat"

# Gecko Web Runtime Client

Python page and browser client for a versioned Firefox-derived WPR runtime.
It exposes pages, contexts, navigation, DOM access, screenshots, diagnostics,
Turnstile and WeChat workflow adapters.

Version 1.4.6 also exposes persistent standalone `JSContext` and `AsyncJSContext`:

```python
from gecko_web_runtime import JSContext
from gecko_web_runtime.javascript import NetworkConfig

with JSContext(network=NetworkConfig()) as ctx:
    ctx.navigate("https://example.com/")
    print(ctx.eval("({title:document.title,url:location.href})"))
```

Install the matching private release products first:

```powershell
python -m gecko_web_runtime.javascript install-runtime --product network --version 155.0.10
python -m gecko_web_runtime.javascript install-runtime --version 155.0.1
```

The standalone host and self-implemented DOM/BOM remain in the native Runtime
repository. Both standalone JavaScript and `wpr-http` reuse the same network
product; no second network implementation is bundled. Separate Contexts can
run concurrently; operations within one Context are ordered.

The native runtime is installed separately:

```powershell
python -m gecko_web_runtime install-runtime --version 155
```

The runtime is held in a private GitHub Release. Automatic download needs
access to `childclm/gecko-web-runtime`. The Python client depends on
`wpr-runtime-core` for Worker lifecycle and protocol compatibility. Historical
HTTP imports are retained through `wpr-http`.

## Optional HTTP WeChat integration

The same distribution includes `gecko_web_runtime.integrations.wechat`.
It uses `wpr-http` and a local Node.js environment, with no browser Page.
Install version 1.4.4 or newer with its optional dependencies, then prepare Node.js
20+ dependencies in the user's cache:

```powershell
pip install --upgrade "gecko-web-runtime-client[wechat]>=1.4.4"
python -m gecko_web_runtime.integrations.wechat install-js
```

These commands require a client build containing this integration. An older
published package does not acquire the integration by installing the extra.
Local development: `pip install -e "./python[wechat]"`.

```python
from gecko_web_runtime.integrations.wechat import AsyncWechatClient

async with AsyncWechatClient(proxy="") as client:
    result = await client.get(article_url)
    print(result.summary())
    if result.ok:
        article_html = result.html
```

Only Node dependencies are installed by `install-js`; a compatible Firefox
network runtime is installed separately through `wpr-http`. Native graphics
queries additionally require an installed Firefox ANGLE directory containing
`libEGL.dll` and `libGLESv2.dll`, passed as `native_gl_dir` or `NATIVE_GL_DIR`.
Direct article responses need neither Node nor native graphics.

One client retains one HTTP session and serializes its article flows. Separate
client instances have separate HTTP and JavaScript state. `total_timeout`
bounds an active flow; cancellation terminates an active Node subprocess.
The module uses Python logging and does not configure application handlers.
Artifacts are off by default; `output_dir` opts into per-call directories that
can contain session-sensitive diagnostic data. Keep those directories outside
source control. Site rejection is returned as a structured failure, not a
promise that any verification will succeed.

## WeChat article videos (1.4.5+)

The same `gecko-web-runtime-client[wechat]` installation now provides two
separate operations, without starting a browser Page:

```python
from gecko_web_runtime.integrations.wechat import AsyncWechatClient, VideoDownloadOptions

async with AsyncWechatClient(proxy="") as client:
    article = await client.get("ARTICLE_URL")
    if article.ok:
        downloads = await client.download_videos(
            article, output_dir="./videos",
            options=VideoDownloadOptions(quality="sd"),
        )
        for download in downloads:
            print(download.summary())
```

Article retrieval, WeChat video downloads, and current Tencent SDK resolution
share the caller's `wpr-http` session and proxy. Run `python -m
gecko_web_runtime.integrations.wechat install-js` after upgrading to prepare
the production Node dependencies. Downloads use validated Range requests,
conditional resume and verified-file skipping; completed files are not
overwritten. Servers ignoring Range still return buffered whole responses.
See `python/docs/wechat-video-download.md` for limits and recovery behavior.

## Media capture

For a browser-played Widevine page, use `AsyncMediaDownloader`. It starts the
Worker, provisions the caller's Widevine profile, enables bounded native H.264
capture, waits for playback progress, writes a checkpoint, and verifies the
final MP4 with FFmpeg.

```python
import asyncio
from pathlib import Path

from gecko_web_runtime import AsyncMediaDownloader


async def main() -> None:
    result = await AsyncMediaDownloader(
        "https://example.test/video",
        output_dir=Path("./media-output"),
        ffmpeg=Path(r"C:\\ffmpeg\\bin\\ffmpeg.exe"),
        expected_duration=2575.32,
        max_retries=1,
    ).download()
    if not result["completed"]:
        raise RuntimeError(result["attempts"])
    print(result["package"]["output"])


asyncio.run(main())
```

Set `WPR_RUNTIME_DIR`, `WPR_WORKER_EXE`, `WPR_PERSISTENT_PROFILE_DIR`, and
`WPR_ENABLE_CDM=1` before creating the downloader. Widevine is installed by
Firefox's official GMP installer into the persistent WPR profile; no existing
Firefox profile or component is imported. This requires the Worker build with
`runtime.widevine_start/status` support (Runtime 155.0.8 or later).
For a bounded
smoke test of a longer page, use `sample_seconds=120` instead of
`expected_duration`. The downloader keeps a `checkpoint.json`; rerunning with
the same output directory returns a completed result without starting another
Worker.

