Metadata-Version: 2.4
Name: dbgidchromium
Version: 1.1.9
Summary: Android-focused browser automation toolkit
Home-page: https://github.com/dbgid/dbgidchromium
Author: dbgid
License: MIT
Project-URL: Repository, https://github.com/dbgid/dbgidchromium
Project-URL: Required Browser, https://github.com/dbgid/DBG-ID-Browser
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: requires-python
Dynamic: summary

# dbgidchromium

[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
[![Android](https://img.shields.io/badge/Android-Termux%20%7C%20Pydroid3-3DDC84?style=for-the-badge&logo=android&logoColor=white)](https://developer.android.com/)
[![Status](https://img.shields.io/badge/Automation-WebDriver%20Toolkit-111827?style=for-the-badge)](https://github.com/dbgid/dbgidchromium)
[![Version](https://img.shields.io/badge/Version-1.1.9-7C3AED?style=for-the-badge)](https://pypi.org/project/dbgidchromium/)
[![PyPI](https://img.shields.io/badge/PyPI-dbgidchromium-0A66C2?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/dbgidchromium/)
[![Repo](https://img.shields.io/badge/GitHub-dbgid%2Fdbgidchromium-181717?style=for-the-badge&logo=github)](https://github.com/dbgid/dbgidchromium)
[![Required Browser](https://img.shields.io/badge/Required%20Browser-DBG--ID%20Browser-0F766E?style=for-the-badge&logo=googlechrome&logoColor=white)](https://github.com/dbgid/DBG-ID-Browser)

<p align="center">
  <strong>Android-first browser automation for DBG-ID Browser.</strong><br>
  Stable navigation, resilient interaction helpers, JavaScript execution, request interception,
  iframe support, spoof headers, and Selenium-like ergonomics for Termux / Pydroid3 workflows.
</p>

---

## Why use `dbgidchromium`?

`dbgidchromium` is a practical WebDriver-style toolkit focused on real automation work on Android.
It is designed for sessions that need more than just `find_element(...).click()`:

- reliable `goto()` page readiness with multiple wait modes
- retry-friendly interaction helpers for typing, clicking, selecting, and checking checkboxes
- request interception for lighter/faster navigations
- same-origin iframe detection and switching
- JavaScript execution with Selenium-like `arguments[...]`
- browser cleanup, cookie loading, and spoof/header helpers

Current release: **`1.1.9`**

## What's new in `1.1.9`

- refreshed README with cleaner structure, badges, and examples
- bumped package version to `1.1.9`
- documented the resilient interaction helper family:
  - `ensure_typing(...)`
  - `ensure_click(...)`
  - `ensure_option_select(...)`
  - `ensure_checked_checkbox(...)`

---

## Requirements

- Python **3.10+**
- Android environment such as **Termux** or **Pydroid3**
- [DBG-ID Browser](https://github.com/dbgid/DBG-ID-Browser)

> Install the required browser first. This package is the Python-side automation toolkit.

---

## Installation

### Install from PyPI

```bash
pip install dbgidchromium
```

### Install from GitHub

```bash
pip install git+https://github.com/dbgid/dbgidchromium
```

### Local clone

```bash
git clone https://github.com/dbgid/dbgidchromium
cd dbgidchromium
```

---

## Import

```python
from dbgidchromium import (
    WebDriver,
    By,
    goto,
    clear_browser,
    generate_ip,
    use_spoof,
)
```

---

## Quick Start

### Method style

```python
from dbgidchromium import WebDriver

driver = WebDriver(gui=False, debug=True)

try:
    driver.goto(
        "https://example.com",
        wait_until="javascript",
        locator="body",
        timeout=45,
        log=True,
    )

    driver.ensure_typing("input[name='q']", "hello world", press_enter=True)
    driver.ensure_click("button[type='submit']")

    print("URL  :", driver.current_url)
    print("Title:", driver.title)
finally:
    driver.close()
```

### Function style

```python
from dbgidchromium import WebDriver, goto

driver = WebDriver(gui=False, debug=True)

try:
    goto(
        driver,
        "https://example.com",
        wait_until="complete",
        fallback_wait_until="url_only",
        fallback_after=5,
        locator="body",
        timeout=60,
        log=True,
    )

    print(driver.current_url)
    print(driver.title)
finally:
    driver.close()
```

---

## Feature Overview

| Area | Helpers | What it gives you |
|---|---|---|
| Navigation | `goto(...)`, `driver.goto(...)`, `driver.wait_for_navigation(...)` | Stable page-load waiting with multiple readiness modes |
| Interaction | `ensure_typing`, `ensure_click`, `ensure_option_select`, `ensure_checked_checkbox` | Retry-friendly helpers for flaky or dynamic UIs |
| DOM lookup | `find_element`, `find_elements`, `querySelector`, `querySelectorAll` | Selenium-like element lookup with CSS shortcuts |
| JavaScript | `driver.execute_script(...)` | Run JS and pass/return elements like Selenium |
| Frames | `detect_iframes`, `switch_to.frame(...)` | Same-origin iframe inspection and switching |
| Session cleanup | `clear_browser`, `driver.clear_browser()` | Clear cookies, storage, and best-effort cache state |
| Cookies / headers | `load_cookies_from_requests`, `use_spoof`, `driver.headers` | Bridge requests sessions and spoof request headers |
| Network control | `interceptor=...` in `goto()` | Block selected resource types during navigation |
| Utilities | `generate_ip()` | Local ASN/IP helper using bundled database |

---

## Core API at a glance

| Helper | Type | Summary |
|---|---|---|
| `WebDriver(gui=True, debug=False)` | class | Main browser session object |
| `goto(driver, url, **kwargs)` | function | Navigate with wait control and progress logging |
| `driver.goto(url, **kwargs)` | method | Same navigation helper in method style |
| `clear_browser(driver)` | function | Clear browser state |
| `driver.clear_browser()` | method | Clear browser state from the session |
| `driver.execute_script(script, *args)` | method | Run JavaScript with argument support |
| `driver.query_selector(selector)` / `driver.querySelector(selector)` | method | First CSS match |
| `driver.query_selector_all(selector)` / `driver.querySelectorAll(selector)` | method | All CSS matches |
| `driver.detect_iframes(include_elements=False)` | method | Inspect frame metadata |
| `driver.switch_to.frame(ref)` / `driver.switch_to.iframe(ref)` | method | Enter a same-origin iframe |
| `driver.switch_to.default_content()` / `driver.switch_to.parent_frame()` | method | Leave frame context |
| `element.text` | property | Normalized text similar to Selenium |

---

## Resilient interaction helpers

These helpers are intended for dynamic pages where a plain immediate action can be too brittle.

### 1. `ensure_typing(selector, text, press_enter=False)`

- waits for a visible element
- retries up to **5 times**
- types character-by-character like `typing_like_human(...)`

```python
driver.ensure_typing("input[name='q']", "hello world", press_enter=True)
driver.ensure_typing("//input[@name='q']", "hello", press_enter=False)
```

### 2. `ensure_click(selector)`

- waits for visibility
- waits for clickability
- retries up to **5 times**
- CSS selectors get a `querySelector(...).click()` JavaScript fallback

```python
driver.ensure_click("button[type='submit']")
driver.ensure_click("//button[contains(., 'Search')]")
```

### 3. `ensure_option_select(selector, selected)`

- waits for a visible, clickable `<select>`
- tries visible text, value, then index if applicable
- retries up to **5 times**
- CSS selectors get a JavaScript fallback

```python
driver.ensure_option_select("select[name='country']", "Indonesia")
driver.ensure_option_select("select[name='country']", "id")
driver.ensure_option_select("select[name='country']", 2)
driver.ensure_option_select("//select[@name='country']", "Indonesia")
```

### 4. `ensure_checked_checkbox(selector)`

- returns immediately if already checked
- otherwise waits for clickability and checks it
- retries up to **5 times**
- CSS selectors get a JavaScript fallback

```python
driver.ensure_checked_checkbox("input[name='terms']")
driver.ensure_checked_checkbox("#remember-me")
driver.ensure_checked_checkbox("//input[@type='checkbox' and @name='terms']")
```

> Notes:
> - CSS selectors can use the JavaScript `querySelector(...)` fallback.
> - XPath selectors still use the normal retry + wait flow.

---

## Navigation modes

`goto()` and `wait_for_navigation()` support multiple wait strategies:

| `wait_until` mode | Meaning |
|---|---|
| `realtime` | Wait using richer activity signals such as DOM/network/mutation stability |
| `javascript` | Balanced mode for most modern pages |
| `complete` | Wait for document complete state |
| `domcontentloaded` | Return earlier once DOM content is ready |
| `url_only` | Focus on URL/navigation change only |
| `network_blind` | Ignore element waiting and return sooner based on page state rules |

### Common `goto()` kwargs

| Kwarg | Description |
|---|---|
| `wait_until` | Primary page readiness mode |
| `fallback_wait_until` | Optional fallback mode |
| `fallback_after` | Seconds before the fallback mode activates |
| `locator` / `wait_for` | CSS, XPath, or `(By, value)` tuple to wait for |
| `timeout` | Max navigation time in seconds |
| `poll_frequency` | Poll interval while waiting |
| `settle_time` | Stability window before a page is treated as ready |
| `log` | Enable styled progress output |
| `clear_browser_state` | Clear cookies/storage/cache before navigation |
| `use_spoof` | Apply spoof headers before loading the page |
| `use_cookie_from_requests` | Load `requests` cookies into the browser |
| `interceptor` | Block selected resource types for the next page load |

### Example

```python
driver.goto(
    "https://www.python.org",
    wait_until="realtime",
    fallback_wait_until="url_only",
    fallback_after=5,
    locator="body",
    interceptor=["image", "audio", "video"],
    timeout=45,
    log=True,
)
```

---

## Common usage examples

### Wait for click-triggered navigation

```python
from dbgidchromium import By

button = driver.find_element_by_css_selector("button")
button.click()

driver.wait_for_navigation(
    locator=(By.CSS_SELECTOR, "body"),
    wait_until="javascript",
)
```

Alternative modes:

```python
driver.wait_for_navigation(wait_until="domcontentloaded")
driver.wait_for_navigation(wait_until="complete")
driver.wait_for_navigation(wait_until="url_only")
```

### Request interceptor

```python
driver.goto("https://www.python.org", interceptor="image")
driver.goto("https://www.python.org", interceptor=["image", "audio", "video"])
driver.goto("https://www.python.org", interceptor={"mime_types": ["image", "document"]})
driver.goto("https://www.python.org", interceptor=True)  # default: image,audio,video
```

Supported values include:

- `image`
- `audio`
- `video`
- `media` → expands to `audio` + `video`
- `document`
- `iframe` / `frame` / `frames` → normalized to `document`

### Cookies from `requests`

```python
import requests

session = requests.Session()

driver.goto(
    "https://example.com",
    use_cookie_from_requests=(True, session.cookies),
)
```

### JavaScript execution

```python
result = driver.execute_script("return document.title")
print(result)

button = driver.querySelector("button")
tag = driver.execute_script("return arguments[0].tagName", button)
print(tag)

driver.execute_script('arguments[0].setAttribute("data-test", "ok")', button)
```

### Returning elements from JavaScript

```python
first_button = driver.execute_script("return document.querySelector('button')")
print(first_button.text)

buttons = driver.execute_script("""
return Array.from(document.querySelectorAll('button')).slice(0, 2)
""")

for button in buttons:
    print(button.text)
```

### CSS query helpers

```python
first_button = driver.querySelector("button")
all_buttons = driver.querySelectorAll("button")

print(first_button.text)
print(len(all_buttons))
```

Helpers also work from a `WebElement`:

```python
form = driver.querySelector("form")
submit = form.querySelector("button[type='submit']")
buttons = form.querySelectorAll("button")
```

### Iframe support

```python
frames = driver.detect_iframes()
print(frames)

driver.switch_to.frame(0)
print(driver.querySelector("body").text)
driver.switch_to.default_content()
```

Frame switching supports:

- integer index
- id or name string
- CSS selector string
- XPath string
- iframe `WebElement`

### Header spoofing

```python
from dbgidchromium import generate_ip

spoof = generate_ip()
print(spoof)

driver.goto(
    "https://example.com",
    use_spoof=spoof,
    log=True,
)
```

Or generate automatically:

```python
driver.goto(
    "https://example.com",
    use_spoof=True,
    log=True,
)
```

### Browser cleanup

```python
from dbgidchromium import clear_browser

clear_browser(driver)
driver.goto("https://example.com")
```

Method style:

```python
driver.clear_browser()
driver.goto("https://example.com")
```

### Turnstile token helper

```python
token = driver.get_turnstile_token()
print(token)
```

Custom wait:

```python
token = driver.get_turnstile_token(
    selector="input[name='cf-turnstile-response']",
    timeout=20,
    poll_frequency=0.5,
)
```

---

## Example session

```python
from dbgidchromium import WebDriver

driver = WebDriver(gui=False, debug=True)

try:
    driver.goto(
        "https://claimyshare.io",
        wait_until="complete",
        fallback_wait_until="url_only",
        fallback_after=5,
        use_spoof=True,
        log=True,
    )

    driver.ensure_typing("input[name='q']", "demo", press_enter=True)

    print("URL       :", driver.current_url)
    print("Title     :", driver.title)
    print("Turnstile :", driver.get_turnstile_token())
finally:
    driver.close()
```

---

## Local data helper

`generate_ip()` uses the bundled `ip2asn-v4-u32.tsv` database.

| Field | Meaning |
|---|---|
| `ip` | generated public IPv4 |
| `country` | ASN country code |
| `name` | ISP / organization / network label |
| `asn` | ASN number |

---

## Changelog

Release notes now live in:

- [`CHANGELOG.md`](./CHANGELOG.md)

---

## Maintainer release workflow

For maintainers, a helper script is included:

```bash
./release.sh
```

That will:

- clean previous build artifacts
- build both **sdist** and **wheel**
- validate the artifacts with `twine check`

Upload to PyPI:

```bash
./release.sh --upload
```

Upload to TestPyPI:

```bash
./release.sh --upload --testpypi
```

You can also keep existing artifacts if needed:

```bash
./release.sh --skip-clean
```

Credentials can come from:

- `~/.pypirc`
- `TWINE_USERNAME`
- `TWINE_PASSWORD`
- `TWINE_REPOSITORY_URL`

---

## Notes

- This toolkit is built for **DBG-ID Browser**.
- For dynamic UI automation, prefer the `ensure_*` helpers over direct one-shot actions.
- For CSS selectors, the resilient helpers can use JavaScript fallback logic.
- For package examples and local behavior checks, you can run:

```bash
python example.py
```

---

## Project links

- Repository: https://github.com/dbgid/dbgidchromium
- Required browser: https://github.com/dbgid/DBG-ID-Browser
- PyPI: https://pypi.org/project/dbgidchromium/
